Towers lasers and spacecrafts example
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
[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(&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;
}
}