Fire bullets example
-
Hello,
I just wanted to play a bit with QGraphicsScene. So I made the following simple example inspired by flash like 2D games.
"Fire bullets example on youtube":http://www.youtube.com/watch?v=8b4-mU5_tVU
[YouTubeID:8b4-mU5_tVU]
I post it hoping it can help some (And I hope, I'm in the right place...)
It creates a nice(?) flow of fire bullets, moving from the center of the scene to the cursor position.
The help:
The scene object:
- is a QGraphicsScene
- It reimplements some mouse events
- The press and release events starts and stops the fire bullets Timer.
- The method "fire" creates the "fire bullets".
- The method "advance" removes the bullets from the sceneRect before calling QGraphicsScene::advance();
Bullets are QGraphicsEllipseItem brushed with a radial gradient
The game engine(!) is a QTimer in main.cpp. It asks the scene to advance every 0.01s
Main.cpp
@
#include <QtCore>
#include <QtGui>
#include "scene.h"int main(int argc, char **argv)
{
QApplication app(argc, argv);Scene scene; scene.setSceneRect(0.0, 0.0, 400.0, 400.0); scene.setBackgroundBrush(Qt::black); QTimer *timer = new QTimer(); QObject::connect(timer, SIGNAL(timeout()), &scene, SLOT(advance())); timer->start(10); QGraphicsView window(&scene); window.show(); return app.exec();
}@
scene.h
@#ifndef SCENE_H
#define SCENE_H#include <QGraphicsScene>
class Scene : public QGraphicsScene
{
Q_OBJECT
public:
Scene();
virtual void mousePressEvent ( QGraphicsSceneMouseEvent * mouseEvent );
virtual void mouseMoveEvent ( QGraphicsSceneMouseEvent * mouseEvent );
virtual void mouseReleaseEvent ( QGraphicsSceneMouseEvent * mouseEvent );
public slots:
void fire();
void advance();
private:
QTimer * m_FireTimer;
QPointF m_FireTarget;
};
#endif // SCENE_H@scene.cpp
@#include "scene.h"
#include <QGraphicsEllipseItem>
#include <QGraphicsSceneMouseEvent>
#include <QTimer>
#include <math.h>class GraphicsCircle : public QGraphicsEllipseItem
// class for the fire bullets
{
public:
GraphicsCircle(qreal dirx, qreal diry)
: m_Speed(5.0)
, m_DirX(dirx)
, m_DirY(diry)
{
setRect(-20.0,-20.0,40.0,40.0);
setPos(200,200);
QRadialGradient rGrad( 0.0, 0.0, 20.0, 0.0, 0.0);
rGrad.setColorAt(0.0, QColor(255,255,255));
rGrad.setColorAt(0.7, QColor(200,100,20));
rGrad.setColorAt(1.0, QColor(255,0,0,0));
setBrush(QBrush(rGrad) );
setPen(QPen(Qt::NoPen));
}virtual ~GraphicsCircle() {} void advance(int phase) { setPos(x()+m_Speed*m_DirX, y()+m_Speed*m_DirY); }
private:
QBrush m_Brush;
qreal m_Speed;
qreal m_DirX;
qreal m_DirY;
};Scene::Scene() : QGraphicsScene()
{
// the fire timer has the responsability to create bullets
// it will be started when needed : when mouse is pressed
m_FireTimer= new QTimer();
QObject::connect(m_FireTimer, SIGNAL(timeout()), this, SLOT(fire()));
}void Scene::mousePressEvent ( QGraphicsSceneMouseEvent * mouseEvent )
{
m_FireTarget = mouseEvent->scenePos();
m_FireTimer->start(10);
QGraphicsScene::mousePressEvent(mouseEvent);
}void Scene::mouseMoveEvent ( QGraphicsSceneMouseEvent * mouseEvent )
{
m_FireTarget = mouseEvent->scenePos();
QGraphicsScene::mouseMoveEvent(mouseEvent);
}void Scene::mouseReleaseEvent ( QGraphicsSceneMouseEvent * mouseEvent )
{
m_FireTimer->stop();
QGraphicsScene::mouseReleaseEvent(mouseEvent);
}void Scene::fire()
// creates a fire bullet
// the bullet will move in the direction of the mouse cursor
// the trajectory is sligthly perturbated by a random small angle
{
qreal dirx = m_FireTarget.x()-200.0;
qreal diry = m_FireTarget.y()-200.0;qreal length = sqrt(dirx*dirx+diry*diry); if (length!=0) { // normalized direction vector qreal invLength= 1.0/length; dirx *= invLength; diry *= invLength; // creating an angle perturbation of +/- 3° qreal alphaPerturbation = static_cast<qreal>(qrand()%6-3) * M_PI / 180.0; qreal xPerturbation = cos(alphaPerturbation); qreal yPerturbation = sin(alphaPerturbation); // cos(a+b)=... dirx = dirx*xPerturbation - diry*yPerturbation; // sin(a+b)=... diry = diry*xPerturbation + dirx*yPerturbation; GraphicsCircle * circle = new GraphicsCircle(dirx, diry); addItem(circle); }
}
void Scene::advance()
{
// we first remove the circle out of the sceneRect
for (int i=0; i<items().count(); ++i)
{
QGraphicsItem * item = items().at(i);
qreal x= item->x();
qreal y= item->y();
qreal sx=sceneRect().width();
qreal sy= sceneRect().height();
if ( (x < 0.0) || (y < 0.0) || (x > sx) || (y > sy))
{
removeItem(item);
delete item;
}
}
QGraphicsScene::advance();
}
@ -
Thank you for this post.
-
[quote author="xsacha" date="1291475228"]I can see this being used in one of those 2D tower games.
I'm just wondering: shouldn't the timer be (less than?) 1/refresh rate rather than just 0.01s. To save unnecessary calculations and possibly remove tearing.[/quote]
Which is the game you are referring to? Is the code available.
-
Oh it's a classic game, repeated everywhere. I'm talking about Tower Defence.
Here's an opensource Qt version: http://gitorious.org/open-tower-defence
-
[quote author="xsacha" date="1291476247"]Oh it's a classic game, repeated everywhere. I'm talking about Tower Defence.
Here's an opensource Qt version: http://gitorious.org/open-tower-defence[/quote]
Thank you for the link. I was not aware of this.
-
[quote author="xsacha" date="1291475228"]I'm just wondering: shouldn't the timer be (less than?) 1/refresh rate rather than just 0.01s. To save unnecessary calculations and possibly remove tearing.[/quote]
Yes, you are right.
How do you retrieve the refresh rate with Qt ? -
Avoiding tearing in Qt by sticking with the screens refresh rate is discussed by Gunnar "here":http://labs.qt.nokia.com/2010/12/02/velvet-and-the-qml-scene-graph/
-
I read that post when it came out but it doesn't really explain the code behind it. I guess it's in the repository, but that's a fair bit of reading.
That blog was more about 'what not to do' really.I would be interested in finding out how to discover refresh rate in Qt. Is there something in Qt for system information?
Obviously using 16.66ms wasn't working in that QML Scene Graph example ;).