Towers lasers and spacecrafts example

From Qt Wiki
Revision as of 14:44, 23 February 2015 by Maintenance script (talk | contribs)
Jump to navigation Jump to search

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 &lt;QtGui&amp;gt;<br />#include &quot;scene.h&amp;quot;<br />#include &quot;simpletower.h&amp;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(&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 /> }
<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 &lt;QGraphicsScene&amp;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 &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;

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

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

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

simpleTower = new SimpleTower();<br /> simpleTower-&gt;setPos(250.0, 050.0);<br /> addItem(simpleTower);

simpleTower = new SimpleTower();<br /> simpleTower-&gt;setPos(250.0, 310.0);<br /> addItem(simpleTower);

simpleTower = new SimpleTower();<br /> simpleTower-&gt;setPos(300.0, 110.0);<br /> addItem(simpleTower);

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

simpleTower = new SimpleTower();<br /> simpleTower-&gt;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&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 /> }
<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&quot; 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 &lt;QtCore&amp;gt;<br />#include &lt;QGraphicsRectItem&amp;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 &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 />}
<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 />}
<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 />


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 &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 />};
<br />#endif // MOBILEUNIT_H<br />


mobileunit.cpp


#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;
<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 />}
<br />void MobileUnit::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)<br />{<br /> painter</s>&gt;setPen(Qt::NoPen);

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