Towers lasers and spacecrafts example

From Qt Wiki
Jump to navigation Jump to search
This article may require cleanup to meet the Qt Wiki's quality standards. Reason: Auto-imported from ExpressionEngine.
Please improve this article if you can. Remove the {{cleanup}} tag and add this page to Updated pages list after it's clean.

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


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(&scene);
 QTimer timer;
 QObject::connect(&timer, SIGNAL (timeout()), &scene, SLOT (advance()));
 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>

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) && (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();
}

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) && (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) && (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) && (NULL==m_Target) )
 {
 QGraphicsItem * item = itemList.at(i);
 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>

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

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