Towers lasers and spacecrafts example

From Qt Wiki
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
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;
 }
}