PDA

View Full Version : mouse pos() returns wrong position



FlyDoodle
30th September 2019, 14:06
Hello !,
So i have a problem getting pos from the point where i click on the mouse ( using right click )
check this video :
https://streamable.com/cjo0d
- Here you can see at the beginning i try moving him around using right click on the mouse and it's all working fine. But when i reach the land or at least when im near it the returned position is completely different as i expected it to be...
you can see when i print the character current position and the positioning of the mouse ( when i press right click ) in the debug console it gives me wrong coordinates ( from the mouse ) and i don't know why... Sea and Land are two different QGraphicsPixmapItems both have their own class, so maybe it has to do something with that ?
Here is the main class code:

Game.h:


#ifndef GAME_H
#define GAME_H
#include "player.h"
#include "button.h"
#include "sea.h"
#include "land.h"

#include <QGraphicsView>
#include <QGraphicsScene>
#include <QObject>
#include <QMouseEvent>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsScene>
#include <QWidget>
#include <QKeyEvent>

class Game: public QGraphicsView{
Q_OBJECT
public:
// this class stuff
static QString in_room;
Game(QWidget *parent=NULL);
QGraphicsScene *scene;
void menu();
void createMap();
void createPlayer();
QPixmap darkenImage(QString image, float num);

// other class stuff
Player *player;
Button *startButton;
Button *quitButton;
Land *land;
Sea *sea;

// static stuff
int curr_pos_in_menu;
void changeColorOfButton();

public slots:
void start();
void mousePressEvent(QMouseEvent *event);
};

#endif // GAME_H

Game.cpp:


#include "game.h"
#include "player.h"
#include "button.h"

#include <QGraphicsRectItem>
#include <QGraphicsScene>
#include <QDebug>
#include <QApplication>
#include <QTimer>
#include <QFont>
#include <QMessageBox>
#include <fstream>
#include <cmath>
#include <QRect>
#include <QDesktopWidget>
#include <QtMath>

QString Game::in_room = "menu";

Game::Game(QWidget *parent)
{
scene = new QGraphicsScene();
scene->setSceneRect(0,0,350,300);
setFixedSize(350,300);
setScene(scene);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOf f);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff) ;
setBackgroundBrush(QBrush(QImage("red")));
show();
menu();
}

void Game::menu()
{
QGraphicsTextItem *title = new QGraphicsTextItem("NIGHTLESS");
title->setPos(10,30);
title->setFont(QFont("comic sans",40,70));
scene->addItem(title);

qDebug() << width();
startButton = new Button("START", 200, 50);
startButton->setPos(width()/2-startButton->rect().width()/2,125);
connect(startButton,SIGNAL(clicked()),this,SLOT(st art()));
scene->addItem(startButton);

quitButton = new Button("QUIT", 200, 50);
quitButton->setPos(width()/2-quitButton->rect().width()/2,200);
connect(quitButton,SIGNAL(clicked()),this,SLOT(clo se()));
scene->addItem(quitButton);

}

void Game::createMap()
{
land = new Land;
land->setPos(0,0);
scene->addItem(land);

sea = new Sea;
sea->setPos(0,0);
scene->addItem(sea);
}

void Game::createPlayer()
{
player = new Player;
player->setFlag(QGraphicsItem::ItemIsFocusable);
player->setFocus();
centerOn(player);
scene->addItem(player);
}

QPixmap Game::darkenImage(QString file_name, float num)
{
QImage tmp( file_name );

for(int i=0;i<tmp.height();i++)
{
for(int k=0;k<tmp.width();k++)
{
QColor color( tmp.pixel( k, i ) ); // changed this from pixelColor() to ensure alpha is copied
if ( tmp.pixel(k,i) != 0) // modify only the pixels with non-zero alpha
{
color.setRgb(color.red()*num,color.blue()*num,colo r.green()*num);
tmp.setPixelColor(k,i,color);
}
}
}

// Now, convert the image to a pixmap and set it on the graphics object
QPixmap t = QPixmap::fromImage( tmp );
return t;
}

void Game::start()
{
scene->clear();
in_room = "world";
// semi-transparent background
/*
QRect rec = QApplication::desktop()->screenGeometry();
int height = rec.height();
int width = rec.width();
*/

setSceneRect(0,0,7097,7298);
setFixedSize(1000,800);
//setBackgroundBrush(QBrush(QImage(":/Images/Grass.png")));
//showFullScreen();

createMap();
createPlayer();
}

void Game::mousePressEvent(QMouseEvent *event)
{
if(event->buttons() == Qt::RightButton && in_room!="menu"){

QLineF ln(event->pos(),player->pos());
int angle = -1 * ln.angle() + 460;

int tmp = angle + 80;
if(tmp>360)
tmp-=360;

if(angle > 360)
angle += -360;

player->setPlayerRotation(angle,tmp);

QPointF t(event->pos());
player->setDestination(event->pos());

}
}

In the Land and Sea class i just setPixmap to the image, put them into the scene,set position and that is it.

And i have one more question, how do i make a player stop when he reaches his destination ? i was thinking about making an QGraphicsRectItem whenever i press a right button and then checking if player has collided with that item and when he does he stops and then that item gets deleted or is there a better and easier way to do that, cause comparing mouse pos() and player pos() is not working.

