I'm developing a game with QtQuick 2 (Qt5.2) QML and C++. I want most of the game-logic in C++ (I don't want to do it with JS), and I'm trying to use QStateMachines for a lot of the logic.
Now my question is, how do I implement a proper game-loop in that scenario?
The game has for example objects that are moving from and to certain waypoints on the screen, so I think I can't do everything state/event-based. At some point I need to say "my object should move 10 pixels in direction x every second". So for example when my object is in its state "moving", it should move by a certain amount every second and then of course check for some conditions if it has to switch the state (e.g. to "attacking").
Now all the demos in the Qt-examples and on the web seem to be either fully event-based (e.g. four-in-a-row-wins-like) or written in JavaScript. So I am a bit lost here.
One idea I could find was to create a QTimer with a timer of e.g. 30ms and connect that QTimer's timeout() signal to an advance() slot of every moving object, and start that timer before 'return app.exec();'. Like this:
QTimer timer;
QObject::connect(&timer, SIGNAL(timeout()), &scene, SLOT(advance()));
timer.start(1000 / 33);
return app.exec();
and then each object (e.g. the Mouse) has a
void Mouse::advance(int step)
However, this requires a QGraphicsScene and I'm not sure how well that goes with a QtQuick/QML project on Android/iOS.
Is that a good solution? Or is my view of the problem somehow wrong and I don't need a game loop to accomplish my goal?
The solution shouldn't use any desktop-only stuff from Qt, i.e. it should work on Android, iOS and desktops.
That's the way to go: QTimer. Here you find some detailed example on it:
A typical loop for game in Qt:
int main(int argc, char* argv[]) {
// init stuff
while(game.isRunning()) {
a.processEvents(); //(a is a QApplication created during the init, should use a better name i guess)
QTime currentTime= QTime::currentTime();
int timeSinceLastUpdate = lastUpdate.msecsTo(currentTime);
while(timeSinceLastUpdate>updateTimeStep){
game.update();
timeSinceLastUpdate-=updateTimeStep;
lastUpdate=lastUpdateaddMSecs(updateTimeStep);
}
renderer.setInterpolateFraction(static_cast<float>(timeSinceLastUpdate)/static_cast<float>updateTimeStep);
renderer.renderGameObjects();
renderer.renderGUI();
renderer.swap();
}
a.exit();
return 0;
}
Source: Game loop in Qt
Making a Simple Game Loop with QTimer
Qt as a game engine
That's should be enough info for you to get started.
Usual game loop of simple game can look like this (not sure if I understand you correctly though).
Each class that represents game object have 2 public methods: update(); and render();
On each call of QTimer object you iterate over all game objects and call their update method. After it you repeat the same for render method();
In update methods each object decides what to do on game map (move/shot/stand/...) and changes its coordinates/properties. In render methods each object just draws itself on display.
Related
It feels like this question has been asked about a hundred times before (e.g. here) but I haven't found a working solution yet..
I have a Qt5 program (Linux) which takes some time (about 2sec) for initialization. I don't want to spawn a thread (for several reasons) and before initialization is done the program is not usable anyway.
Currently the program starts and it shows a black window, until initialization is done.
I'd like to have the window content be drawn as soon as possible and queue a method which does the rest which gets executed right after the main window has been drawn.
This is what I tried:
class my_window : public QMainWindow {
Q_OBJECT
explicit my_window(QWidget *parent = 0) : QMainWindow(parent) {
initializeUI();
/// UI is ready and should be drawn. initializeRest() should
/// be queued
/// tried to repaint() or update() the MainWindow and to 'force'
/// processing outstanding events - with no effect
update();
repaint();
QApplication::processEvents();
/// don't call - just queue
QMetaObject::invokeMethod(this, "initializeRest", Qt::QueuedConnection);
}
void initializeRest() {
// do stuff which takes a while
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
But the main window stayes black until initializeRest() has been executed.
How can I tell Qt to execute initializeRest() right after the window has been constructed?
I can think of starting a timer (bad, introduces extra latency) or an event handler which reacts on some kind of "WindowDrawn" event (bad, complicated).
What's the Qt-way to do this?
Update:
I've also tried to put the initializeRest() method into the main() function like suggested by Murphy:
my_window::my_window(QWidget *parent = 0) : QMainWindow(parent) {
initializeUI();
}
int main(int a_argsc, char *a_argsv[]) {
QApplication l_application(a_argsc, a_argsv);
my_window mainWindow;
mainWindow.show();
QApplication::processEvents();
mainWindow.initializeRest();
return l_application.exec();
}
With same results: Waiting for a couple of seconds inside initializeRest() makes show up the initially black main window and be drawn right after initializeRest() returned (which seems to be logical to me because the event loop has not been started yet..)
Note: This suggestion doesn't solve the issue; it's left here for completeness.
You can split the startup into smaller steps in main():
Create the QApplication instance.
Instantiate the main window (I'll call the variable mainWindow here). You can safely remove all that repaint-workaround stuff after initializeUI(); from the constructor of your code example.
Call mainWindow.show() to enforce showing the main window, followed by a call to QApplication::processEvents() to enforce the paint events being handled.
Do all the other initialization stuff of your application.
Start the event loop as usual by calling QApplication::exec().
Be aware that with complex applications/main window implementations it can get quite hairy to do everything in the right order; a QSplashScreen would surely be the less tedious solution.
I have the same problem. I think the problem is based not on show() function. Try to run the next code.
If add a button to boxLayout the application starts fast. But if I try setGeometry() the application takes a long time to start.
I wrote in C++ a solver for the 8-puzzle game, and now I'm trying to use Qt to give it a GUI.
Basically I have an underlying object of type "Board" which represents the board of the puzzle, and I have organized the GUI as a grid of QPushButton. Then I have a method updateUI which associates to every button the correct text, based on the Board. Something like
for(int i=0; i<Board::MATRIX_DIM * Board::MATRIX_DIM; i++)
{
m_buttons[i]->setText(m_values[i]);
}
In another method (solveGUI) I have
void MainWindow::solveGUI()
{
m_game->solve();
int solutionDepth = m_game->getSolutionDepth();
Move *solutionMoves = m_game->getSolutionMoves();
for(int i=0; i<solutionDepth; i++)
{
Move m = solutionMoves[i];
m_board.performMove(m); /* perform the move on the Board object */
updateUI(); /* should update the GUI so that it represents the Board */
Sleep(1000);
}
}
where the first line (m_game->solve) takes some time. Then I obtain a list of the moves performed, in solutionMoves, and what I would like to do is showing this moves on the board, with some delay between a move and the next one. This method is called by my main, which looks like this:
QApplication app(argc, argv);
MainWindow w;
w.show();
w.solveGUI();
return app.exec();
The result is that the GUI hangs and, after some time, it displays only the solution, completely skipping the moves.
What am I missing? Thank you!
P.S. I don't think I need a different Thread for the solver because I want the solver to run before the solution is displayed. Is it right?
It's app.exec() that actually runs the main loop which handles all events, including displaying GUI. If you want to call solve() before that, it's OK, but if you want to actually display and update GUI before exec(), it's wrong. I'm not sure if it's totally impossible, but it's definitely not the right way to do it.
There are two ways around it. The more canonical way is to redesign a program using a QTimer. Then everything will be smooth and responsive. But that can be tedious sometimes. In your case it should be quite easy, though. Just save the results somewhere, and call a slot using a QTimer object every 1000 seconds - it will have the same effect as your Sleep(), but will keep everything responsive.
The other solution is to call your solveGUI() method after exec() starts its job. It can be done, for example, using QTimer::singleShot():
QTimer::singleShot(0, &w, SLOT(showGUI()));
return app.exec();
Then, before each Sleep(), you should call QApplication::processEvents(), which basically allows you to temporary yield control, processing all pending events, including GUI updates. This approach is somewhat easier, but it's inferior since the GUI still freezes at each Sleep(). For example, if the user wants to exit the application, or if the window is needed to be repainted, it will cause uncomfortable GUI lags.
You're stalling the main thread (which also does the event processing) and rendering it uncapable of responding to keyboard/mouse/window messages.
You should use an asynchronous timer operation instead of the sleep function: use a QTimer to delay showing the next solution and avoid messages being left unanswered for too long.
There is a nice article of methods to keep the GUI responsive during processing loops. if it's not a complicated case I think, just insert QCoreApplication::processEvents(); inside the long processing loops.
try the following:
void MainWindow::Wait(int interval ) {
QTime timer = new QTime;
timer.restart();
while(timer.elapsed() < interval) {
QApplication::processEvents();
}
}
...
for(...) {
//wait 1 second (1000 milliseconds) between each loop run at first
Wait(1000);
...
}
...
not tested yet - but should work (maybe there is some cpu load)!
How can I run a game loop inside a Qt window without making a new thread?
My "RunFrame" method looks like this:
void GameWindow::RunFrame()
{
// Update the game time.
gameTime.Update();
timeBehind += gameTime.TotalTime() - lastTime;
lastTime = gameTime.TotalTime();
while( timeBehind >= targetTimeStep )
{
Update();
timeBehind -= targetTimeStep;
}
Draw();
SwapBuffers();
}
As you can see, it runs as if it is getting called every event loop. Is there a way I can get a method in my window to be called every event loop? Or should I redesign my loop around the timers in Qt?
I think QTimer is a working and relatively simple solution. It uses multimedia timers (if needed) to achive millisecond resolutions so it's feasible for a game loop.
I'm coding a Qt Gui and I'm trying to implement a new feature, I now the precise result I want to arrive at but not how to code it.
I'm trying to add a checkable button that when checked would run a function that would only stop when the button is unchecked, but every second a PaintArea I have on the window would be updated (letting me see how the multiple executions of my function are changing my data). It seem that I'll need to use some QThread objects, but just the part dealing with the button is already counter intuitive to me, I've been trying to play with the autoRepeatDelay and autoRepeatInterval without getting my hand on what they do and how they could be useful to me.
I guess that what I'm trying to code is not really original, would have an idea of the steps to implement it, or an example of a code?
Edit:
According to the first answers (thank you for them by the way) my question may not be clear. Putting on the side the thread thing, I'd like to implement an infinite loop that only starts when a pressbutton goes to pressed position (it's a checkable button) and stops only when leaving it. The first version I tried to do (with a while(button->isChecked() loop) would completely freeze as the application would be running the loop, the gui would freeze and the button couldn't be turned off (hence the idea of running it in a separate thread). Voila! I hope it's a clearer formulation. Thank you in advance.
Here's a simple skeleton of something that might work. Without knowing your exact requirements, it may or may not be right for your problem. Hopefully it will give you a few hints that do actually help.
void Ui::buttonPressedSlot(bool checked){
if (checked){
Processor *processor = new Processor;
connect(this, SIGNAL(abortCalculations()), processor, SLOT(abort()), Qt::QueuedConnection);
connect(processor, SIGNAL(updateNeeded()), this, SLOT(updateGui()), Qt::QueuedConnection);
QThreadPool::globalInstance()->start(processor);
} else {
emit abortCalculations(); // this is a signal in your UI class
}
}
You can then use the following for your calculations.
class Processor : public QObject, public QRunnable{ // QObject must always be first in multiple inheritance
Q_OBJECT
public:
~Processor();
void run();
public slots:
void abort();
void doCalculations();
signals:
void updateNeeded(); // connect this to the GUI to tell it to refresh
private:
QScopedPointer<QEventLoop> loop;
};
Processor::~Processor(){
abort();
}
void Processor::run() {
loop.reset(new QEventLoop);
QTimer timer;
connect(&timer, SIGNAL(timeout()), this, SLOT(doCalculations()));
timer.setInterval(1000);
timer.start();
loop->exec();
}
void Processor::abort(){
if (!loop.isNull()){
loop->quit();
}
}
void Processor::doCalculations(){
// do whatever needs to be done
emit updateNeeded();
}
I don't know if I really understand what you want to do, but I will try to answer.
First, you want a Button that send a start & stop info to control a thread. You can use a checkbox to begin. This check box send a signal when its state changes. Connect this signal to a slot that perform start thread and stop according to the boolean sent.
Second, in you thread you need to launch the events loop. After, set a timer that call you repaint after every timeout.
Hope it helped.
PS: take care of execution context with you thread and Qt's objects.
I am new to Qt and was trying to write a simple qt class that can plan a wav file.
After some reading and looking around I wrote the class and the code is as below. Questions follow after code
#include <QtGui/QApplication>
#include "playsound.h"
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
playSound w;
int ch = 2;
int ready = 1;
cout<<"ready"<<ready<<endl;
// w.show();
w.playwav(ch);
return a.exec();
}
Source code for playsound.cpp
#include "playsound.h"
playSound::playSound(QWidget *parent): QWidget(parent) {}
playSound::~playSound() {}
void playSound::playwav(int ch)
{
switch (ch)
{
case 1: {QSound::play("/home/alok/qtworkspace/sounds/abc.wav"); break;}
case 2: {QSound::play("/home/alok/qtworkspace/sounds/xyz.wav"); break;}
case 3: {QSound::play("/home/alok/qtworkspace/sounds/abc.wav"); break;}
case 4: {QSound::play("/home/alok/qtworkspace/sounds/aaa.wav"); break;}
case 5: {QSound::play("/home/alok/qtworkspace/sounds/nnn.wav"); break;}
}
}
Problems and questions:
1) I want to close the application once the sound is played.As of now it says program running and I have to forcefully close it using the red button in the "Application Output" area in Qt creator. I tried using close() from Qwidget and quit() from QApplication but probably I am doing it wrong. How to go abt this?
2) Can there be a simpler implementation for this functionality? You know something that does not involve event loops. I was trying to do it in old school C++ style where I call a function when I need to play a sound and be done with it but could not get it done. Suggestions most welcome on this one.
Thanks
I can offer an answer using PyQt4 (since I'm a python coder), but the idea is the same.
1) QSound is a very basic interface for playing sounds. It doesn't have any useful signals (though I think it really should). In order to know when the QSound is complete, you have to keep checking its isFinished() method. You could either do this in a loop in your main thread, or get more advanced and create a QThread which will play your sound, go into the loop, and then emit its own custom signal when its complete. The QThread is preferred because you should never block your main thread. You would then be able to connect this custom soundIsFinished() SIGNAL to say the close() SLOT of your widget, or any other handler.
If you want more advanced options, you can use the phonon module, which does have all of these various signals built in. Its a litte more annoying to set up, but then you won't need a QThread.
2) Event loops are the core concept of how Qt functions. Your main application always enters an event loop so that widgets can post their events and have them processed. You could technically use Qt without an event loop, but then its really pointless because you are just fighting against the framework and losing everything that its capable of.
To exit from an application, you have to close the top level widget (if you're App has the property verbosely named quitOnLastWindowClosed set to true, but this is default so you don't have to worry with it) or emit a quit signal to the QCoreApplication you've created.
In the example below, I've taken the easy way: emit a close signal.
As stated by jdi, it would be better to create a Thread, but I've understood that you're only learning Qt and wrote this as an example, so busy waiting for isFinished is good enough. Below an example of how it should go:
#include "playsound.h"
playSound::playSound(QWidget *parent): QWidget(parent) {}
playSound::~playSound() {}
void playSound::playwav(int ch)
{
QSound *player = 0; // Creates an object so that you can call player.isFinished()
// the isFinished function is not static.
switch (ch)
{
case 1: {player = new QSound("/home/alok/qtworkspace/sounds/abc.wav"); break;}
// other cases
}
if(!player)
this->close();
while(!player->isFinished()); // waits until the player has finished playing
delete player;
this->close(); // closes this widget, and
// as this Widget has no parent, i.e. it's the "top-level" widget,
// it'll close the app.
}
edit: Shame on me for not reading the docs how I should have. QSound does not have a default constructor, I've edited the code.
A few notes: as this is only a test for you to learn how to use Qt, I've created a pointer to QSound and deleted it afterward. This is not a good approach, you should not play with pointers as I did there, a much better solution would be only instantiating the object you would use. Having to delete things manually is not good, and it's really better to rely on the good ol' stack for that.