Bomberman - QTimer, QTimerEvent, Signals and Slots connection problems
-
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:
@#ifndef ENEMY_H
#define ENEMY_H#include <QWidget>
class QTimer;
class Enemy : public QWidget{
Q_OBJECTpublic:
Enemy(QWidget *parent=0, int x=0, int y=0);
void paintEnemy(QPainter &painter);
QRect enemyRect();
QPoint enemyCenter;
int curX, curY;
void saveEnemyCoordinates(int x, int y, int element);public slots:
void randomEnemyDirection();signals:
void moveEnemy(int element);protected:
private slots:
private:
};
#endif // ENEMY_H
@@#include <QPainter>
#include <QDateTime>#include "enemy.h"
Enemy::Enemy(QWidget *parent, int x, int y):QWidget(parent) {
enemyCenter = QPoint(x, y);
curX = x;
curY = y;
}void Enemy::paintEnemy(QPainter &painter){
painter.setPen(Qt::black);
painter.setBrush(Qt::white);
painter.drawEllipse(enemyRect());
}QRect Enemy::enemyRect(){
QRect result(0, 0, 39, 39);
static bool firstTime = true;
if (firstTime) {
firstTime = false;
QTime midnight(0, 0, 0);
qsrand(midnight.secsTo(QTime::currentTime()));
}
result.moveCenter(QPoint(rand() % 400, rand() % 400));
return result;
}void Enemy::saveEnemyCoordinates(int x, int y, int element){
static int curElement = element;
curX = x;
curY = y;
curElement++;
}void Enemy::randomEnemyDirection(){
static int element = 0;
emit moveEnemy(element);
element++;
}
@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:- 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...)
- How can I transfer the position from the class, where enemies are made to the class where is enemyRect(), the center of the enemy?
- What's the difference between the QTimer and QTimerEvent?
So summarizing, I don't know how:
Make this line in enemy.cpp:
@result.moveCenter(QPoint(rand() % 400, rand() % 400));@
something like this:
@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.
-
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":http://doc.qt.nokia.com/4.7/timers.html
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":http://doc.qt.nokia.com/4.7/signalsandslots.htmlThe 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.
-
Thanks for your reply. However, after reading your links, I've got some other questions:
- So what's the reason to use QTimerEvent for events, if I can connect the QTimer's signal timeout() to a correct slot?
- 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:
@for(int i = 0; i < numberEnemy; i++){
connect(autoEnemyTimer[i], SIGNAL(timeout()), enemy[i], SLOT(randomEnemyDirection()));
connect(enemy[i], SIGNAL(moveEnemy(int)), this, SLOT(randomEnemyDirection(int)));
}@
@void BomberField::randomEnemyDirection(int element){
int enemyDirection = 0;
static bool firstTime = true;
if (firstTime) {
firstTime = false;
QTime midnight(0, 0, 0);
qsrand(midnight.secsTo(QTime::currentTime()));
}
enemyDirection = rand() % 4;
if(enemyDirection == 0) tryEnemyMoveLeft(element);
if(enemyDirection == 1) tryEnemyMoveRight(element);
if(enemyDirection == 2) tryEnemyMoveDown(element);
if(enemyDirection == 3) tryEnemyMoveUp(element);
}@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. -
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. -
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?
@connect(autoBombTimer, SIGNAL(timeout()), this, SLOT(checkBombExistence()));
for(int i = 0; i < 5; i++){
connect(autoExplosionTimer[i], SIGNAL(timeout()), this, SLOT(checkExplosionExistence()));
}@ -
I've got another very strange error. When I changed the:
@for(int i = 0; i < numberEnemy; i++){
connect(autoEnemyTimer[i], SIGNAL(timeout()), enemy[i], SLOT(randomEnemyDirection()));
connect(enemy[i], SIGNAL(moveEnemy(int)), this, SLOT(randomEnemyDirection(int)));
}@
to:
@connect(autoEnemyTimer[0], SIGNAL(timeout()), enemy[0], SLOT(randomEnemyDirection()));
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 0x3 in read in psymtab, but not in symtab.)
(Internal error: pc 0x3 in read in psymtab, but not in symtab.)
(Internal error: pc 0x3 in read in psymtab, but not in symtab.)
(Internal error: pc 0x0 in read in psymtab, but not in symtab.)
(Internal error: pc 0x3 in read in psymtab, but not in symtab.)
(Internal error: pc 0x3 in read in psymtab, but not in symtab.)
(Internal error: pc 0x3 in read in psymtab, but not in symtab.)
(Internal error: pc 0x3 in read in psymtab, but not in symtab.)
(Internal error: pc 0x3 in read in psymtab, but not in symtab.)
(Internal error: pc 0x3 in read in psymtab, but not in symtab.)
(Internal error: pc 0x3 in read in psymtab, but not in symtab.)
(Internal error: pc 0x3 in read in psymtab, but not in symtab.)
(Internal error: pc 0x3 in read in psymtab, but not in symtab.)
(Internal error: pc 0x3 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?
-
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.
-
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. -
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
[quote author="koahnig" date="1305977811"]..., 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.
[/quote] -
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.
-
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. -
//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:
@int curEnemyX[5];
int curEnemyY[5];@
in bomberfield.cpp to:
@QRect Enemy::enemyRect(){
QRect result(0, 0, 39, 39);
result.moveCenter(QPoint(curX, curY));
return result;
}@
in enemy.cpp every time the timeout() signal is called? -
[quote author="Jizin" date="1306086194"]
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.[/quote]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.
-
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:
@ void Enemy::moveEnemy(){
static int element = 0;
emit timerRunningFalse(element);
element++;
}
@
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:
@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). -
//Edit
I've just solved it... The timers' timeout() were connected to the moveEnemy() where the elements increased up until the 5, but if 1 is destroyed then it went through doing nothing just taking the time and slowing the rest. I used another variable:
@bool enemyExistence;@
And the moving and painting is done only if this variable is true. I left the autoEnemyTimers alone and everything works fine.Hi. I've encountered another strange problem. After making moving enemies everything went well. Up until I decided to add the option to destroy them with the bombs. Destroying one makes the rest slowing... I added proper conditions:
@for(int i = 0; i < numberEnemy; i++){
if(bomb->bombCenter.x() - 20 > curEnemyX[i] - 40 && bomb->bombCenter.x() - 20 < curEnemyX[i] + 40 && bomb->bombCenter.y() + 20 > curEnemyY[i] - 40 && bomb->bombCenter.y() + 20 < curEnemyY[i] + 40)
autoEnemyTimer[i]->stop();
}@
Stoping the timer makes them unable to move anymore. Furthermore I added the condition in paintEvent:
@for(int i = 0; i < numberEnemy; i++){
if(autoEnemyTimer[i]->isActive())
enemy[i]->paintEnemy(painter, curEnemyX[i], curEnemyY[i]);
}@
So they aren't painted when destroyed. And that all works. But here's the problem. When one of them is destroyed it doesn't move anymore and isn't painted as well. But the rest start moving slower... With 5 of them the speed is good, but when only 1 is left it moves really slow... I don't know what's causing this problem and how to fix it. I've the
@int autoEnemyTimer[5]@
and start them independently. And I'm using them like:
@for(int i = 0; i < numberEnemy; i++){
connect(autoEnemyTimer[i], SIGNAL(timeout()), enemy[i], SLOT(moveEnemy()));
connect(enemy[i], SIGNAL(randomEnemyDirection(int)), this, SLOT(randomEnemyDirection(int)));
}@
Anyone know why there are these undefined connections between different autoEnemyTimers timeout() signals? -
So, It's me again. I finally made my game to the state when it's playable. Everything works as it should, but I just encountered another strange error, this time with the .exe of my application. When I open the project normally with QT, compile and run, everything is ok, and looks like this:
"Good one":http://imageshack.us/photo/my-images/845/goodu.png/
And when i try to run the .exe it looks like this:
"Your text to link here...":http://imageshack.us/photo/my-images/829/badp.png/
At first it wanted some libraries: mingwm10.dll, libgcc_s_dw2-1.dll, msvcp71d.dll, msvcr71d.dll, QtCored4.dll, QtGuid4.dll but after I copied them to the folder the application finally executed but with the effect shown above.
I also tried the release (copied libraries as well) and the effect is same. What can cause this problem?