May 20, 2011

Jizin Jizin
Lab Rat
12 posts

Bomberman - QTimer, QTimerEvent, Signals and Slots connection problems

Page  
1

Hi there. I’m new to Gui programming and I’m trying write some easy game called Bomberman. I’ve already got the field, solid, understroyable blocks and destroyable obstacles. There’s one player moving by cursors and planting bombs by space key. What’s making me trouble are enemies. I’ve wrote the enemy.h and enemy.cpp files as well as using them in my main file:

  1. #ifndef ENEMY_H
  2. #define ENEMY_H
  3.  
  4. #include <QWidget>
  5.  
  6. class QTimer;
  7.  
  8. class Enemy : public QWidget{
  9.      Q_OBJECT
  10.  
  11. public:
  12.      Enemy(QWidget *parent=0, int x=0, int y=0);
  13.      void paintEnemy(QPainter &painter);
  14.      QRect enemyRect();
  15.      QPoint enemyCenter;
  16.      int curX, curY;
  17.      void saveEnemyCoordinates(int x, int y, int element);
  18.  
  19. public slots:
  20.      void randomEnemyDirection();
  21.  
  22. signals:
  23.      void moveEnemy(int element);
  24.  
  25. protected:
  26.  
  27. private slots:
  28.  
  29. private:
  30.  
  31. };
  32.  
  33. #endif // ENEMY_H

  1. #include <QPainter>
  2. #include <QDateTime>
  3.  
  4.  
  5. #include "enemy.h"
  6.  
  7.  
  8. Enemy::Enemy(QWidget *parent, int x, int y):QWidget(parent) {
  9.      enemyCenter = QPoint(x, y);
  10.      curX = x;
  11.      curY = y;  
  12.  }
  13.  
  14.  void Enemy::paintEnemy(QPainter &painter){
  15.      painter.setPen(Qt::black);
  16.      painter.setBrush(Qt::white);
  17.      painter.drawEllipse(enemyRect());
  18.  }
  19.  
  20.  QRect Enemy::enemyRect(){
  21.      QRect result(0, 0, 39, 39);
  22.      static bool firstTime = true;
  23.      if (firstTime) {
  24.         firstTime = false;
  25.         QTime midnight(0, 0, 0);
  26.         qsrand(midnight.secsTo(QTime::currentTime()));
  27.      }
  28.      result.moveCenter(QPoint(rand() % 400, rand() % 400));
  29.      return result;
  30.  }
  31.  
  32.  void Enemy::saveEnemyCoordinates(int x, int y, int element){
  33.      static int curElement = element;
  34.      curX = x;
  35.      curY = y;
  36.      curElement++;
  37.  }
  38.  
  39.  void Enemy::randomEnemyDirection(){
  40.      static int element = 0;
  41.      emit moveEnemy(element);
  42.      element++;
  43.  }

