Towers lasers and spacecrafts example: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 20: Line 20:
** Explode and die
** Explode and die


"View example on Youtube":http://www.youtube.com/watch?v=cofUk8BK7nE
"View example on Youtube":http://www.youtube.com/watch?v=cofUk8BK7nE


[YouTubeID:cofUk8BK7nE]
[YouTubeID:cofUk8BK7nE]
Line 26: Line 26:
== Main.cpp ==
== Main.cpp ==


The main() method creates a scene and a QTimer.<br />The timer calls Scene::advance() every 0.01 sec.
The main() method creates a scene and a QTimer.
The timer calls Scene::advance() every 0.01 sec.


<code><br />#include &lt;QtGui&amp;gt;<br />#include &quot;scene.h&amp;quot;<br />#include &quot;simpletower.h&amp;quot;
<code>
#include <QtGui>
#include "scene.h"
#include "simpletower.h"


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(&amp;scene);<br /> QTimer timer;<br /> QObject::connect(&amp;timer, SIGNAL (timeout()), &amp;scene, SLOT (advance()));<br /> view.show();<br /> timer.start(10);<br /> return app.exec&amp;amp;#40;&amp;#41;;<br /> }
int main(int argc, char '''argv)
<br /></code>
{
QApplication app(argc, argv);
Scene scene;
scene.setSceneRect(0,0,640,360);
QGraphicsView view(&amp;scene);
QTimer timer;
QObject::connect(&amp;timer, SIGNAL (timeout()), &amp;scene, SLOT (advance()));
view.show();
timer.start(10);
return app.exec();
}


<br />h2. The Scene
</code>
<br />* The scene constructor creates several towers<br />* Scene::advance() :<br />''' has a counter m_TicTacTimer<br />'''''' every 20th ticTac, a new mobile unit is created with a random position, direction and speed.<br />'''''' dead units are removed from the item list
 
 
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'''
'''scene.h'''


<code><br />#ifndef SCENE_H<br />#define SCENE_H
<code>
#ifndef SCENE_H
#define SCENE_H


#include &lt;QGraphicsScene&amp;gt;
#include <QGraphicsScene>


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 />};
class Scene : public QGraphicsScene
{
Q_OBJECT
public:
Scene();
public slots:
void advance();
private:
int m_TicTacTime;
};


#endif // SCENE_H<br /></code>
#endif // SCENE_H
</code>


'''scene.cpp'''
'''scene.cpp'''


<code><br />#include &quot;scene.h&amp;quot;<br />#include &quot;mobileunit.h&amp;quot;<br />#include &quot;simpletower.h&amp;quot;<br />#include&amp;lt;QDebug&amp;gt;
<code>
#include "scene.h"
#include "mobileunit.h"
#include "simpletower.h"
#include<QDebug>
 
Scene::Scene()
: QGraphicsScene()
, 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
QGraphicsItem '''item=NULL;
MobileUnit''' unit=NULL;
int i=0;
while (i<items().count())
{
item=items().at(i);
unit=dynamic_cast<MobileUnit* > (item);
if ( ( unit!=NULL) &amp;&amp; (unit->isFinished()==true))
{
removeItem(item);
delete unit;
}
elsei;
}


Scene::Scene()<br /> : QGraphicsScene()<br /> , m_TicTacTime(0)<br />{<br /> SimpleTower * simpleTower = new SimpleTower();<br /> simpleTower-&gt;setPos(200.0, 100.0);<br /> addItem(simpleTower);
// 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);
}


simpleTower = new SimpleTower();<br /> simpleTower-&gt;setPos(200.0, 180.0);<br /> addItem(simpleTower);
QGraphicsScene::advance();
update();
}
</code>


simpleTower = new SimpleTower();<br /> simpleTower-&gt;setPos(200.0, 260.0);<br /> addItem(simpleTower);
h2. Towers


simpleTower = new SimpleTower();<br /> simpleTower-&gt;setPos(250.0, 050.0);<br /> addItem(simpleTower);
* 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.


simpleTower = new SimpleTower();<br /> simpleTower-&gt;setPos(250.0, 310.0);<br /> addItem(simpleTower);
* 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 = new SimpleTower();<br /> simpleTower-&gt;setPos(300.0, 110.0);<br /> addItem(simpleTower);
'''simpletower.h'''
<code>
#ifndef SIMPLETOWER_H
#define SIMPLETOWER_H


simpleTower = new SimpleTower();<br /> simpleTower-&gt;setPos(300.0, 250.0);<br /> addItem(simpleTower);
#include <QtCore>
#include <QGraphicsRectItem>


simpleTower = new SimpleTower();<br /> simpleTower-&gt;setPos(350.0, 180.0);<br /> addItem(simpleTower);
class MobileUnit;


class SimpleTower : public QGraphicsRectItem
{
public:
SimpleTower();
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget '''widget);
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
</code>
'''simpletower.cpp'''
<code>
#include <QPainter>
#include <QGraphicsScene>
#include "simpletower.h"
#include "mobileunit.h"
SimpleTower::SimpleTower()
: QGraphicsRectItem()
, m_DetectionDistance(100.0)
, m_Time(0, 0)
, m_ReloadTime(100)
, m_ShootIsActive(false)
, m_Target(NULL)
, m_TowerImage(QImage(":/lightTower"))
{
setRect(–15.0, -15.0, 30.0, 30.0);
m_Time.start();
}
}


