Simple example of game scene with QGraphicsScene
This simple example mostly shows how to deal with
- QGraphicsScene::advance()
- QGraphicsItem::advance()
- QGraphicsItem::paint(…)
Overview
The example contains three classes with following responsabilities
- The scene is a QGraphicsScene
- Creates and deletes new items (towers and mobile units)
- Towers
- search for nearby units
- shoot them if any
- Mobile units (fighters)
- Move
- Explode and die
View example on Youtube [youtube.com]
Main.cpp
The main() method creates a scene and a QTimer.
The timer calls Scene::advance() every 0.01 sec.
- #include <QtGui>
- #include "scene.h"
- #include "simpletower.h"
- int main(int argc, char **argv)
- {
- Scene scene;
- scene.setSceneRect(0,0,640,360);
- QTimer timer;
- view.show();
- timer.start(10);
- return app.exec();
- }
The Scene
- The scene constructor creates several towers
- Scene::advance() :
- has a counter m_TicTacTimer
- every 20th ticTac, a new mobile unit is created with a random position, direction and speed.
- dead units are removed from the item list
scene.h
- #ifndef SCENE_H
- #define SCENE_H
- #include <QGraphicsScene>
- {
- Q_OBJECT
- public:
- Scene();
- public slots:
- void advance();
- private:
- int m_TicTacTime;
- };
- #endif // SCENE_H
scene.cpp
- #include "scene.h"
- #include "mobileunit.h"
- #include "simpletower.h"
- #include<QDebug>
- Scene::Scene()
- , m_TicTacTime(0)
- {
- SimpleTower * simpleTower = new SimpleTower();
- simpleTower->setPos(200.0, 100.0);
- addItem(simpleTower);
- simpleTower = new SimpleTower();
- simpleTower->setPos(200.0, 180.0);
- addItem(simpleTower);
- simpleTower = new SimpleTower();
- simpleTower->setPos(200.0, 260.0);
- addItem(simpleTower);
- simpleTower = new SimpleTower();
- simpleTower->setPos(250.0, 050.0);
- addItem(simpleTower);
- simpleTower = new SimpleTower();
- simpleTower->setPos(250.0, 310.0);
- addItem(simpleTower);
- simpleTower = new SimpleTower();
- simpleTower->setPos(300.0, 110.0);
- addItem(simpleTower);
- simpleTower = new SimpleTower();
- simpleTower->setPos(300.0, 250.0);
- addItem(simpleTower);
- simpleTower = new SimpleTower();
- simpleTower->setPos(350.0, 180.0);
- addItem(simpleTower);
- }
- void Scene::advance()
- {
- m_TicTacTime++;
- // delete killed objects
- MobileUnit * unit=NULL;
- int i=0;
- while (i<items().count())
- {
- item=items().at(i);
- unit=dynamic_cast<MobileUnit* > (item);
- if ( ( unit!=NULL) && (unit->isFinished()==true))
- {
- removeItem(item);
- delete unit;
- }
- else ++i;
- }
- // Add new units every 20 tictacs
- if(m_TicTacTime % 20==0)
- {
- // qDebug() << "add unit";
- MobileUnit * mobileUnit= new MobileUnit();
- qreal h=static_cast<qreal>( qrand() % static_cast<int>(height()) );
- mobileUnit->setPos(width(), h);
- addItem(mobileUnit);
- }
- update();
- }
Towers
- Towers search for nearby units
- Search is performed by squared distance comparison
- Search stops when a first item is found
- Warning: n against n = O(n2) algorithm… Poor performances on large amount of items… but sufficient here.
- Towers shoot the located item if any
- after shooting, a tower must wait until its weapon is reloaded : it requires a “reload time” of 100ms
- The laser beam is drawn with lines of different thicknesses and colors
simpletower.h
- #ifndef SIMPLETOWER_H
- #define SIMPLETOWER_H
- #include <QtCore>
- #include <QGraphicsRectItem>
- class MobileUnit;
- {
- public:
- SimpleTower();
- void advance(int phase);
- private:
- void searchTarget();
- void shoot();
- private:
- qreal m_DetectionDistance;
- QTime m_Time;
- int m_ReloadTime;
- bool m_ShootIsActive;
- MobileUnit * m_Target;
- QImage m_TowerImage;
- };
- #endif // SIMPLETOWER_H
simpletower.cpp
- #include <QPainter>
- #include <QGraphicsScene>
- #include "simpletower.h"
- #include "mobileunit.h"
- SimpleTower::SimpleTower()
- , m_DetectionDistance(100.0)
- , m_Time(0, 0)
- , m_ReloadTime(100)
- , m_ShootIsActive(false)
- , m_Target(NULL)
- {
- setRect(-15.0, -15.0, 30.0, 30.0);
- m_Time.start();
- }
- {
- painter->drawImage(-15,-15,m_TowerImage);
- if ( (m_Target!=NULL) && (m_ShootIsActive) )
- { // laser beam
- painter->drawLine(towerPoint.x(), towerPoint.y(), target.x(), target.y());
- painter->drawLine(towerPoint.x(), towerPoint.y(), target.x(), target.y());
- painter->drawLine(towerPoint.x(), towerPoint.y(), target.x(), target.y());
- m_ShootIsActive=false;
- }
- }
- void SimpleTower::advance(int phase)
- {
- if (phase==0)
- {
- searchTarget();
- if ( (m_Target!=NULL) && (m_Time.elapsed()> m_ReloadTime) )
- shoot();
- }
- }
- void SimpleTower::searchTarget()
- {
- m_Target=NULL;
- int i = itemList.count()-1;
- qreal dx, dy, sqrDist;
- qreal sqrDetectionDist = m_DetectionDistance * m_DetectionDistance;
- MobileUnit * unit=NULL;
- while( (i>=0) && (NULL==m_Target) )
- {
- unit = dynamic_cast<MobileUnit * >(item);
- if ( (unit!=NULL) && ( unit->lifePoints()>0 ) )
- {
- dx = unit->x()-x();
- dy = unit->y()-y();
- sqrDist = dx*dx+dy*dy;
- if (sqrDist < sqrDetectionDist)
- m_Target=unit;
- }
- --i;
- }
- }
- void SimpleTower::shoot()
- {
- m_ShootIsActive=true;
- m_Target->touched(3);
- m_Time.restart();
- }
Mobile units
Life Cycle of a mobile unit :
- A mobile unit is created with 10 life points
- Position changes according to predefined direction and speed
- It loses life points if touched by a laser beam
- The unit explodes when lifepoints are exhausted
- The explosion has a duration.
- The explosion is drawn by painter->drawEllipse(…) with a growing radius
mobileunit.h
- #ifndef MOBILEUNIT_H
- #define MOBILEUNIT_H
- #include <QtCore>
- #include <QGraphicsRectItem>
- #include <QRadialGradient>
- {
- public:
- MobileUnit();
- inline int lifePoints() { return m_LifePoints; }
- inline bool isFinished() const { return m_IsFinished; }
- void advance(int phase);
- void touched (int hurtPoints);
- private:
- int m_LifePoints;
- qreal m_Alpha;
- qreal m_DirX;
- qreal m_DirY;
- qreal m_Speed;
- bool m_IsFinished;
- bool m_IsExploding;
- int m_ExplosionDuration;
- QRadialGradient m_RedExplosion;
- QTime m_Time;
- QImage m_SpacecraftImage;
- };
- #endif // MOBILEUNIT_H
mobileunit.cpp
- #include "mobileunit.h"
- #include <QPainter>
- #include <QGraphicsScene>
- #include <math.h>
- MobileUnit::MobileUnit()
- , m_LifePoints(10)
- , m_Alpha(0)
- , m_DirX(1.0)
- , m_DirY(0.0)
- , m_Speed(1.0)
- , m_IsFinished(false)
- , m_IsExploding(false)
- , m_ExplosionDuration(500)
- , m_RedExplosion(0.0, 0.0, 20.0, 0.0, 0.0)
- , m_Time(0, 0)
- {
- m_Alpha= static_cast<qreal> (qrand()%90+60);
- qreal speed= static_cast<qreal> (qrand()%10-5);
- m_DirY=cos(m_Alpha/180.0*M_PI );
- m_DirX=sin(m_Alpha/180.0*M_PI);
- m_Alpha= -m_Alpha + 180.0 ;
- m_Speed=1.0+speed*0.1;
- setRect(-10.0, -10.0, 20.0, 20.0);
- m_Time.start();
- }
- {
- if (!m_IsExploding)
- {
- painter->rotate(m_Alpha);
- painter->drawImage(-15,-14, m_SpacecraftImage);
- }
- else
- {
- qreal explosionRadius= 8.0 + m_Time.elapsed() / 50;
- painter->drawEllipse(-explosionRadius, -explosionRadius, 2.0*explosionRadius, 2.0*explosionRadius);
- }
- }
- void MobileUnit::advance(int phase)
- {
- if (phase==0)
- {
- qreal xx=x(); qreal yy=y();
- if ( (xx<0.0) || (xx > scene()->width() ) )
- { // rebond
- m_DirX=-m_DirX;
- m_Alpha=-m_Alpha;
- }
- if ( (yy<0.0) || (yy > scene()->height()))
- { // rebond
- m_DirY=-m_DirY;
- m_Alpha=180-m_Alpha;
- }
- if (m_IsExploding)
- {
- m_Speed*=0.98; // decrease speed
- if (m_Time.elapsed() > m_ExplosionDuration)
- m_IsFinished=true; // is dead
- }
- setPos(x()+m_DirX*m_Speed, y()+m_DirY*m_Speed);
- }
- }
- void MobileUnit::touched (int hurtPoints)
- {
- m_LifePoints -=hurtPoints; // decrease life
- if (m_LifePoints<0) m_LifePoints=0;
- if (m_LifePoints==0)
- {
- m_Time.start();
- m_IsExploding=true;
- }
- }