Any kind of help or advice is appreciated !!!

d_stranz
30th September 2019, 19:48
Where is this "mousePressedEvent()" slot being called from? Or have you mistakenly defined an event handler as a slot when it should be a protected member function?

The player's position will always be in coordinates relative to its QGraphicsItem parent's upper left corner. If it has no parent (i.e. it has been added as a top-level item in a scene), then its position will be relative to the top left corner of the scene, in scene coordinates. If you are handling a mouse press event as an override to a QWidget event (in other words, the QGraphicsView), then the coordinates here will be in widget (pixel) coordinates relative to the top left corner of the widget.

I don't know what your Land and Sea class (or classes) is, but if they are also QGraphicsItem instances in the scene, then mouse press events on them will be relative to their top-left corners.

So you are trying to compare coordinates that are all relative to something different. Your video makes this obvious - as soon as you click on the land, the player turns towards the top left of the view, because you have clicked on the top left corner of the land, and the relative coordinate returned from that click is close to (0,0). You need to convert all coordinates so they refer to a common origin, maybe using QGraphicsItem::mapToScene() and QGraphicsView::mapToScene().

FlyDoodle
30th September 2019, 20:31
Hello
thank you for answering, and regarding the mousePressEvent(), its being called in the game class:
This is the code :



void Game::mousePressEvent(QMouseEvent *event)
{
if(event->buttons() == Qt::RightButton && in_room!="menu"){

QLineF ln(event->pos(),player->pos());
int angle = -1 * ln.angle() + 460;

int tmp = angle + 80;
if(tmp>360)
tmp-=360;

if(angle > 360)
angle += -360;

player->setPlayerRotation(angle,tmp);

QPointF t(event->pos());
player->setDestination(t);

}
}

I tried putting :

QPointF t(mapToScene(event->pos()));

But it still doesn't work...

And yeah they are both QGraphicsItem instances in the scene, here is the code if helps :
Land and Water files ( both are the same just the namings are different )

#ifndef LAND_H
#define LAND_H

#include <QObject>
#include <QGraphicsPixmapItem>

class Land: public QObject, public QGraphicsPixmapItem{
Q_OBJECT
public:
Land(QGraphicsPixmapItem *parent=NULL);

private:
QString file_name;
};

#endif // LAND_H
.cpp

#include "land.h"

Land::Land(QGraphicsPixmapItem *parent)
{
file_name = ":/Images/Land.png";
setPixmap(QPixmap(file_name));
}


I don't know how to make my mouse event ignore those two classes when pressing right click on them ( so that its like i'm trying to access scene only and get coordinate from it )... I tried looking for the solution on google ( ignore() functions and so on ) and i still have no idea...

d_stranz
30th September 2019, 20:52
mousePressEvent(), its being called in the game class

No, it isn't being called there, it is defined there. It is probably being called from Qt's event loop, handling events on the QGraphicsView that are not being handled by other objects.

You should read the Qt documentation on the Qt Graphics View Framework (https://doc.qt.io/qt-5/graphicsview.html) and pay particular attention to the section on "The Graphics View Coordinate System".

You should also be sure that you are handling your mouse events correctly. There is a complete set of mouse and other events that are handled internally in the QGraphicsScene class. These take care of mapping events in the scene to the graphical objects that have the current focus or are under the mouse. If you are handling mouse events at both the widget level (QGraphicsView) and scene level, then the coordinate systems are not the same.

My advice is to handle mouse events only at the scene level (by implementing handlers for your QGraphicsItem-based classes), and map everything to scene coordinates.

FlyDoodle
1st October 2019, 16:07
No, it isn't being called there, it is defined there.
oh i see, ye i have to take a look at view, scene and events handling stuff a little bit more, tho after trying some stuff out i realized that when i was creating a line between the player and the mouse i forgot to use mapToScene when initializing points. So far it seems to be working. Thank you very much for that !!

Tho i have one more question, how do i put waves (image) around my island in the game as the background. I want to make it so that for example 60 x 60 pixels waves fill the background. Doing setBackgroundBrush(QBrush(QImage("path"))); will set the background the way i want it to, but the problem is when the player is moving and he gets near the water, he has to stop. I wanted to check if he is near the water using collision. Like that class Land i made class Sea and this class is also QPixmapItem and i could just check if the player has collided with Sea but the problem comes when i want the sea to be moving, cause changing pictures takes time and that's not optimal...

d_stranz
1st October 2019, 18:52
cause changing pictures takes time and that's not optimal...

Changing pictures only takes time if you are loading them from disk every time you change. If you keep the pictures in memory in the correct form (QPixmap), then changing the image is as fast as calling setPixmap(). QPixmap instances use data sharing, which means that calling setPixmap() does not involve a data copy of the pixmap, simply swapping an internal reference. There is even a QPixmapCache cache that you can store your pixmaps in.

For detecting if your player has walked into the water, you might be able to use QPolygon to define the area of the land, and QPolygon::containsPoint() to determine if your player is still on land.

FlyDoodle
5th October 2019, 20:38
I didn't put QPixmap on the heap, that is why it took couple of seconds to load an image every time. Thank you very much for the help !!!