void Scene::advance()<br />{<br /> m_TicTacTime+'';
void SimpleTower::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget '''widget)
<br /> // delete killed objects<br /> QGraphicsItem '''item=NULL;<br /> MobileUnit''' unit=NULL;<br /> int i=0;<br /> while (i&amp;lt;items().count())<br /> {<br /> item=items().at(i);<br /> unit=dynamic_cast&amp;lt;MobileUnit* &gt; (item);<br /> if ( ( unit!=NULL) &amp;&amp; (unit-&gt;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() &lt;&lt; &quot;add unit&amp;quot;;<br /> MobileUnit * mobileUnit= new MobileUnit();<br /> qreal h=static_cast&amp;lt;qreal&amp;gt;( qrand() % static_cast&amp;lt;int&amp;gt;(height()) );<br /> mobileUnit-&gt;setPos(width(), h);<br /> addItem(mobileUnit);<br /> }
painter->drawImage(–15,-15,m_TowerImage);
<br /> QGraphicsScene::advance();<br /> update();<br />}<br /></code>
if ( (m_Target!=NULL) &amp;&amp; (m_ShootIsActive) )
<br />h2. Towers
{ // laser beam
<br />* Towers search for nearby units<br />'''''' Search is performed by squared distance comparison<br />'''''' Search stops when a first item is found<br />'''''' Warning: n against n = O (n2) algorithm… Poor performances on large amount of items… but sufficient here.
QPointF towerPoint = mapFromScene(pos());
<br />* Towers shoot the located item if any<br />* after shooting, a tower must wait until its weapon is reloaded : it requires a &quot;reload time&amp;quot; of 100ms<br />* The laser beam is drawn with lines of different thicknesses and colors
QPointF target = mapFromScene(m_Target->pos());
<br />'''simpletower.h'''<br /><code><br />#ifndef SIMPLETOWER_H<br />#define SIMPLETOWER_H
painter->setPen(QPen(Qt::yellow,8.0,Qt::SolidLine));
<br />#include &lt;QtCore&amp;gt;<br />#include &lt;QGraphicsRectItem&amp;gt;
painter->drawLine(towerPoint.x(), towerPoint.y(), target.x(), target.y());
<br />class MobileUnit;
painter->setPen(QPen(Qt::red,5.0,Qt::SolidLine));
<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 />};
painter->drawLine(towerPoint.x(), towerPoint.y(), target.x(), target.y());
<br />#endif // SIMPLETOWER_H<br /></code><br />'''simpletower.cpp'''
painter->setPen(QPen(Qt::white,2.0,Qt::SolidLine));
<br /><code><br />#include &lt;QPainter&amp;gt;<br />#include &lt;QGraphicsScene&amp;gt;<br />#include &quot;simpletower.h&amp;quot;<br />#include &quot;mobileunit.h&amp;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(&quot;:/lightTower&amp;quot;))<br />{<br /> setRect(–15.0, <s>15.0, 30.0, 30.0);<br /> m_Time.start();<br />}
painter->drawLine(towerPoint.x(), towerPoint.y(), target.x(), target.y());
<br />void SimpleTower::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget '''widget)<br />{<br /> painter-&gt;drawImage(–15,<s>15,m_TowerImage);<br /> if ( (m_Target!=NULL) &amp;&amp; (m_ShootIsActive) )<br /> { // laser beam<br /> QPointF towerPoint = mapFromScene(pos());<br /> QPointF target = mapFromScene(m_Target</s>&gt;pos());<br /> painter-&gt;setPen(QPen(Qt::yellow,8.0,Qt::SolidLine));<br /> painter-&gt;drawLine(towerPoint.x(), towerPoint.y(), target.x(), target.y());<br /> painter-&gt;setPen(QPen(Qt::red,5.0,Qt::SolidLine));<br /> painter-&gt;drawLine(towerPoint.x(), towerPoint.y(), target.x(), target.y());<br /> painter-&gt;setPen(QPen(Qt::white,2.0,Qt::SolidLine));<br /> painter-&gt;drawLine(towerPoint.x(), towerPoint.y(), target.x(), target.y());<br /> m_ShootIsActive=false;<br /> }<br />}
m_ShootIsActive=false;
<br />void SimpleTower::advance(int phase)<br />{<br /> if (phase==0)<br /> {<br /> searchTarget();<br /> if ( (m_Target!=NULL) &amp;&amp; (m_Time.elapsed()&gt; m_ReloadTime) )<br /> shoot();<br /> }<br />}
}
<br />void SimpleTower::searchTarget()<br />{<br /> m_Target=NULL;<br /> QList&amp;lt;QGraphicsItem''' &gt; itemList = scene()</s>&gt;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&amp;gt;=0) &amp;&amp; (NULL==m_Target) )<br /> {<br /> QGraphicsItem * item = itemList.at(i);<br /> unit = dynamic_cast&amp;lt;MobileUnit * &gt;(item);<br /> if ( (unit!=NULL) &amp;&amp; ( unit</s>&gt;lifePoints()&gt;0 ) )<br /> {<br /> dx = unit-&gt;x()<s>x();<br /> dy = unit</s>&gt;y()<s>y();<br /> sqrDist = dx*dx+dy*dy;<br /> if (sqrDist &lt; sqrDetectionDist)<br /> m_Target=unit;<br /> }<br /> —i;<br /> }<br />}
}
<br />void SimpleTower::shoot()<br />{<br /> m_ShootIsActive=true;<br /> m_Target</s>&gt;touched(3);<br /> m_Time.restart();<br />}<br /></code>
<br />h2. Mobile units


<br />Life Cycle of a mobile unit :
void SimpleTower::advance(int phase)
<br />* A mobile unit is created with 10 life points<br />* Position changes according to predefined direction and speed<br />* It loses life points if touched by a laser beam<br />* The unit explodes when lifepoints are exhausted<br />* The explosion has a duration.<br />* The explosion is drawn by painter-&gt;drawEllipse() with a growing radius
{
<br />'''mobileunit.h'''<br /><code>#ifndef MOBILEUNIT_H<br />#define MOBILEUNIT_H
if (phase==0)
<br />#include &lt;QtCore&amp;gt;<br />#include &lt;QGraphicsRectItem&amp;gt;<br />#include &lt;QRadialGradient&amp;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 />};
searchTarget();
<br />#endif // MOBILEUNIT_H<br /></code>
if ( (m_Target!=NULL) &amp;&amp; (m_Time.elapsed()> m_ReloadTime) )
<br />'''mobileunit.cpp'''
shoot();
}
}


<br /><code>#include &quot;mobileunit.h&amp;quot;<br />#include &lt;QPainter&amp;gt;<br />#include &lt;QGraphicsScene&amp;gt;<br />#include &lt;math.h&amp;gt;
void SimpleTower::searchTarget()
<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(&quot;:/spacecraft00&amp;quot;) )<br />{<br /> m_Alpha= static_cast&amp;lt;qreal&amp;gt; (qrand()&amp;#37;90+60);<br /> qreal speed= static_cast&amp;lt;qreal&amp;gt; (qrand()&amp;#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 />}
m_Target=NULL;
<br />void MobileUnit::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)<br />{<br /> painter</s>&gt;setPen(Qt::NoPen);
QList<QGraphicsItem''' > itemList = scene()->items();
int i = itemList.count()-1;
qreal dx, dy, sqrDist;
qreal sqrDetectionDist = m_DetectionDistance * m_DetectionDistance;
MobileUnit * unit=NULL;
while( (i>=0) &amp;&amp; (NULL==m_Target) )
{
QGraphicsItem * item = itemList.at(i);
unit = dynamic_cast<MobileUnit * >(item);
if ( (unit!=NULL) &amp;&amp; ( unit->lifePoints()>0 ) )
{
dx = unit->x()-x();
dy = unit->y()-y();
sqrDist = dx*dx+dy*dy;
if (sqrDist < sqrDetectionDist)
m_Target=unit;
}
—i;
}
}


if (!m_IsExploding)<br /> {<br /> painter-&gt;rotate(m_Alpha);<br /> painter-&gt;drawImage(–15,<s>14, m_SpacecraftImage);<br /> }<br /> else<br /> {<br /> painter</s>&gt;setBrush(QBrush(m_RedExplosion));<br /> qreal explosionRadius= 8.0 + m_Time.elapsed() / 50;<br /> painter-&gt;drawEllipse(-explosionRadius, <s>explosionRadius, 2.0*explosionRadius, 2.0*explosionRadius);<br /> }<br />}
void SimpleTower::shoot()
<br />void MobileUnit::advance(int phase)<br />{<br /> if (phase==0)<br /> {<br /> qreal xx=x(); qreal yy=y();<br /> if ( (xx&amp;lt;0.0) || (xx &gt; scene()</s>&gt;width() ) )<br /> { // rebond<br /> m_DirX=-m_DirX;<br /> m_Alpha=<s>m_Alpha;<br /> }<br /> if ( (yy&amp;lt;0.0) || (yy &gt; scene()</s>&gt;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() &gt; 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&amp;lt;0) m_LifePoints=0;<br /> if (m_LifePoints==0)<br /> {<br /> m_Time.start();<br /> m_IsExploding=true;<br /> }<br />}<br /></code>
m_ShootIsActive=true;
m_Target->touched(3);
m_Time.restart();
}
</code>
 
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'''
<code>#ifndef MOBILEUNIT_H
#define MOBILEUNIT_H
 
#include <QtCore>
#include <QGraphicsRectItem>
#include <QRadialGradient>
 
class MobileUnit : public QGraphicsRectItem
{
public:
MobileUnit();
inline int lifePoints() { return m_LifePoints; }
inline bool isFinished() const { return m_IsFinished; }
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
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
</code>
 
'''mobileunit.cpp'''
 
 
<code>#include "mobileunit.h"
#include <QPainter>
#include <QGraphicsScene>
#include <math.h>
 
MobileUnit::MobileUnit()
: QGraphicsRectItem()
, 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_SpacecraftImage(QImage(":/spacecraft00") )
{
m_Alpha= static_cast<qreal> (qrand()&amp;#37;90+60);
qreal speed= static_cast<qreal> (qrand()&amp;#37;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();
 
m_RedExplosion.setColorAt(0.0, Qt::white);
m_RedExplosion.setColorAt(0.2, QColor(255, 255, 100, 255));
m_RedExplosion.setColorAt(0.4, QColor(255, 80, 0, 200));
m_RedExplosion.setColorAt(1.0, QColor(255, 255, 255, 0));
}
 
void MobileUnit::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
painter->setPen(Qt::NoPen);
 
if (!m_IsExploding)
{
painter->rotate(m_Alpha);
painter->drawImage(–15,-14, m_SpacecraftImage);
}
else
{
painter->setBrush(QBrush(m_RedExplosion));
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;
}
}
</code>


[[Category:Learning::Demos_and_Examples]]<br />[[Category:Developing with Qt::Games]]
[[Category:Learning::Demos_and_Examples]]
[[Category:Developing with Qt::Games]]

Revision as of 09:07, 25 February 2015

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.

#include <QtGui>
#include "scene.h"
#include "simpletower.h"

int main(int argc, char '''argv)
 {
 QApplication app(argc, argv);
 Scene scene;
 scene.setSceneRect(0,0,640,360);
 QGraphicsView view(&amp;scene);
 QTimer timer;
 QObject::connect(&amp;timer, SIGNAL (timeout()), &amp;scene, SLOT (advance()));
 view.show();
 timer.start(10);
 return app.exec();
 }


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

#ifndef SCENE_H
#define SCENE_H

#include <QGraphicsScene>

class Scene : public 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()
 : QGraphicsScene()
 , 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
 QGraphicsItem '''item=NULL;
 MobileUnit''' unit=NULL;
 int i=0;
 while (i<items().count())
 {
 item=items().at(i);
 unit=dynamic_cast<MobileUnit* > (item);
 if ( ( unit!=NULL) &amp;&amp; (unit->isFinished()==true))
 {
 removeItem(item);
 delete unit;
 }
 elsei;
 }

 // 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);
 }

 QGraphicsScene::advance();
 update();
}

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