Some of the functions are useless now (saveEnemyCoordinates(int x, int y, int element), randomEnemyDirection() and signal moveEnemy(int element), because I used in enemyRect() the rand() function to test positioning enemies.
But let’s start from the beginning.
In my game I want to create, let’s say 5 enemies using the class enemy.cpp. Ok, there are not problems with it. Then I want them to move independently each second passed in random direction. I’m using QTimer for this, but it’s not working. And here are the problems:
1. How can I connect each timeout() signal of each enemy, to move them? Right now they’re moving only after mine action (i click cursor to move my player, they move, i click, they move, etc…)
2. How can I transfer the position from the class, where enemies are made to the class where is enemyRect(), the center of the enemy?
3. What’s the difference between the QTimer and QTimerEvent?

So summarizing, I don’t know how:
Make this line in enemy.cpp:

  1. result.moveCenter(QPoint(rand() % 400, rand() % 400));

something like this:
  1. result.moveCenter(QPoint(curX, curY));

where the curX, and curY are transfered from another class (where the enemies are constructed), and to change these variables every 1 sec.

At the end, sorry for my bad english and maybe hard explanation of my problems. I hope someone will help me, cuz I’m trying to solve this by myself for 5 days and without results. If something is unclear feel free to ask. I’ll explain it as good as I can. Also I don’t want to post here entire code, but if someone really need it to help I can send more fragments via private message or any other way.

21 replies

May 20, 2011

koahnig koahnig
Area 51 Engineer
2879 posts

Hi Jizin,

one comment first. You might want to change the subject of your thread to something describing your problem. This might help to trigger people to read it.

For timers you find some details here [doc.qt.nokia.com]

There are probably different possibilities to achieve what you want.
One first idea would be to use QTimer in the enemy objects. The QTimers will trigger timer events.
You probably will need some understanding of the signal-slot possibilities [doc.qt.nokia.com]

The QTimer is the clock (timer) triggering the events. The QTimerEvent is describing the type of event, respectively the timer’s id. It gives you a possibility to back-trace the event.

May 20, 2011

Jizin Jizin
Lab Rat
12 posts

Thanks for your reply. However, after reading your links, I’ve got some other questions:
1. So what’s the reason to use QTimerEvent for events, if I can connect the QTimer’s signal timeout() to a correct slot?
2. From what I’ve read it seems that the signal’s parameters must be the same as the slot’s. So, how can I connect timeout() with, for example: moveEnemy(int newX, int newY)?
I’m using something like this:

  1. for(int i = 0; i < numberEnemy; i++){
  2.         connect(autoEnemyTimer[i], SIGNAL(timeout()), enemy[i], SLOT(randomEnemyDirection()));
  3.         connect(enemy[i], SIGNAL(moveEnemy(int)), this, SLOT(randomEnemyDirection(int)));
  4.  }

  1. void BomberField::randomEnemyDirection(int element){
  2.      int enemyDirection = 0;
  3.      static bool firstTime = true;
  4.      if (firstTime) {
  5.         firstTime = false;
  6.         QTime midnight(0, 0, 0);
  7.         qsrand(midnight.secsTo(QTime::currentTime()));
  8.      }
  9.      enemyDirection = rand() % 4;
  10.      if(enemyDirection == 0) tryEnemyMoveLeft(element);
  11.      if(enemyDirection == 1) tryEnemyMoveRight(element);
  12.      if(enemyDirection == 2) tryEnemyMoveDown(element);
  13.      if(enemyDirection == 3) tryEnemyMoveUp(element);
  14.  }

But it totally doesn’t work… The slots aren’t called every second. They aren’t called at all. So how should I connect the autoEnemyTimer[i] (which is QTimer type) signal timeout() to… And here’s another question… Should I change the enemies’ X and Y values inside the enemies’ classes or in the main class where they are made? Should I change the curX, curY in the enemy.cpp from my main file, or should I make an array of enemyX and enemyY in the main file and operate on them? I suppose the first option, because there’s the enemyRect() which is the area with center of the enemy, which I want to paint. However I don’t know how to connect this…
Any help much appreciated.

May 20, 2011

koahnig koahnig
Area 51 Engineer
2879 posts

Well, you might receive timer event from several timers at the slot routine. Then you have the possibility to find out from it comes. However, I do not use it myself. In the application I am using timer respectively QTimer just one event slot. So, there no need for back tracing.

Does one of the signal connections work?
You should be able to track this in the debugger.

May 20, 2011

koahnig koahnig
Area 51 Engineer
2879 posts

Do you get any feedback from the connect statement?
If there is already a problem, you will not receive the signals. You can also test the return value of connect to make sure that the connection has been established.

May 20, 2011

Jizin Jizin
Lab Rat
12 posts

My program debugs fine, no errors. Ummm… what do you mean by “back tracing”? I’m also using 2 other connections (for bomb and explosions using QTimer timeout() signal, because they are existing only in short period of time) and they work fine. How can I test the return value of the connect?

  1. connect(autoBombTimer, SIGNAL(timeout()), this, SLOT(checkBombExistence()));
  2. for(int i = 0; i < 5; i++){
  3.      connect(autoExplosionTimer[i], SIGNAL(timeout()), this, SLOT(checkExplosionExistence()));        
  4. }

May 21, 2011

Jizin Jizin
Lab Rat
12 posts

I’ve got another very strange error. When I changed the:

  1. for(int i = 0; i < numberEnemy; i++){
  2.              connect(autoEnemyTimer[i], SIGNAL(timeout()), enemy[i], SLOT(randomEnemyDirection()));
  3.         connect(enemy[i], SIGNAL(moveEnemy(int)), this, SLOT(randomEnemyDirection(int)));
  4.  }

to:
  1. connect(autoEnemyTimer[0], SIGNAL(timeout()), enemy[0], SLOT(randomEnemyDirection()));
  2. connect(enemy[0], SIGNAL(moveEnemy(int)), this, SLOT(randomEnemyDirection(int)));

To test the moving only on 1 enemy the game runs normal, but after few seconds it closes with this message:
HEAP[Bomberman.exe]:
Heap block at 0B205300 modified at 0B205324 past requested size of 18
(Internal error: pc 0×3 in read in psymtab, but not in symtab.)
(Internal error: pc 0×3 in read in psymtab, but not in symtab.)
(Internal error: pc 0×3 in read in psymtab, but not in symtab.)
(Internal error: pc 0×0 in read in psymtab, but not in symtab.)
(Internal error: pc 0×3 in read in psymtab, but not in symtab.)
(Internal error: pc 0×3 in read in psymtab, but not in symtab.)
(Internal error: pc 0×3 in read in psymtab, but not in symtab.)
(Internal error: pc 0×3 in read in psymtab, but not in symtab.)
(Internal error: pc 0×3 in read in psymtab, but not in symtab.)
(Internal error: pc 0×3 in read in psymtab, but not in symtab.)
(Internal error: pc 0×3 in read in psymtab, but not in symtab.)
(Internal error: pc 0×3 in read in psymtab, but not in symtab.)
(Internal error: pc 0×3 in read in psymtab, but not in symtab.)
(Internal error: pc 0×3 in read in psymtab, but not in symtab.)

I don’t have any idea of what’s happening…

PS. Koahnig as you are the only one helping me for now, maybe it would be better to change the type of communication from forum to communicator? It’d be better to talk in real time. I can also send you entire project, bacause maybe there’s the source of errors elsewhere and I don’t know it?

May 21, 2011

koahnig koahnig
Area 51 Engineer
2879 posts

Just out of curiousity, can you show me the statement declaring the arrays autoEnemyTimer and enemy?

What is the type of compiler you are using? Respectively you should specify the ewhoel environment you are using.
I do not recognize the internal error statements you are posting.

Side mark: Changing the channel of communication may change the speed of communication, but not necessarily the quality. I have only little time to spare to help you anyhow. A big project meeting is coming up. So, let us hope that someone else is joining the discussion.

May 21, 2011

Gerolf Gerolf
Robot Herder
3235 posts

I think it would be the easiest, if you try reducing your problem to as small as possible and put the source code somewherem, so we cxan look at it, perhaps dropbox, gitorious, etc.

 Signature 

Nokia Certified Qt Specialist.
Programming Is Like Sex: One mistake and you have to support it for the rest of your life. (Michael Sinz)

May 21, 2011

Jizin Jizin
Lab Rat
12 posts

Ok, I don’t have any idea of how the gitorious work, so I uploaded the whole project here:
<problem solved, link deleted>

It’s the version without bugs and errors. As I mentioned before there are some unused functions, signals or slot (used for testing only). As I said I just want to add the independent movement for enemies and in the future some graphic (I don’t even know how to start with it).
If you want I can rehost it with comments.

May 21, 2011

koahnig koahnig
Area 51 Engineer
2879 posts

Hi Jizin,
I guess everybody will have a problem to analyse your program in an instance and give help. Gerolf’s advice is a good one in that respect. Reducing your problem to a small example gives people a chance to help you.

If you cannot isolate your problem you might considering

koahnig wrote:
…, can you show me the statement declaring the arrays autoEnemyTimer and enemy?

What is the type of compiler you are using? Respectively you should specify the whole environment you are using.

May 22, 2011

Jizin Jizin
Lab Rat
12 posts

The simple example:
<problem solved, link deleted>

Ok, so now I left only the bomberboard containing bomberfield and the enemy class. So I’m making 5 enemies, start 5 enemyTimers, connect their timeout() signals to the slots, then I random their direction, check their new position and if it’s good then change it. After that I update the whole bomberfield and… nothing happens. The problem is probably in the wrong timer-slot connection or in wrong refreshin the enemies position or both of them. I don’t know hot to solve this.

May 22, 2011

koahnig koahnig
Area 51 Engineer
2879 posts

Sorry, did you ever try to use your debugger?
This is not a signal-slot problem. Set a breakpoint in the slot routines and you will find out that the slots are called regularly. The counter “int element” is increased each time.
I guess you should either use the debugger or output statements to a file to find out if the program is doing as you expect.

May 22, 2011

Jizin Jizin
Lab Rat
12 posts

//Please read from the edit//

Umm… I don’t know what are you talking to me about. How do I set a breakpoint in the slot routines? What is it? I’m using the debugger but it’s running without errors. Sorry for lack in my knowledge but I’m still learning the QT. Actually I’m in the high school and It’s my project for a 6 grade. We didn’t have too much lessons with QT and almost all what I used I learnt from the tutorials on this page.

But back to topic:
Could you explain it in clearlier way, that I could understand? Maybe step-after-step? I don’t have any idea how to set breakpoints…

PS. Maybe we should change the channel of the communication? If u still want to help me. The forum isn’t fast enough. I did not mention that I have to finish it by the end of May.

//edit
Hi Volker. Thanks for reply and for this load of useful advices. After reading this link I think I get your point. I’m just stressed and now I know – that’s my problem.

So I’ll give some more details now:
I’m using:
Qt Creator 2.0.1
Based on Qt 4.7.0 (32 bit)

Built on Aug 24 2010 at 11:00:55

I’ve read about the breakpoints and now I understand it. The loops, as you said are working properly, connects aren’t the problem. The enemies are probably moving in the bomberfield.cpp but it’s not transfered to the enemy.cpp. Their centers don’t change so they don’t change in the paintEvent as well and in effect don’t move.
From what I learnt until now about connect() is that the signals’ and slots’ parameters must be the same. So how can I transfer the data from the:

  1. int curEnemyX[5];
  2. int curEnemyY[5];

in bomberfield.cpp to:
  1. QRect Enemy::enemyRect(){
  2.      QRect result(0, 0, 39, 39);
  3.      result.moveCenter(QPoint(curX, curY));
  4.      return result;
  5.  }

in enemy.cpp every time the timeout() signal is called?

May 22, 2011

Volker Volker
Ant Farmer
5428 posts
Jizin wrote:
PS. Maybe we should change the channel of the communication? If u still want to help me. The forum isn’t fast enough. I did not mention that I have to finish it by the end of May.

This statement will most likely drive you to the point where you get no answers at all and make everyone involved so far upset.

This forum (as many others too) is driven by volunteers. Requesting them to do things for you becaus you have a problem, is for sure one of the worst habits.

Make sure to behave friendly in a forum, for example read and understand http://www.catb.org/~esr/faqs/smart-questions.html

This is not an official support channel of Nokia and Digia. And if it was, you’d have to pay commercial support to actually request things.

May 23, 2011

Jizin Jizin
Lab Rat
12 posts

Ok, I’ve thought about what you told me and I think I got the solution… I’ve finally made my enemies move, however it causes a lot of different and strange errors.
Here is the newest version of the project:
<problem solved, link deleted>

This time I cannot isolate my problem, because it’s a strange mix of everything I’ve made so far. So the enemies move only once, not in the loop. Furthermore now there are a lot of undefined, surprising connections. As example now the enemies move and sometimes: – everything is alright, – they disappear and do nothing more, – they disappear and some of the obstacles as well, – they are duplicating and 2 of them are reacting to the keyboard events: i press cursor key to move so one of them moves, I press another key and another one moves.

There are strange errors in the debuger as well. All of the problems appears randomly.
I think that the first issue (about only one move) is probably here:

  1.  void Enemy::moveEnemy(){
  2.      static int element = 0;
  3.      emit timerRunningFalse(element);
  4.      element++;
  5.  }

I need the element variable to know which enemy move, but after it increases up to 5 (all of the enemies move) it’s not zeroed… Where should I put in it the 0 value?

The other problems are about autoEnemyTimer[i] (which isn’t stopped nowhere, so that can cause problems) and the obstacle[i]->existence variable. I first make enemies and then obstacles (because they’re dependent on each other, and it was just easier to remember 5 enemies than ~150 obstacles), so they’re maybe moving because the obstacle is made or the variable is set…

//edit
After some workout I finally made it. The problem was in the code I posted before. The element just increased without end and there are only 5 enemies. Adding this:

  1. if(element > 4) element = 0;

solved everything. Now the enemies move continously and withing the area I set them (they don’t go over the edges of map and on the obstacles). The next think I’m working on is a proper algorithm for moving (for now it’s only randomizing only 1 of 4 possible directions, which makes it they barely move (goes one step right and one step left, up and down onto the same position).

Page  
1

  ‹‹ Getting errors when trying to compile qt-labs-qt3d in windows with visual studio 2008      if i want to place QWebKit on top of OpenGL what is the best way to do it ? ››

You must log in to post a reply. Not a member yet? Register here!