Towers lasers and spacecrafts example
h1. 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":http://www.youtube.com/watch?v=cofUk8BK7nE
[YouTubeID:cofUk8BK7nE]
Main.cpp
The main() method creates a scene and a QTimer.
The timer calls Scene::advance() every 0.01 sec.
<br />#include <QtGui&gt;<br />#include "scene.h&quot;<br />#include "simpletower.h&quot;
int main(int argc, char '''argv)<br /> {<br /> QApplication app(argc, argv);<br /> Scene scene;<br /> scene.setSceneRect(0,0,640,360);<br /> QGraphicsView view(&scene);<br /> QTimer timer;<br /> QObject::connect(&timer, SIGNAL (timeout()), &scene, SLOT (advance()));<br /> view.show();<br /> timer.start(10);<br /> return app.exec&amp;#40;&#41;;<br /> }
<br />
h2. 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
<br />#ifndef SCENE_H<br />#define SCENE_H
#include <QGraphicsScene&gt;
class Scene : public QGraphicsScene<br />{<br /> Q_OBJECT<br />public:<br /> Scene();<br />public slots:<br /> void advance();<br />private:<br /> int m_TicTacTime;<br />};
#endif // SCENE_H<br />
scene.cpp
<br />#include "scene.h&quot;<br />#include "mobileunit.h&quot;<br />#include "simpletower.h&quot;<br />#include&lt;QDebug&gt;
Scene::Scene()<br /> : QGraphicsScene()<br /> , m_TicTacTime(0)<br />{<br /> SimpleTower * simpleTower = new SimpleTower();<br /> simpleTower->setPos(200.0, 100.0);<br /> addItem(simpleTower);
simpleTower = new SimpleTower();<br /> simpleTower->setPos(200.0, 180.0);<br /> addItem(simpleTower);
simpleTower = new SimpleTower();<br /> simpleTower->setPos(200.0, 260.0);<br /> addItem(simpleTower);
simpleTower = new SimpleTower();<br /> simpleTower->setPos(250.0, 050.0);<br /> addItem(simpleTower);
simpleTower = new SimpleTower();<br /> simpleTower->setPos(250.0, 310.0);<br /> addItem(simpleTower);
simpleTower = new SimpleTower();<br /> simpleTower->setPos(300.0, 110.0);<br /> addItem(simpleTower);
simpleTower = new SimpleTower();<br /> simpleTower->setPos(300.0, 250.0);<br /> addItem(simpleTower);
simpleTower = new SimpleTower();<br /> simpleTower->setPos(350.0, 180.0);<br /> addItem(simpleTower);
}
void Scene::advance()<br />{<br /> m_TicTacTime+'';
<br /> // delete killed objects<br /> QGraphicsItem '''item=NULL;<br /> MobileUnit''' unit=NULL;<br /> int i=0;<br /> while (i&lt;items().count())<br /> {<br /> item=items().at(i);<br /> unit=dynamic_cast&lt;MobileUnit* > (item);<br /> if ( ( unit!=NULL) && (unit->isFinished()==true))<br /> {<br /> removeItem(item);<br /> delete unit;<br /> }<br /> elsei;<br /> }
<br /> // Add new units every 20 tictacs<br /> if(m_TicTacTime % 20==0)<br /> {<br /> // qDebug() << "add unit&quot;;<br /> MobileUnit * mobileUnit= new MobileUnit();<br /> qreal h=static_cast&lt;qreal&gt;( qrand() % static_cast&lt;int&gt;(height()) );<br /> mobileUnit->setPos(width(), h);<br /> addItem(mobileUnit);<br /> }
<br /> QGraphicsScene::advance();<br /> update();<br />}<br />
h2. 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
<br />#ifndef SIMPLETOWER_H<br />#define SIMPLETOWER_H
<br />#include <QtCore&gt;<br />#include <QGraphicsRectItem&gt;
<br />class MobileUnit;
<br />class SimpleTower : public QGraphicsRectItem<br />{<br />public:<br /> SimpleTower();<br /> void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget '''widget);<br /> void advance(int phase);<br />private:<br /> void searchTarget();<br /> void shoot();<br />private:<br /> qreal m_DetectionDistance;<br /> QTime m_Time;<br /> int m_ReloadTime;<br /> bool m_ShootIsActive;<br /> MobileUnit''' m_Target;<br /> QImage m_TowerImage;<br />};
<br />#endif // SIMPLETOWER_H<br />
simpletower.cpp
<br />#include <QPainter&gt;<br />#include <QGraphicsScene&gt;<br />#include "simpletower.h&quot;<br />#include "mobileunit.h&quot;<br />SimpleTower::SimpleTower()<br /> : QGraphicsRectItem()<br /> , m_DetectionDistance(100.0)<br /> , m_Time(0, 0)<br /> , m_ReloadTime(100)<br /> , m_ShootIsActive(false)<br /> , m_Target(NULL)<br /> , m_TowerImage(QImage(":/lightTower&quot;))<br />{<br /> setRect(–15.0, <s>15.0, 30.0, 30.0);<br /> m_Time.start();<br />}
<br />void SimpleTower::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget '''widget)<br />{<br /> painter->drawImage(–15,<s>15,m_TowerImage);<br /> if ( (m_Target!=NULL) && (m_ShootIsActive) )<br /> { // laser beam<br /> QPointF towerPoint = mapFromScene(pos());<br /> QPointF target = mapFromScene(m_Target</s>>pos());<br /> painter->setPen(QPen(Qt::yellow,8.0,Qt::SolidLine));<br /> painter->drawLine(towerPoint.x(), towerPoint.y(), target.x(), target.y());<br /> painter->setPen(QPen(Qt::red,5.0,Qt::SolidLine));<br /> painter->drawLine(towerPoint.x(), towerPoint.y(), target.x(), target.y());<br /> painter->setPen(QPen(Qt::white,2.0,Qt::SolidLine));<br /> painter->drawLine(towerPoint.x(), towerPoint.y(), target.x(), target.y());<br /> m_ShootIsActive=false;<br /> }<br />}
<br />void SimpleTower::advance(int phase)<br />{<br /> if (phase==0)<br /> {<br /> searchTarget();<br /> if ( (m_Target!=NULL) && (m_Time.elapsed()> m_ReloadTime) )<br /> shoot();<br /> }<br />}
<br />void SimpleTower::searchTarget()<br />{<br /> m_Target=NULL;<br /> QList&lt;QGraphicsItem''' > itemList = scene()</s>>items();<br /> int i = itemList.count()<s>1;<br /> qreal dx, dy, sqrDist;<br /> qreal sqrDetectionDist = m_DetectionDistance * m_DetectionDistance;<br /> MobileUnit * unit=NULL;<br /> while( (i&gt;=0) && (NULL==m_Target) )<br /> {<br /> QGraphicsItem * item = itemList.at(i);<br /> unit = dynamic_cast&lt;MobileUnit * >(item);<br /> if ( (unit!=NULL) && ( unit</s>>lifePoints()>0 ) )<br /> {<br /> dx = unit->x()<s>x();<br /> dy = unit</s>>y()<s>y();<br /> sqrDist = dx*dx+dy*dy;<br /> if (sqrDist < sqrDetectionDist)<br /> m_Target=unit;<br /> }<br /> —i;<br /> }<br />}
<br />void SimpleTower::shoot()<br />{<br /> m_ShootIsActive=true;<br /> m_Target</s>>touched(3);<br /> m_Time.restart();<br />}<br />
h2. 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<br />#define MOBILEUNIT_H
<br />#include <QtCore&gt;<br />#include <QGraphicsRectItem&gt;<br />#include <QRadialGradient&gt;
<br />class MobileUnit : public QGraphicsRectItem<br />{<br />public:<br /> MobileUnit();<br /> inline int lifePoints() { return m_LifePoints; }<br /> inline bool isFinished() const { return m_IsFinished; }<br /> void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);<br /> void advance(int phase);<br /> void touched (int hurtPoints);<br />private:<br /> int m_LifePoints;<br /> qreal m_Alpha;<br /> qreal m_DirX;<br /> qreal m_DirY;<br /> qreal m_Speed;<br /> bool m_IsFinished;<br /> bool m_IsExploding;<br /> int m_ExplosionDuration;<br /> QRadialGradient m_RedExplosion;<br /> QTime m_Time;<br /> QImage m_SpacecraftImage;<br />};
<br />#endif // MOBILEUNIT_H<br />
mobileunit.cpp
#include "mobileunit.h&quot;<br />#include <QPainter&gt;<br />#include <QGraphicsScene&gt;<br />#include <math.h&gt;
<br />MobileUnit::MobileUnit()<br /> : QGraphicsRectItem()<br /> , m_LifePoints(10)<br /> , m_Alpha(0)<br /> , m_DirX(1.0)<br /> , m_DirY(0.0)<br /> , m_Speed(1.0)<br /> , m_IsFinished(false)<br /> , m_IsExploding(false)<br /> , m_ExplosionDuration(500)<br /> , m_RedExplosion(0.0, 0.0, 20.0, 0.0, 0.0)<br /> , m_Time(0, 0)<br /> , m_SpacecraftImage(QImage(":/spacecraft00&quot;) )<br />{<br /> m_Alpha= static_cast&lt;qreal&gt; (qrand()&#37;90+60);<br /> qreal speed= static_cast&lt;qreal&gt; (qrand()&#37;10-5);<br /> m_DirY=cos(m_Alpha/180.0*M_PI );<br /> m_DirX=sin(m_Alpha/180.0*M_PI);<br /> m_Alpha= -m_Alpha'' 180.0 ;<br /> m_Speed=1.0+speed*0.1;<br /> setRect(–10.0, <s>10.0, 20.0, 20.0);<br /> m_Time.start();
<br /> m_RedExplosion.setColorAt(0.0, Qt::white);<br /> m_RedExplosion.setColorAt(0.2, QColor(255, 255, 100, 255));<br /> m_RedExplosion.setColorAt(0.4, QColor(255, 80, 0, 200));<br /> m_RedExplosion.setColorAt(1.0, QColor(255, 255, 255, 0));<br />}
<br />void MobileUnit::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)<br />{<br /> painter</s>>setPen(Qt::NoPen);
if (!m_IsExploding)<br /> {<br /> painter->rotate(m_Alpha);<br /> painter->drawImage(–15,<s>14, m_SpacecraftImage);<br /> }<br /> else<br /> {<br /> painter</s>>setBrush(QBrush(m_RedExplosion));<br /> qreal explosionRadius= 8.0 + m_Time.elapsed() / 50;<br /> painter->drawEllipse(-explosionRadius, <s>explosionRadius, 2.0*explosionRadius, 2.0*explosionRadius);<br /> }<br />}
<br />void MobileUnit::advance(int phase)<br />{<br /> if (phase==0)<br /> {<br /> qreal xx=x(); qreal yy=y();<br /> if ( (xx&lt;0.0) || (xx > scene()</s>>width() ) )<br /> { // rebond<br /> m_DirX=-m_DirX;<br /> m_Alpha=<s>m_Alpha;<br /> }<br /> if ( (yy&lt;0.0) || (yy > scene()</s>>height()))<br /> { // rebond<br /> m_DirY=<s>m_DirY;<br /> m_Alpha=180-m_Alpha;<br /> }<br /> if (m_IsExploding)<br /> {<br /> m_Speed*=0.98; // decrease speed<br /> if (m_Time.elapsed() > m_ExplosionDuration)<br /> m_IsFinished=true; // is dead<br /> }<br /> setPos(x()+m_DirX*m_Speed, y()+m_DirY*m_Speed);<br /> }<br />}
<br />void MobileUnit::touched (int hurtPoints)<br />{<br /> m_LifePoints</s>=hurtPoints; // decrease life<br /> if (m_LifePoints&lt;0) m_LifePoints=0;<br /> if (m_LifePoints==0)<br /> {<br /> m_Time.start();<br /> m_IsExploding=true;<br /> }<br />}<br />