#ifndef SIMPLETOWER_H
#define SIMPLETOWER_H

#include <QtCore>
#include <QGraphicsRectItem>

class MobileUnit;

class SimpleTower : public QGraphicsRectItem
{
public:
 SimpleTower();
 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget '''widget);
 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()
 : QGraphicsRectItem()
 , m_DetectionDistance(100.0)
 , m_Time(0, 0)
 , m_ReloadTime(100)
 , m_ShootIsActive(false)
 , m_Target(NULL)
 , m_TowerImage(QImage(":/lightTower"))
{
 setRect(15.0, -15.0, 30.0, 30.0);
 m_Time.start();
}

void SimpleTower::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget '''widget)
{
 painter->drawImage(15,-15,m_TowerImage);
 if ( (m_Target!=NULL) &amp;&amp; (m_ShootIsActive) )
 { // laser beam
 QPointF towerPoint = mapFromScene(pos());
 QPointF target = mapFromScene(m_Target->pos());
 painter->setPen(QPen(Qt::yellow,8.0,Qt::SolidLine));
 painter->drawLine(towerPoint.x(), towerPoint.y(), target.x(), target.y());
 painter->setPen(QPen(Qt::red,5.0,Qt::SolidLine));
 painter->drawLine(towerPoint.x(), towerPoint.y(), target.x(), target.y());
 painter->setPen(QPen(Qt::white,2.0,Qt::SolidLine));
 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) &amp;&amp; (m_Time.elapsed()> m_ReloadTime) )
 shoot();
 }
}

