summaryrefslogtreecommitdiffstats
path: root/examples/statemachine
diff options
context:
space:
mode:
authorEskil Abrahamsen Blomfeldt <eblomfel@trolltech.com>2009-04-30 09:13:44 +0200
committerEskil Abrahamsen Blomfeldt <eblomfel@trolltech.com>2009-04-30 09:13:44 +0200
commit0f8de3b0b4ccccbeba97999635eb5716f561fb30 (patch)
tree9f4832310807cd7d784295f79549bb64dab0a2a9 /examples/statemachine
parent19e7d27d9c107b69157591edd09a36e87c9d93b2 (diff)
Add two AIs: They are both designed to do the same. Spin until they see a tank
and then fire. One of them has an error, which causes it to enter its error state. The errorstate example has been changed to handle this by disabling the tank. The rest of the tanks will keep working.
Diffstat (limited to 'examples/statemachine')
-rw-r--r--examples/statemachine/errorstate/gameitem.h5
-rw-r--r--examples/statemachine/errorstate/mainwindow.cpp19
-rw-r--r--examples/statemachine/errorstate/plugin.h2
-rw-r--r--examples/statemachine/errorstate/rocketitem.cpp9
-rw-r--r--examples/statemachine/errorstate/rocketitem.h5
-rw-r--r--examples/statemachine/errorstate/tankitem.cpp56
-rw-r--r--examples/statemachine/errorstate/tankitem.h10
-rw-r--r--examples/statemachine/errorstateplugins/errorstateplugins.pro10
-rw-r--r--examples/statemachine/errorstateplugins/spin_ai/spin_ai.cpp29
-rw-r--r--examples/statemachine/errorstateplugins/spin_ai/spin_ai.h44
-rw-r--r--examples/statemachine/errorstateplugins/spin_ai/spin_ai.pro13
-rw-r--r--examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.cpp29
-rw-r--r--examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.h44
-rw-r--r--examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.pro13
14 files changed, 262 insertions, 26 deletions
diff --git a/examples/statemachine/errorstate/gameitem.h b/examples/statemachine/errorstate/gameitem.h
index 6ca32b7dd9..98087067e7 100644
--- a/examples/statemachine/errorstate/gameitem.h
+++ b/examples/statemachine/errorstate/gameitem.h
@@ -7,13 +7,8 @@ class QLineF;
class GameItem: public QGraphicsItem
{
public:
- enum { Type = UserType + 1 };
-
- int type() const { return Type; }
virtual void idle(qreal elapsed) = 0;
- virtual void hitByRocket() = 0;
-
protected:
QPointF tryMove(const QPointF &requestedPosition, QLineF *collidedLine = 0,
QGraphicsItem **collidedItem = 0) const;
diff --git a/examples/statemachine/errorstate/mainwindow.cpp b/examples/statemachine/errorstate/mainwindow.cpp
index e04ca94e57..daf7be0d41 100644
--- a/examples/statemachine/errorstate/mainwindow.cpp
+++ b/examples/statemachine/errorstate/mainwindow.cpp
@@ -105,6 +105,7 @@ void MainWindow::init()
QAction *addTankAction = menuBar()->addAction("&Add tank");
QAction *runGameAction = menuBar()->addAction("&Run game");
+ runGameAction->setObjectName("runGameAction");
QAction *stopGameAction = menuBar()->addAction("&Stop game");
menuBar()->addSeparator();
QAction *quitAction = menuBar()->addAction("&Quit");
@@ -112,9 +113,7 @@ void MainWindow::init()
connect(addTankAction, SIGNAL(triggered()), this, SLOT(addTank()));
connect(quitAction, SIGNAL(triggered()), this, SLOT(close()));
- m_machine = new QStateMachine(this);
- m_machine->setGlobalRestorePolicy(QStateMachine::RestoreProperties);
-
+ m_machine = new QStateMachine(this);
QState *stoppedState = new QState(m_machine->rootState());
stoppedState->setObjectName("stoppedState");
stoppedState->assignProperty(runGameAction, "enabled", true);
@@ -168,7 +167,11 @@ void MainWindow::runStep()
qreal elapsedSecs = elapsed / 1000.0;
QList<QGraphicsItem *> items = m_scene->items();
foreach (QGraphicsItem *item, items) {
- GameItem *gameItem = qgraphicsitem_cast<GameItem *>(item);
+ // ###
+ GameItem *gameItem = qgraphicsitem_cast<TankItem *>(item);
+ if (gameItem == 0)
+ gameItem = qgraphicsitem_cast<RocketItem *>(item);
+
if (gameItem != 0)
gameItem->idle(elapsedSecs);
}
@@ -206,7 +209,13 @@ void MainWindow::addTank()
emit mapFull();
QState *region = new QState(m_runningState);
- region->setInitialState(plugin->create(region, tankItem));
+ QState *pluginState = plugin->create(region, tankItem);
+ region->setInitialState(pluginState);
+
+ // If the plugin has an error it is disabled
+ QState *errorState = new QState(region);
+ errorState->assignProperty(tankItem, "enabled", false);
+ pluginState->setErrorState(errorState);
}
}
diff --git a/examples/statemachine/errorstate/plugin.h b/examples/statemachine/errorstate/plugin.h
index 1ac7e0f4e9..69b9574e20 100644
--- a/examples/statemachine/errorstate/plugin.h
+++ b/examples/statemachine/errorstate/plugin.h
@@ -1,6 +1,8 @@
#ifndef PLUGIN_H
#define PLUGIN_H
+#include <QtPlugin>
+
class QState;
class Tank;
class Plugin
diff --git a/examples/statemachine/errorstate/rocketitem.cpp b/examples/statemachine/errorstate/rocketitem.cpp
index de9eef7d1f..85d436bc24 100644
--- a/examples/statemachine/errorstate/rocketitem.cpp
+++ b/examples/statemachine/errorstate/rocketitem.cpp
@@ -1,4 +1,5 @@
#include "rocketitem.h"
+#include "tankitem.h"
#include <QPainter>
#include <QGraphicsScene>
@@ -25,10 +26,6 @@ void RocketItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWid
painter->drawEllipse(boundingRect());
}
-void RocketItem::hitByRocket()
-{
-}
-
void RocketItem::idle(qreal elapsed)
{
qreal dist = elapsed * speed();
@@ -51,8 +48,8 @@ void RocketItem::idle(qreal elapsed)
if (requestedPosition == nextPosition) {
setPos(nextPosition);
} else {
- if (GameItem *gameItem = qgraphicsitem_cast<GameItem *>(collidedItem))
- gameItem->hitByRocket();
+ if (TankItem *tankItem = qgraphicsitem_cast<TankItem *>(collidedItem))
+ tankItem->hitByRocket();
scene()->removeItem(this);
delete this;
diff --git a/examples/statemachine/errorstate/rocketitem.h b/examples/statemachine/errorstate/rocketitem.h
index 9805a8a2fd..b055b0aa7b 100644
--- a/examples/statemachine/errorstate/rocketitem.h
+++ b/examples/statemachine/errorstate/rocketitem.h
@@ -6,13 +6,14 @@
class RocketItem: public GameItem
{
public:
+ enum { Type = UserType + 2 };
+
RocketItem();
+ int type() const { return Type; }
virtual void idle(qreal elapsed);
qreal speed() const { return 100.0; }
void setDirection(qreal direction) { m_direction = direction; }
-
- void hitByRocket();
protected:
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
diff --git a/examples/statemachine/errorstate/tankitem.cpp b/examples/statemachine/errorstate/tankitem.cpp
index 814de2b4a7..705623cc34 100644
--- a/examples/statemachine/errorstate/tankitem.cpp
+++ b/examples/statemachine/errorstate/tankitem.cpp
@@ -85,17 +85,25 @@ private:
bool m_reverse;
};
-TankItem::TankItem(QObject *parent) : Tank(parent), m_currentAction(0), m_currentDirection(0.0)
+TankItem::TankItem(QObject *parent)
+ : Tank(parent), m_currentAction(0), m_currentDirection(0.0), m_enabled(true)
{
connect(this, SIGNAL(fireCannon()), this, SIGNAL(actionCompleted()));
}
void TankItem::idle(qreal elapsed)
{
- if (m_currentAction != 0) {
- if (!m_currentAction->apply(elapsed)) {
- setAction(0);
- emit actionCompleted();
+ if (m_enabled) {
+ if (m_currentAction != 0) {
+ if (!m_currentAction->apply(elapsed)) {
+ setAction(0);
+ emit actionCompleted();
+ }
+
+ QGraphicsItem *item = 0;
+ qreal distance = distanceToObstacle(&item);
+ if (TankItem *tankItem = qgraphicsitem_cast<TankItem *>(item))
+ emit tankSpotted(tankItem->direction(), distance);
}
}
}
@@ -176,6 +184,17 @@ void TankItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidge
QPointF(brect.right() - 2.0, brect.bottom()));
painter->fillRect(rightTrackRect, Qt::darkYellow);
painter->drawRect(rightTrackRect);
+
+ if (!m_enabled) {
+ painter->setPen(QPen(Qt::red, 5));
+
+ painter->drawEllipse(brect);
+
+ QPainterPath path;
+ path.addEllipse(brect);
+ painter->setClipPath(path);
+ painter->drawLine(brect.topRight(), brect.bottomLeft());
+ }
}
QRectF TankItem::boundingRect() const
@@ -195,11 +214,32 @@ void TankItem::setDirection(qreal newDirection)
rotate(diff);
}
-qreal TankItem::distanceToObstacle() const
+qreal TankItem::distanceToObstacle(QGraphicsItem **obstacle) const
{
- // ###
+ qreal dist = sqrt(pow(scene()->sceneRect().width(), 2) + pow(scene()->sceneRect().height(), 2));
+
+ qreal a = m_currentDirection * M_PI / 180.0;
+
+ qreal yd = dist * sin(a);
+ qreal xd = dist * sin(M_PI / 2.0 - a);
- return 0.0;
+ QPointF requestedPosition = pos() + QPointF(xd, yd);
+ QGraphicsItem *collidedItem = 0;
+ QPointF nextPosition = tryMove(requestedPosition, 0, &collidedItem);
+ if (collidedItem != 0) {
+ if (obstacle != 0)
+ *obstacle = collidedItem;
+
+ QPointF d = nextPosition - pos();
+ return sqrt(pow(d.x(), 2) + pow(d.y(), 2));
+ } else {
+ return 0.0;
+ }
+}
+
+qreal TankItem::distanceToObstacle() const
+{
+ return distanceToObstacle(0);
}
diff --git a/examples/statemachine/errorstate/tankitem.h b/examples/statemachine/errorstate/tankitem.h
index c9e0d223be..e598cfdbca 100644
--- a/examples/statemachine/errorstate/tankitem.h
+++ b/examples/statemachine/errorstate/tankitem.h
@@ -10,8 +10,13 @@ class Action;
class TankItem: public Tank, public GameItem
{
Q_OBJECT
+ Q_PROPERTY(bool enabled READ enabled WRITE setEnabled)
public:
+ enum { Type = UserType + 1 };
+
TankItem(QObject *parent = 0);
+
+ int type() const { return Type; }
virtual void moveForwards(qreal length);
virtual void moveBackwards(qreal length);
@@ -19,6 +24,7 @@ public:
virtual void stop();
virtual qreal direction() const;
virtual qreal distanceToObstacle() const;
+ virtual qreal distanceToObstacle(QGraphicsItem **item) const;
void setColor(const QColor &color) { m_color = color; }
QColor color() const { return m_color; }
@@ -33,6 +39,9 @@ public:
void hitByRocket();
+ void setEnabled(bool b) { m_enabled = b; }
+ bool enabled() const { return m_enabled; }
+
signals:
virtual void fireCannon();
@@ -46,6 +55,7 @@ private:
Action *m_currentAction;
qreal m_currentDirection;
QColor m_color;
+ bool m_enabled;
};
#endif
diff --git a/examples/statemachine/errorstateplugins/errorstateplugins.pro b/examples/statemachine/errorstateplugins/errorstateplugins.pro
new file mode 100644
index 0000000000..e7718a98f6
--- /dev/null
+++ b/examples/statemachine/errorstateplugins/errorstateplugins.pro
@@ -0,0 +1,10 @@
+TEMPLATE = subdirs
+SUBDIRS = random_ai \
+ spin_ai_with_error \
+ spin_ai
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/statemachine/errorstateplugins
+sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS errorstateplugins.pro
+sources.path = $$[QT_INSTALL_EXAMPLES]/statemachine/errorstateplugins
+INSTALLS += target sources
diff --git a/examples/statemachine/errorstateplugins/spin_ai/spin_ai.cpp b/examples/statemachine/errorstateplugins/spin_ai/spin_ai.cpp
new file mode 100644
index 0000000000..8452523061
--- /dev/null
+++ b/examples/statemachine/errorstateplugins/spin_ai/spin_ai.cpp
@@ -0,0 +1,29 @@
+#include "spin_ai.h"
+
+#include <QtPlugin>
+
+QState *SpinAi::create(QState *parentState, Tank *tank)
+{
+ QState *topLevel = new QState(parentState);
+ QState *spinState = new SpinState(tank, topLevel);
+ topLevel->setInitialState(spinState);
+
+ // When tank is spotted, fire two times and go back to spin state
+ QState *fireState = new QState(topLevel);
+
+ QState *fireOnce = new QState(fireState);
+ fireState->setInitialState(fireOnce);
+ connect(fireOnce, SIGNAL(entered()), tank, SLOT(fireCannon()));
+
+ QState *fireTwice = new QState(fireState);
+ connect(fireTwice, SIGNAL(entered()), tank, SLOT(fireCannon()));
+
+ fireOnce->addTransition(tank, SIGNAL(actionCompleted()), fireTwice);
+ fireTwice->addTransition(tank, SIGNAL(actionCompleted()), spinState);
+
+ spinState->addTransition(tank, SIGNAL(tankSpotted(qreal,qreal)), fireState);
+
+ return topLevel;
+}
+
+Q_EXPORT_PLUGIN2(spin_ai, SpinAi)
diff --git a/examples/statemachine/errorstateplugins/spin_ai/spin_ai.h b/examples/statemachine/errorstateplugins/spin_ai/spin_ai.h
new file mode 100644
index 0000000000..7336640268
--- /dev/null
+++ b/examples/statemachine/errorstateplugins/spin_ai/spin_ai.h
@@ -0,0 +1,44 @@
+#ifndef SPIN_AI_H
+#define SPIN_AI_H
+
+#include <errorstate/plugin.h>
+#include <errorstate/tank.h>
+
+#include <QObject>
+#include <QState>
+
+class SpinState: public QState
+{
+ Q_OBJECT
+public:
+ SpinState(Tank *tank, QState *parent) : QState(parent), m_tank(tank)
+ {
+ }
+
+public slots:
+ void spin()
+ {
+ m_tank->turn(90.0);
+ }
+
+protected:
+ void onEntry()
+ {
+ connect(m_tank, SIGNAL(actionCompleted()), this, SLOT(spin()));
+ spin();
+ }
+
+private:
+ Tank *m_tank;
+
+};
+
+class SpinAi: public QObject, public Plugin
+{
+ Q_OBJECT
+ Q_INTERFACES(Plugin)
+public:
+ virtual QState *create(QState *parentState, Tank *tank);
+};
+
+#endif
diff --git a/examples/statemachine/errorstateplugins/spin_ai/spin_ai.pro b/examples/statemachine/errorstateplugins/spin_ai/spin_ai.pro
new file mode 100644
index 0000000000..c2fd937956
--- /dev/null
+++ b/examples/statemachine/errorstateplugins/spin_ai/spin_ai.pro
@@ -0,0 +1,13 @@
+TEMPLATE = lib
+CONFIG += plugin
+INCLUDEPATH += ../..
+HEADERS = spin_ai.h
+SOURCES = spin_ai.cpp
+TARGET = $$qtLibraryTarget(spin_ai)
+DESTDIR = ../../errorstate/plugins
+
+#! [0]
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/statemachine/errorstate/plugins
+sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS spin_ai.pro
+sources.path = $$[QT_INSTALL_EXAMPLES]/statemachine/errorstateplugins/spin_ai \ No newline at end of file
diff --git a/examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.cpp b/examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.cpp
new file mode 100644
index 0000000000..dd73270a20
--- /dev/null
+++ b/examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.cpp
@@ -0,0 +1,29 @@
+#include "spin_ai_with_error.h"
+
+#include <QtPlugin>
+
+QState *SpinAiWithError::create(QState *parentState, Tank *tank)
+{
+ QState *topLevel = new QState(parentState);
+ QState *spinState = new SpinState(tank, topLevel);
+ topLevel->setInitialState(spinState);
+
+ // When tank is spotted, fire two times and go back to spin state
+ // (no initial state set for fireState will lead to run-time error in machine)
+ QState *fireState = new QState(topLevel);
+
+ QState *fireOnce = new QState(fireState);
+ connect(fireOnce, SIGNAL(entered()), tank, SLOT(fireCannon()));
+
+ QState *fireTwice = new QState(fireState);
+ connect(fireTwice, SIGNAL(entered()), tank, SLOT(fireCannon()));
+
+ fireOnce->addTransition(tank, SIGNAL(actionCompleted()), fireTwice);
+ fireTwice->addTransition(tank, SIGNAL(actionCompleted()), spinState);
+
+ spinState->addTransition(tank, SIGNAL(tankSpotted(qreal,qreal)), fireState);
+
+ return topLevel;
+}
+
+Q_EXPORT_PLUGIN2(spin_ai_with_error, SpinAiWithError)
diff --git a/examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.h b/examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.h
new file mode 100644
index 0000000000..26def6670d
--- /dev/null
+++ b/examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.h
@@ -0,0 +1,44 @@
+#ifndef SPIN_AI_WITH_ERROR_H
+#define SPIN_AI_WITH_ERROR_H
+
+#include <errorstate/plugin.h>
+#include <errorstate/tank.h>
+
+#include <QObject>
+#include <QState>
+
+class SpinState: public QState
+{
+ Q_OBJECT
+public:
+ SpinState(Tank *tank, QState *parent) : QState(parent), m_tank(tank)
+ {
+ }
+
+public slots:
+ void spin()
+ {
+ m_tank->turn(90.0);
+ }
+
+protected:
+ void onEntry()
+ {
+ connect(m_tank, SIGNAL(actionCompleted()), this, SLOT(spin()));
+ spin();
+ }
+
+private:
+ Tank *m_tank;
+
+};
+
+class SpinAiWithError: public QObject, public Plugin
+{
+ Q_OBJECT
+ Q_INTERFACES(Plugin)
+public:
+ virtual QState *create(QState *parentState, Tank *tank);
+};
+
+#endif
diff --git a/examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.pro b/examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.pro
new file mode 100644
index 0000000000..31f4c7fac0
--- /dev/null
+++ b/examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.pro
@@ -0,0 +1,13 @@
+TEMPLATE = lib
+CONFIG += plugin
+INCLUDEPATH += ../..
+HEADERS = spin_ai_with_error.h
+SOURCES = spin_ai_with_error.cpp
+TARGET = $$qtLibraryTarget(spin_ai_with_error)
+DESTDIR = ../../errorstate/plugins
+
+#! [0]
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/statemachine/errorstate/plugins
+sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS spin_ai_with_error.pro
+sources.path = $$[QT_INSTALL_EXAMPLES]/statemachine/errorstateplugins/spin_ai_with_error \ No newline at end of file