KeyPressEvent stops working when QPushButton is visible - c++

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));
}
}

Related

Water in a falling sand simulation

I am currently working on a very simple 'Falling Sand' simulation game in C++ and SDL2, and am having problems with getting water to flow in a more realistic manner. I basically have a grid of cells that I iterate through bottom-to-top, left-to-right and if I find a water cell, I just check below, down to left, down to the right, left then right for empty cells and it moves into the first one its finds (it makes a random choice if both diagonal cells or both horizontal cells are free). I then mark the cell it moved into as processed so that it is not checked again for the rest of that loop.
My problem is a sort of 'left-bias' in how the particles move; if I spawn a square of water cells above a barrier, they will basically all shift to left without moving once the particles begin to reach the barrier, while the cells on the right will run down in the proper way. So instead of forming a nice triangular shape flowing out evenly to both sides, the whole shape will just move to the left. This effect is reversed whenever I iterate left-to-right, so I know it's something to do with that but so far I've been stumped trying to fix it. I initially thought it was a problem with how I marked the cells as processed but I've found no obvious bugs with that system in many hours of testing. Has anyone faced any similar challeneges in developing a simulation like this, or knows something that I'm missing? Any help would be very much appreciated.
EDIT:
Ok so I've made a little progress, however I've ran into another bug that seems to be unrelated to iteration, since now I save a copy of the old cells and read from that to decide an update, then update the original cells and display that. This already made the sand work better, however water, which checks horizontally for free cells, now 'disappears' when it does move horizontally. I've been testing it all morning and have yet to find a solution, I thought it might've been someting to do with how I was copying the arrays over, but it seems to work as far as I can tell.
New snippets:
Simulation.cpp
void Simulation::update()
{
copyStates(m_cells, m_oldCells); // so now oldcells is the last new state
for(int y = m_height - 1; y>= 0; y--)
for(int x = 0; x < m_width; x++)
{
Cell* c = getOldCell(x, y); // check in the old state for possible updates
switch(c->m_type)
{
case EMPTY:
break;
case SAND:
if(c->m_visited == false) update_sand(x, y);
break;
case WATER:
if(c->m_visited == false) update_water(x, y);
break;
default:
break;
}
}
}
void Simulation::update_water(int x, int y)
{
bool down = (getOldCell(x, y+1)->m_type == EMPTY) && checkBounds(x, y+1) && !getOldCell(x, y+1)->m_visited;
bool d_left = (getOldCell(x-1, y+1)->m_type == EMPTY) && checkBounds(x-1, y+1) && !getOldCell(x-1, y+1)->m_visited;
bool d_right = (getOldCell(x+1, y+1)->m_type == EMPTY) && checkBounds(x+1, y+1) && !getOldCell(x+1, y+1)->m_visited ;
bool left = (getOldCell(x-1, y)->m_type == EMPTY) && checkBounds(x-1, y) && !getOldCell(x-1, y)->m_visited ;
bool right = (getOldCell(x+1, y)->m_type == EMPTY) && checkBounds(x+1, y) && !getOldCell(x+1, y)->m_visited ;
// choose random dir if both are possible
if(d_left && d_right)
{
int r = rand() % 2;
if(r) d_right = false;
else d_left = false;
}
if(left && right)
{
int r = rand() % 2;
if(r) right = false;
else left = false;
}
if(down)
{
getCell(x, y+1)->m_type = WATER; // we now update the new state
getOldCell(x, y+1)->m_visited = true; // mark as visited so it will not be checked again in update()
} else if(d_left)
{
getCell(x-1, y+1)->m_type = WATER;
getOldCell(x-1, y+1)->m_visited = true;
} else if(d_right)
{
getCell(x+1, y+1)->m_type = WATER;
getOldCell(x+1, y+1)->m_visited = true;
} else if(left)
{
getCell(x-1, y)->m_type = WATER;
getOldCell(x-1, y)->m_visited = true;
} else if(right)
{
getCell(x+1, y)->m_type = WATER;
getOldCell(x+1, y)->m_visited = true;
}
if(down || d_right || d_left || left || right) // the original cell is now empty; update the new state
{
getCell(x, y)->m_type = EMPTY;
}
}
void Simulation::copyStates(Cell* from, Cell* to)
{
for(int x = 0; x < m_width; x++)
for(int y = 0; y < m_height; y++)
{
to[x + y * m_width].m_type = from[x + y * m_width].m_type;
to[x + y * m_width].m_visited = from[x + y * m_width].m_visited;
}
}
Main.cpp
sim.update();
Uint32 c_sand = 0xedec9a00;
for(int y = 0; y < sim.m_height; y++)
for(int x = 0; x < sim.m_width; x++)
{
sim.getCell(x, y)->m_visited = false;
if(sim.getCell(x, y)->m_type == 0) screen.setPixel(x, y, 0);
if(sim.getCell(x, y)->m_type == 1) screen.setPixel(x, y, c_sand);
if(sim.getCell(x, y)->m_type == 2) screen.setPixel(x, y, 0x0000cc00);
}
screen.render();
I've attached a gif showing the problem, hopefully this might help make it a little clearer. You can see the sand being placed normally, then the water and the strange patterns it makes after being placed (notice how it moves off to the left when it's spawned, unlike the sand)
You also have to mark the destination postion as visited to stop multiple cells moving in to the same place.