void SimpleTower::searchTarget()
{
 m_Target=NULL;
 QList<QGraphicsItem''' > itemList = scene()->items();
 int i = itemList.count()-1;
 qreal dx, dy, sqrDist;
 qreal sqrDetectionDist = m_DetectionDistance * m_DetectionDistance;
 MobileUnit * unit=NULL;
 while( (i>=0) &amp;&amp; (NULL==m_Target) )
 {
 QGraphicsItem * item = itemList.at(i);
 unit = dynamic_cast<MobileUnit * >(item);
 if ( (unit!=NULL) &amp;&amp; ( 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();
}

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
#define MOBILEUNIT_H

#include <QtCore>
#include <QGraphicsRectItem>
#include <QRadialGradient>

class MobileUnit : public QGraphicsRectItem
{
public:
 MobileUnit();
 inline int lifePoints() { return m_LifePoints; }
 inline bool isFinished() const { return m_IsFinished; }
 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
 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()
 : QGraphicsRectItem()
 , 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_SpacecraftImage(QImage(":/spacecraft00") )
{
 m_Alpha= static_cast<qreal> (qrand()&amp;#37;90+60);
 qreal speed= static_cast<qreal> (qrand()&amp;#37;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();

 m_RedExplosion.setColorAt(0.0, Qt::white);
 m_RedExplosion.setColorAt(0.2, QColor(255, 255, 100, 255));
 m_RedExplosion.setColorAt(0.4, QColor(255, 80, 0, 200));
 m_RedExplosion.setColorAt(1.0, QColor(255, 255, 255, 0));
}

void MobileUnit::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
 painter->setPen(Qt::NoPen);

if (!m_IsExploding)
 {
 painter->rotate(m_Alpha);
 painter->drawImage(15,-14, m_SpacecraftImage);
 }
 else
 {
 painter->setBrush(QBrush(m_RedExplosion));
 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;
 }
}