I have a task: QRect object should be painted when mouse cursor entering it.
After a couple of hours I made this.
void myObj::mouseMoveEvent(QMouseEvent* event){
int x1, y1, x2, y2;
QPoint point = event->pos();
rect->getCoords(&x1, &y1, &x2, &y2);
if((point.x() >= x1) && (point.x() <= x2) && (point.y() >= y1) && (point.y() <= y2)){
changeRectColour();
}else{
brush->setColor(Qt::green);
repaint();
}
}
myObj is inherited from QWidget.
But I think that my idea isn't efficient. Because on every mouse move outside the QRect it changes color to green(even if it's green).
Unfortunatelly, QRect hasn't enterEvent() function.
Can you, please, give an advice how to do this properly.
QWidget::repaint() means "paint now!!! I can't wait!". Use QWidget::update() instead, which will fold several paint requests into one (better explanation in the doc).
Btw you are basically reimplementing QRect::contains(). Your new code will be
void myObj::mouseMoveEvent(QMouseEvent* event){
QPoint point = event->pos();
if(rect->contains(point, true)){
changeRectColour();
}
else{
brush->setColor(Qt::green);
update();
}
}
You could create a boolean class member e.g. _lastPositionWasInsideRect, initialize that to false and program your if statement somewhat like this:
bool positionIsInsideRect = (point.x() >= x1) && (point.x() <= x2) && (point.y() >= y1) && (point.y() <= y2));
if( positionIsInsideRect && !_lastPositionWasInsideRect )
{
_lastPositionWasInsideRect = true;
// do the changes which are required when entering the rect
}
else if( !positionIsInsideRect && _lastPositionWasInsideRect )
{
_lastPositionWasInsideRect = false;
// do the changes which are required when leaving the rect
}
A much easier alternative would be to consider using the QGraphicsView framework.
Related
Trying to make a collision system in sfml for the first time in SFML without using a tutorial, using a array-based thing like so:
bool moright, moleft, moup, xcollision, ycollision;
float xvel, yvel;
int position, predictx, predicty, cm, arraynum;
class playerClass{
public:
playerClass(){
}
void update()
{
if (moright == true){
xvel = 2;}
if (moleft == true){
xvel = -2;}
if (!(moright || moleft)){
if (xvel < 0)
xvel = 0;
if (xvel > 0)
xvel = 0;}
}
};
int main()
{
playerClass playerObject;
// Create the main window
RenderWindow window(VideoMode(1080, 720), "SFML window");
// Load a sprite to display
Texture texture;
if (!texture.loadFromFile("gsquare100x100.png"))
return EXIT_FAILURE;
Sprite sprite(texture);
Sprite floor(texture);
Sprite wall(texture);
floor.setPosition(Vector2f(0.f, 498.f));
wall.setPosition(Vector2f(598.f,0.f));
floor.setColor(Color(0, 255, 0));
floor.setScale(12.f, 12.f);
wall.setScale(12.f, 12.f);
wall.setColor(Color(0, 0, 255));
int collisions[2][4]{
{0, 400, 500, 600},
};
// Start the game loop
while (window.isOpen())
{
Vector2f position = sprite.getPosition();
cout << position.y << endl;
predictx = position.x + xvel;
predicty = position.y + yvel;
yvel = 1;
for (arraynum = 0; arraynum < 2; arraynum++){
if ((predictx > collisions[arraynum][0])&&(predictx < collisions[arraynum][1])&&(predicty > collisions[arraynum][2])&&(predicty < collisions[arraynum][3])){
if ((position.y > collisions[arraynum][3])||(position.y < collisions[arraynum][2])){
xcollision = true;}
if ((position.x > collisions[arraynum][1])||(position.x < collisions[arraynum][0])){
ycollision = true;}
}
}
if (xcollision == true)
xvel = 0;
xcollision = false;
if (ycollision == true)
yvel = 0;
ycollision = false;
sprite.move(sf::Vector2f(0.f+xvel, 0.f+yvel));
// Process events
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == Event::KeyPressed)
{if (event.key.code == Keyboard::D)
moright = true;
if (event.key.code == Keyboard::A)
moleft = true;}
if (event.type == Event::KeyReleased)
{if (event.key.code == Keyboard::D)
moright = false;
if (event.key.code == Keyboard::A)
moleft = false;}
playerObject.update();
}
However the collision never registers, removing the bit that checks from which direction the sprite is moving in from doesn't help.
Very new to c++ so apologies if this is a stupid question and for my likely overly elaborate collision system.
I've written simple collisions with SFML before, and here's my advice to you: make your code as readable as possible! Things are going to get more complicated, and you need to have a system is reusable and easy to understand.
I've read your code but I don't understand why you've used an array. I assume you're trying to check if a smaller rectangle sprite is about to exit the collisions array?
For this purpose I suggest using a FloatRect object. It has useful functions like .contains() and .intersects() that you might need in the future. One downside it that is has top and left only, and to make it more and short, we'll define a simple struct to handle that part for us, as well as work for rectangular sprites as well.
I've left comments that explain the code, but haven't tested it personally. You can do that and integrate what you've learned into your project. Good luck
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
using namespace sf;
//using a struct is not necessarily faster. BUT it does give your code more readability and is reusable for future needs
//this struct just stores a floatRect of the given sprite/Floatrecct, defining some useful functions allowing for shorter code and more readability
struct rectangularShape
{
FloatRect containingRectangle;
//constructor with sprite input
rectangularShape(Sprite sprite)
{
this -> containingRectangle = FloatRect(Vector2f(sprite.getGlobalBounds().left, sprite.getGlobalBounds().top),
Vector2f(sprite.getGlobalBounds().left + sprite.getGlobalBounds().width,sprite.getGlobalBounds().top + sprite.getGlobalBounds().height));
}
//constructor with floatrect
rectangularShape(FloatRect rect)
{
this -> containingRectangle = rect;
}
//any useful functions for rectangular shapes-- you could add more if you want
float getTop() {return containingRectangle.top;}
float getbottom() {return containingRectangle.top + containingRectangle.height;}
float getleft() {return containingRectangle.left;}
float getright() {return containingRectangle.left + containingRectangle.width;}
};
//note the use of a FloatRect instead of the arrays you were using, this just makes it easier to understand
FloatRect inclusionArea(TopLeftVector, BottomRightVector);
Sprite sprite(texture);
//declare rectangularShapes, here we check if smallRectangle is exiting it's container
rectangularShape containingRectangle(inclusionArea);
rectangularShape smallRectangle(sprite);
//alternatively you can use the sprite's next position:
/*
spriteCopy = sprite;
spriteCopy.move(deltaTime * Vector2f(xSpeed, ySpeed));
rectangularShape smallRectangle(spriteCopy);
*/
//do the check:
if (smallRectangle.getTop() < containingRectangle.getTop() or smallRectangle.getBottom() > containingRectangle.getBottom())
//exiting in Y axis
//do something;
;
if (smallRectangle.getLeft() < containingRectangle.getLeft() or smallRectangle.getRight() > containingRectangle.getRight())
//exiting in X axis
//do something;
;
I can't comment due to low reputation.
From the code presented, it's seems like you never set xcollision or ycollision to true anywhere.
i have a problem with my overriden KeyPressEvent in my Arkanoid game.
I use it to control the paddle( left, right). If i loose a game, QPushButton is gettin visible and i can click it to reset the game, but after this i cant control my paddle. Whats wrong?
My keyPressEvent:
void MainWindow::keyPressEvent(QKeyEvent * event)
{
int x = ui->paletka->x();
int y = ui->paletka->y();
if( ui->paletka->x() > 2 )
if( event->key() == Qt::Key_Left)
ui->paletka->move(QPoint(x-8, y));
if( ui->paletka->x() < 898 )
if( event->key() == Qt::Key_Right)
ui->paletka->move(QPoint(x+8, y));
}
Have you tried clicking somewhere around a paddle to change the focus to the window?
Also... Your code is really unreadable, please use curly braces for if-s. ALWAYS. Also what is the point of these nested ifs. Isn't it better to use && instead?
void MainWindow::keyPressEvent(QKeyEvent * event)
{
int x = ui->paletka->x();
int y = ui->paletka->y();
if( ui->paletka->x() > 2 && event->key() == Qt::Key_Left)
{
ui->paletka->move(QPoint(x-8, y));
}
if( ui->paletka->x() < 898 && event->key() == Qt::Key_Right)
{
ui->paletka->move(QPoint(x+8, y));
}
}
Hey I want to draw something when a form pops up. I noticed the OnPaint even but it doesn't show my drawing. When I add it to a button it does show what I wanted to draw.
This is what I draw:
void TSelectElementForm::DrawLinesInLayout()
{
EraseLayOut(); //draws a new square over the previous lines
int X_origin, Y_origin;
int X1, Y1, X2, Y2, X3, Y3, X4, Y4;
int VectorSize;
int scale;
X_origin = Layout->Left +5;
Y_origin = Layout->Top +5;
Canvas->Pen->Color=clRed;
Canvas->MoveTo(X_origin,Y_origin);
Canvas->LineTo(X_origin,Y_origin+10);
Canvas->MoveTo(X_origin,Y_origin);
Canvas->LineTo(X_origin+10,Y_origin);
VectorSize = ElementVector[Index].vectorLP.size();
scale = CalculateScale();
for(int i =0; i < VectorSize; i++)
{
int LPX = ElementVector[Index].vectorLP[i].X;
int LPY = ElementVector[Index].vectorLP[i].Y;
int BeginX = ElementVector[Index].el_beginX;
Canvas->Pen->Color=clLime;
X1 = X_origin + ((LPX - BeginX) / scale);
Y1 = Y_origin + (LPY / scale);
if(i == 0)
{
Canvas->MoveTo(X1, Y1);
}
else
{
Canvas->LineTo(X1, Y1);
}
}
}
So when I call this method in a button it works, but when I call this in a OnShow event or OnPaint event it doesn't draw anything.
The strange thing is, when I simply just create a new project with a drawLine() method(that draws 1 line) and a OnPaint event It DOES work. How is that possible?
So, simply 2 questions:
Is this method to long to draw it before my form pops up?
How can I get this drawn on my form right away when it pops up?
I'm a beginner in Qt. I want to draw roads and a moving circle. The circle can only move on the road. And the road is made of a simple rectangle.
In painting the road I'm initializing a QPolygonF then appending the 4 corners of the desired rectangle.
And the circle is also drawn a boundingRect which returns QRectF (-10,-10,20,20).
I'm using collidesWithItem to determine where the circle can move.
The problem is that the circle is only moving along the center of the road, and it seems with my research that boundingRect is not the suitable function.
What is a simple method to draw a road and a circle that can move along the whole road?
Code for painting the road (Edge):
void Edge::paint( QPainter* painter,
const QStyleOptionGraphicsItem* option,
QWidget* widget )
{
painter->setBrush(Qt::gray);
int x1= startPoint.x();
int y1= startPoint.y();
int x2= endPoint.x();
int y2= endPoint.y();
QPolygonF Rectangle;
if (y1 != y2)
{
Rectangle.append(QPointF(x1-width/2,y1));
Rectangle.append(QPointF(x1+width/2,y1));
Rectangle.append(QPointF(x2+width/2,y2));
Rectangle.append(QPointF(x2-width/2,y2));
}
if (x1 != x2)
{
Rectangle.append(QPointF(x1,y1+width/2));
Rectangle.append(QPointF(x1,y1-width/2));
Rectangle.append(QPointF(x2,y2-width/2));
Rectangle.append(QPointF(x2,y2+width/2));
}
painter->drawPolygon(Rectangle);
}
And this is the code for the boundingRect of the road(edge):
QRectF Edge::boundingRect() const
{
int x1= startPoint.x();
int y1= startPoint.y();
int x2= endPoint.x();
int y2= endPoint.y();
if (y1 != y2)
{
return QRectF(x1 - width/2, y1, width, y2 - y1);
}
if (x1 != x2)
{
return QRectF(x1, y1 - width/2, x2 - x1, width);
}
}
I coded a simple program with two cubes on screen, one the user can move, the other is stationary. I just started using sfml and have never touched on collision, so this was completely new to me. In my code, I aim to have a warning window pop up when the user steers the cube into the stationary one. the problem, however, is that the warning window appears as soon as I start the program, even though It is in an if loop. Here is my code:
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <iostream>
using namespace std;
bool isCollision(int x, int y, int x2, int y2){ // borrowed function, all credits go to whom ever made it
if (abs(x2 - x) > 20 || abs(y2 - y) > 20)
return false;
else
return true;
}
int main()
{
sf::RenderWindow App(sf::VideoMode(800, 600, 32), "My SFML Window");
sf::RenderWindow Warning(sf::VideoMode(400, 225, 32), "WARNING!");
sf::Shape Rect = sf::Shape::Rectangle(0, 0, 20, 20, sf::Color::Red);
sf::Shape Rect2 = sf::Shape::Rectangle(50, 0, 70, 20, sf::Color::Blue);
while (App.IsOpened())
{
sf::Event event;
while (App.GetEvent(event)) // I now know the shorter way to handle events, just haven't edited it yet. No functional difference
{
if (event.Type == sf::Event::Closed)
App.Close();
if ((event.Type == sf::Event::KeyPressed) && (event.Key.Code == sf::Key::Escape))
App.Close();
if ((event.Type == sf::Event::KeyPressed) && (event.Key.Code == sf::Key::Right))
Rect.Move(5.0, 0);
if ((event.Type == sf::Event::KeyPressed) && (event.Key.Code == sf::Key::Left))
Rect.Move(-5.0, 0);
if ((event.Type == sf::Event::KeyPressed) && (event.Key.Code == sf::Key::Down))
Rect.Move(0, 5.0);
if ((event.Type == sf::Event::KeyPressed) && (event.Key.Code == sf::Key::Up))
Rect.Move(0, -5.0);
}
int x = Rect.GetPosition().x;
int y = Rect.GetPosition().y;
int x2 = Rect2.GetPosition().x;
int y2 = Rect2.GetPosition().y;
isCollision(x, y, x2, y2);
if (isCollision(x, y, x2, y2) == true) // if loop that I am messing up somehow
}
Warning.Clear(sf::Color::White);
}
App.Clear();
App.Draw(Rect);
App.Draw(Rect2);
App.Display();
}
return EXIT_SUCCESS;
}
I got the bool isCollision function from a tut I was watching, but the tut was done in allegro, so I scraped from it what I could. (My logic in using his function was that our cubes are the exact same size, and identical in their properties [one moving one stationary]. The problem must lie, I assume, in how I call the function. Any and all help is greatly appreciated
In C++, it is up to the caller to collect the returned value.
isCollision(x, y, x2, y2);
Now the call to the above function, does nothing useful. You need to collect the returned value and serve it as a flag for an if condition. Or directly place the function call in the if condition itself.
if ( isCollision(x, y, x2, y2) ){
// Code here
}
i think your function isnt returning properly, you should try it like this. (FYI 2 returns is real bad practice)
bool isCollision(int x, int y, int x2, int y2){
bool exitVal; // maybe make it static if this is being called over and over
if (abs(x2 - x) > 20 || abs(y2 - y) > 20)
exitVal = false;
else
exitVal = true;
return exitVal;
}
Hope this helps :D
in your code sample:
isCollision(x, y, x2, y2);
if (isCollision(x, y, x2, y2) == true) // if loop that I am messing up somehow
}
Warning.Clear(sf::Color::White);
}
Why are you calling the function isCollision twice?
You can just do like this:
bool is_coll = isCollision(x, y, x2, y2);
if(is_coll) {
//....
}
Also the brace } after if statement look like not matched ....
moreover in the implementation of function isCollision, what is abs(x2 - x)
I feel like abs() is probably a macro, so check if the macro is defined properly or not?
Two macro calls in the if() statement can really mess things up.
I suspect that the RenderWindow constructor creates and shows the window.
You shouldn't create it (or at least delay displaying it) until there has been a collision.
I will leave it as an exercise for you to figure out how to accomplish that.
Try to use a more simple way to figure out where exactly the mistake lies. molbdnilo might be right, so instead of using the new window to check whether your collision code is correct you could do something that will definately work. I usually just output messages with cout:
#include <iostream>
if (isCollision(x, y, x2, y2) == true)
{
cout << "squares r colliding!!!";
Warning.Clear(sf::Color::White);
}
Just make sure you can see the console (sometimes you need to use debug mode for that)
Also there are no if loops ;)