Cocos: How to detect touch input inside of a area/layout

I have several cocos layouts they are various panels or menus. I was wondering how to close then with a touch input outside of their area like most apps.
Basically how to close a popup menu by tapping any empty area on screen.
Basically you calculate the spaces between the edges of the screen and your popup and then call the destructor.
This is how I do it:
In init
auto touchListener = cocos2d::EventListenerTouchOneByOne::create();
touchListener->setSwallowTouches(true);
touchListener->onTouchBegan = CC_CALLBACK_2(CLASS::onTouch, this);
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(touchListener, this);
On Touch
bool CLASS::onTouch(cocos2d::Touch* touch, cocos2d::Event* event)
{
int ySize = visibleSize.height - holder->getContentSize().height;
int locationY = touch->getLocation().y;
if((locationY > 0 && locationY < ySize/2) || (locationY > origin.y + ySize/2 + holder->getContentSize().height && locationY < visibleSize.height))
{
this->removeFromParentAndCleanup(true);
}
return true;
}

Fill the QRect on enter event

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.

How should i scale this for multiple buttons in SDL?

I have a simple script which displays one button (which is a png image) and when the user clicks it the application quits.
But i want to add multiple buttons which is where im finding my current thinking will lead to a very long if:else situation and I am wondering if that is the only way.
This is how i have my current menu set up in a main.cpp file.
bool handle_mouse_leftClick(int x, int y, SDL_Surface *button) {
if( ( ( mouseX > x ) && ( mouseX < x + button->w ) ) && ( ( mouseY > y ) && ( mouseY < y + button->h ) ) ) {
return true;
} else {
return false;
}
}
This is my detection function.
Below is my main function which acts as my game loop, i've removed non relevant code to keep it easier to follow:
//menu button
SDL_Surface *button;
button = IMG_Load("button.png");
while(!quit){
//handle events
while( SDL_PollEvent( &event ) ){
switch(event.type){
case SDL_QUIT: quit = true; break;
case SDL_MOUSEBUTTONDOWN:
if (event.button.button == SDL_BUTTON_LEFT) {
if(handle_mouse_leftClick(btnx,btny,button)){
quit = true;
}
}
break;
}
}
The issue is should my main.cpp have all this checking going on, is going to get very long very quickly when i add more buttons so I'm wondering if I have missed a trick to simplify my efforts?
When you get right down to the basic logic/computation, I would say the answer is "no", you haven't missed any tricks. I've never found a way around checking each target one at a time - at least in terms of computation. You could make your code cleaner a lot of ways. You could have a GuiElement class that exposes a "bool IsIn( int x, int y )" method. Then your big case statement would look more like:
bool handle_mouse_leftClick(int x, int y, SDL_Surface *button)
{
if (Button1.IsIn( mouseX, mouseY )
{
// do this
}
else if (Button2.IsIn( mouseX, mouseY ))
{
// do that
}
else
{
return false;
}
}
You could then further reduce amount code with a list or table:
int handle_mouse_leftClick(int x, int y, SDL_Surface *button)
{
for (unsigned int i = 0; i < buttonList.size(); i++
{
if (buttonList[i].IsIn( mouseX, mouseY ))
{
return i; // return index of button pressed
}
return -1; // nothing pressed
}
}
But it's still ultimately looking at each rectangle one at a time.
Couple caveats:
I don't think there's much real computational overhead in checking the hit boxes of each item - unless you're doing it at some crazy high frame rate.
Sure, you could optimize the checking with some kind of spatial index (like a b-tree or quad-tree organized by the locations of the buttons on the screen), but ... see #1. ;-)
If instead of 10 or 15 buttons/controls you have THOUSANDS then you will likely want to do #2 because #1 will no longer be true.
********* Update ***********
Here's a brief sample of a class that could be used for this. As far as .h vs main.cpp, the typical approach is to put the header in a "Button.h" and the implementation (code) in a "Button.cpp", but you could just put this at the top of main.cpp to get started - it has all the logic right in the class definition.
You'll notice I didn't really write any new code. The "IsIn()" test is your logic verbatim, I just changed the variable names to match the class. And since you already have a single button, I'm assuming you can reuse the code that renders that button the Render() method.
And lastly, if is not something you're familiar with, you don't have to create a list/vector at all. The code the renders the buttons could just call "okButton.Render()", followed by "cancelButton.Render()".
Sample "button" class:
class Button
{
private:
int m_x, m_y; // coordinates of upper left corner of control
int m_width, m_height; // size of control
public:
Button(int x, int y, int width, int height, const char* caption)
{
m_x = x;
m_y = y;
m_width = width;
m_height = height;
// also store caption in variable of same type you're using now for button text
}
bool IsIn( int mouseX, int mouseY )
{
if (((mouseX > m_x) && (mouseX < m_x + m_width))
&& ((mouseY > m_y) && (mouseY < m_y + m_height ) ) ) {
return true;
} else {
return false;
}
}
void Render()
{
// use the same code you use now to render the button in OpenGL/SDL
}
};
Then to create it/them (using the list approach):
Button okButton( 10, 10, 100, 50, "OK" );
buttonList.push_back( okButton );
Button cancelButton( 150, 10, 100, 50, "Cancel" );
buttonList.push_back( cancelButton );
And in your render loop:
void Update()
{
for (unsigned int i = 0; i < buttonList.size(); i++
{
buttonList[i].Render();
}
}

OpenGL VS2008 on C++: Same code bevahes differently even on the same computer

I have two exact same codes OpenGL C++ , compiled using VS2008 in different project , but when i compile them, it behaves differently. One of them can recognize the condition if ( mod == GLUT_ACTIVE_CTRL && button == GLUT_WHEEL_UP ) and one of them is not.
Here is the complete function:
void mouse(int button, int state, int x, int y) {
int mod = glutGetModifiers();
mouseState = state;
mouseButton = button;
double modelview[16], projection[16];
int viewport[4];
float z = 0 ;
/*Read the projection, modelview and viewport matrices
using the glGet functions.*/
glGetIntegerv( GL_VIEWPORT, viewport );
glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
//glGetDoublev( GL_PROJECTION_MATRIX, projection );
//Read the window z value from the z-buffer
glReadPixels( x, viewport[3]-y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z );
// Used for wheels, has to be up
if (state == GLUT_UP ) {
if ( mod == GLUT_ACTIVE_CTRL && button == GLUT_WHEEL_UP ){
printf("Wheel Up\n");
zoom += 0.1;
}
else if (mod == GLUT_ACTIVE_CTRL && button == GLUT_WHEEL_DOWN ){
printf("Wheel Down\n");
zoom -= 0.1;
}
else if (mod == GLUT_ACTIVE_ALT && button == GLUT_WHEEL_UP) {
//printf("Z++\n");
translation_z = translation_z + 0.1;
//printf("Z = %f", translation_z);
}
else if (mod == GLUT_ACTIVE_ALT && button == GLUT_WHEEL_DOWN) {
//printf("Z--\n");
translation_z = translation_z - 0.1;
}
else if (mod == GLUT_ACTIVE_SHIFT && button == GLUT_WHEEL_UP) {
//printf("Shift Wheel Up. Z axis rotation goes here.\n");
zrotation += (5*(z - oldZ)); // about x-axis
}
else if (mod == GLUT_ACTIVE_SHIFT && button == GLUT_WHEEL_DOWN) {
//printf("Shift Wheel Down. Z Axis rotation goes here\n");
zrotation -= (5*(z - oldZ)); // about x-axis
//translation_z = translation_z - 0.1;
}
}
else if (state == GLUT_DOWN) {
//printf("Glut Down before z processing\n");
if (button == GLUT_LEFT_BUTTON){
cursorX = x;
cursorY = y;
mode = SELECT;
//printf("Left is down\n");
}
oldX = x;
oldY = y;
}
}
`
and I uploaded both of the project here.
Both the source and the project settings appear to be exactly the same.
I can't see any added include paths though, do you have a copy of opengl locally in both projects?
Only other thing I can think of right now is this comment from your source...
//Use patched version of GLUT (http://www.realmtech.net/opengl/glut.php) in case the wheel interaction does not work.
1) This sounds like it might relate to the exact problem you are having.
2) Did you maybe use that patched version in one project, but not the other?
Edit:
In one of the projects you have the printf lines for wheel down and wheel up commented out, is this by any chance how you tell that one doesn't recognize the wheel?
I found the problem, In the project that can not recognize wheel up/down condition (despite that it should), I included:
freeglut.lib glut32.lib opengl32.lib user32.lib
This can be solved by including only the following libs:
opengl32.lib user32.lib
Thank you #melak47 for suggesting to check the project settings.