summaryrefslogtreecommitdiffstats
path: root/examples/statemachine/tankgameplugins
diff options
context:
space:
mode:
Diffstat (limited to 'examples/statemachine/tankgameplugins')
-rw-r--r--examples/statemachine/tankgameplugins/random_ai/random_ai.pro13
-rw-r--r--examples/statemachine/tankgameplugins/random_ai/random_ai_plugin.cpp38
-rw-r--r--examples/statemachine/tankgameplugins/random_ai/random_ai_plugin.h64
-rw-r--r--examples/statemachine/tankgameplugins/seek_ai/seek_ai.cpp48
-rw-r--r--examples/statemachine/tankgameplugins/seek_ai/seek_ai.h204
-rw-r--r--examples/statemachine/tankgameplugins/seek_ai/seek_ai.pro13
-rw-r--r--examples/statemachine/tankgameplugins/spin_ai/spin_ai.cpp29
-rw-r--r--examples/statemachine/tankgameplugins/spin_ai/spin_ai.h46
-rw-r--r--examples/statemachine/tankgameplugins/spin_ai/spin_ai.pro13
-rw-r--r--examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.cpp29
-rw-r--r--examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.h46
-rw-r--r--examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.pro13
-rw-r--r--examples/statemachine/tankgameplugins/tankgameplugins.pro11
13 files changed, 567 insertions, 0 deletions
diff --git a/examples/statemachine/tankgameplugins/random_ai/random_ai.pro b/examples/statemachine/tankgameplugins/random_ai/random_ai.pro
new file mode 100644
index 0000000..5bc0b26
--- /dev/null
+++ b/examples/statemachine/tankgameplugins/random_ai/random_ai.pro
@@ -0,0 +1,13 @@
+TEMPLATE = lib
+CONFIG += plugin
+INCLUDEPATH += ../..
+HEADERS = random_ai_plugin.h
+SOURCES = random_ai_plugin.cpp
+TARGET = $$qtLibraryTarget(random_ai)
+DESTDIR = ../../tankgame/plugins
+
+#! [0]
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/statemachine/tankgame/plugins
+sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS random_ai.pro
+sources.path = $$[QT_INSTALL_EXAMPLES]/statemachine/tankgameplugins/random_ai \ No newline at end of file
diff --git a/examples/statemachine/tankgameplugins/random_ai/random_ai_plugin.cpp b/examples/statemachine/tankgameplugins/random_ai/random_ai_plugin.cpp
new file mode 100644
index 0000000..c196247
--- /dev/null
+++ b/examples/statemachine/tankgameplugins/random_ai/random_ai_plugin.cpp
@@ -0,0 +1,38 @@
+#include "random_ai_plugin.h"
+
+#include <QState>
+#include <QtPlugin>
+
+#include <time.h>
+
+QState *RandomAiPlugin::create(QState *parentState, QObject *tank)
+{
+ qsrand(uint(time(NULL)));
+
+ QState *topLevel = new QState(parentState);
+
+ QState *selectNextActionState = new SelectActionState(topLevel);
+ topLevel->setInitialState(selectNextActionState);
+
+ QState *fireState = new RandomDistanceState(topLevel);
+ connect(fireState, SIGNAL(distanceComputed(qreal)), tank, SLOT(fireCannon()));
+ selectNextActionState->addTransition(selectNextActionState, SIGNAL(fireSelected()), fireState);
+
+ QState *moveForwardsState = new RandomDistanceState(topLevel);
+ connect(moveForwardsState, SIGNAL(distanceComputed(qreal)), tank, SLOT(moveForwards(qreal)));
+ selectNextActionState->addTransition(selectNextActionState, SIGNAL(moveForwardsSelected()), moveForwardsState);
+
+ QState *moveBackwardsState = new RandomDistanceState(topLevel);
+ connect(moveBackwardsState, SIGNAL(distanceComputed(qreal)), tank, SLOT(moveBackwards(qreal)));
+ selectNextActionState->addTransition(selectNextActionState, SIGNAL(moveBackwardsSelected()), moveBackwardsState);
+
+ QState *turnState = new RandomDistanceState(topLevel);
+ connect(turnState, SIGNAL(distanceComputed(qreal)), tank, SLOT(turn(qreal)));
+ selectNextActionState->addTransition(selectNextActionState, SIGNAL(turnSelected()), turnState);
+
+ topLevel->addTransition(tank, SIGNAL(actionCompleted()), selectNextActionState);
+
+ return topLevel;
+}
+
+Q_EXPORT_PLUGIN2(random_ai, RandomAiPlugin)
diff --git a/examples/statemachine/tankgameplugins/random_ai/random_ai_plugin.h b/examples/statemachine/tankgameplugins/random_ai/random_ai_plugin.h
new file mode 100644
index 0000000..f5e3b6f
--- /dev/null
+++ b/examples/statemachine/tankgameplugins/random_ai/random_ai_plugin.h
@@ -0,0 +1,64 @@
+#ifndef RANDOM_AI_PLUGIN_H
+#define RANDOM_AI_PLUGIN_H
+
+#include <QObject>
+#include <QState>
+
+#include <tankgame/plugin.h>
+
+class SelectActionState: public QState
+{
+ Q_OBJECT
+public:
+ SelectActionState(QState *parent = 0) : QState(parent)
+ {
+ }
+
+signals:
+ void fireSelected();
+ void moveForwardsSelected();
+ void moveBackwardsSelected();
+ void turnSelected();
+
+protected:
+ void onEntry(QEvent *)
+ {
+ int rand = qrand() % 4;
+ switch (rand) {
+ case 0: emit fireSelected(); break;
+ case 1: emit moveForwardsSelected(); break;
+ case 2: emit moveBackwardsSelected(); break;
+ case 3: emit turnSelected(); break;
+ };
+ }
+};
+
+class RandomDistanceState: public QState
+{
+ Q_OBJECT
+public:
+ RandomDistanceState(QState *parent = 0) : QState(parent)
+ {
+ }
+
+signals:
+ void distanceComputed(qreal distance);
+
+protected:
+ void onEntry(QEvent *)
+ {
+ emit distanceComputed(qreal(qrand() % 180));
+ }
+};
+
+class RandomAiPlugin: public QObject, public Plugin
+{
+ Q_OBJECT
+ Q_INTERFACES(Plugin)
+public:
+ RandomAiPlugin() { setObjectName("Random"); }
+
+ virtual QState *create(QState *parentState, QObject *tank);
+};
+
+#endif // RANDOM_AI_PLUGIN_H
diff --git a/examples/statemachine/tankgameplugins/seek_ai/seek_ai.cpp b/examples/statemachine/tankgameplugins/seek_ai/seek_ai.cpp
new file mode 100644
index 0000000..2fb05d4
--- /dev/null
+++ b/examples/statemachine/tankgameplugins/seek_ai/seek_ai.cpp
@@ -0,0 +1,48 @@
+#include "seek_ai.h"
+
+QState *SeekAi::create(QState *parentState, QObject *tank)
+{
+ QState *topLevel = new QState(parentState);
+ topLevel->setObjectName("topLevel");
+
+ QState *seek = new QState(topLevel);
+ seek->setObjectName("seek");
+ topLevel->setInitialState(seek);
+
+ QState *lookForNearestWall = new SearchState(tank, seek);
+ lookForNearestWall->setObjectName("lookForNearestWall");
+ seek->setInitialState(lookForNearestWall);
+
+ QState *driveToFirstObstacle = new QState(seek);
+ driveToFirstObstacle->setObjectName("driveToFirstObstacle");
+ lookForNearestWall->addTransition(lookForNearestWall, SIGNAL(nearestObstacleStraightAhead()),
+ driveToFirstObstacle);
+
+ QState *drive = new QState(driveToFirstObstacle);
+ drive->setObjectName("drive");
+ driveToFirstObstacle->setInitialState(drive);
+ connect(drive, SIGNAL(entered()), tank, SLOT(moveForwards()));
+ connect(drive, SIGNAL(exited()), tank, SLOT(stop()));
+
+ // Go in loop
+ QState *finishedDriving = new QState(driveToFirstObstacle);
+ finishedDriving->setObjectName("finishedDriving");
+ drive->addTransition(tank, SIGNAL(actionCompleted()), finishedDriving);
+ finishedDriving->addTransition(drive);
+
+ QState *turnTo = new QState(seek);
+ turnTo->setObjectName("turnTo");
+ driveToFirstObstacle->addTransition(new CollisionTransition(tank, turnTo));
+
+ turnTo->addTransition(tank, SIGNAL(actionCompleted()), driveToFirstObstacle);
+
+ ChaseState *chase = new ChaseState(tank, topLevel);
+ chase->setObjectName("chase");
+ seek->addTransition(new TankSpottedTransition(tank, chase));
+ chase->addTransition(chase, SIGNAL(finished()), driveToFirstObstacle);
+ chase->addTransition(new TankSpottedTransition(tank, chase));
+
+ return topLevel;
+}
+
+Q_EXPORT_PLUGIN2(seek_ai, SeekAi)
diff --git a/examples/statemachine/tankgameplugins/seek_ai/seek_ai.h b/examples/statemachine/tankgameplugins/seek_ai/seek_ai.h
new file mode 100644
index 0000000..2835988
--- /dev/null
+++ b/examples/statemachine/tankgameplugins/seek_ai/seek_ai.h
@@ -0,0 +1,204 @@
+#ifndef SEEK_AI_H
+#define SEEK_AI_H
+
+#include <tankgame/plugin.h>
+
+#include <QState>
+#include <QFinalState>
+#include <QSignalTransition>
+#include <QSignalEvent>
+#include <QVariant>
+#include <QLineF>
+#include <QDebug>
+
+class SearchState: public QState
+{
+ Q_OBJECT
+public:
+ SearchState(QObject *tank, QState *parentState = 0)
+ : QState(parentState),
+ m_tank(tank),
+ m_distanceToTurn(360.0),
+ m_nearestDistance(-1.0),
+ m_directionOfNearestObstacle(0.0)
+ {
+ }
+
+public slots:
+ void turnAlittle()
+ {
+ qreal dist = m_tank->property("distanceToObstacle").toDouble();
+
+ if (m_nearestDistance < 0.0 || dist < m_nearestDistance) {
+ m_nearestDistance = dist;
+ m_directionOfNearestObstacle = m_tank->property("direction").toDouble();
+ }
+
+ m_distanceToTurn -= 10.0;
+ if (m_distanceToTurn < 0.0) {
+ disconnect(m_tank, SIGNAL(actionCompleted()), this, SLOT(turnAlittle()));
+ connect(m_tank, SIGNAL(actionCompleted()), this, SIGNAL(nearestObstacleStraightAhead()));
+ m_tank->setProperty("direction", m_directionOfNearestObstacle);
+ }
+
+ qreal currentDirection = m_tank->property("direction").toDouble();
+ m_tank->setProperty("direction", currentDirection + 10.0);
+ }
+
+signals:
+ void nearestObstacleStraightAhead();
+
+protected:
+ void onEntry(QEvent *)
+ {
+ connect(m_tank, SIGNAL(actionCompleted()), this, SLOT(turnAlittle()));
+ turnAlittle();
+ }
+
+ void onExit(QEvent *)
+ {
+ disconnect(m_tank, SIGNAL(actionCompleted()), this, SLOT(turnAlittle()));
+ disconnect(m_tank, SIGNAL(actionCompleted()), this, SLOT(nearestObstacleStraightAhead()));
+ }
+
+private:
+ QObject *m_tank;
+
+ qreal m_distanceToTurn;
+ qreal m_nearestDistance;
+ qreal m_directionOfNearestObstacle;
+};
+
+class CollisionTransition: public QSignalTransition
+{
+public:
+ CollisionTransition(QObject *tank, QState *turnTo)
+ : QSignalTransition(tank, SIGNAL(collision(QLineF))),
+ m_tank(tank),
+ m_turnTo(turnTo)
+ {
+ setTargetState(turnTo);
+ }
+
+protected:
+ bool eventTest(QEvent *event) const
+ {
+ bool b = QSignalTransition::eventTest(event);
+ if (b) {
+ QSignalEvent *se = static_cast<QSignalEvent *>(event);
+ m_lastLine = se->arguments().at(0).toLineF();
+ }
+ return b;
+ }
+
+ void onTransition(QEvent *)
+ {
+ qreal angleOfWall = m_lastLine.angle();
+
+ qreal newDirection;
+ if (qrand() % 2 == 0)
+ newDirection = angleOfWall;
+ else
+ newDirection = angleOfWall - 180.0;
+
+ m_turnTo->assignProperty(m_tank, "direction", newDirection);
+ }
+
+private:
+ mutable QLineF m_lastLine;
+ QObject *m_tank;
+ QState *m_turnTo;
+};
+
+class ChaseState: public QState
+{
+ class GoToLocationState: public QState
+ {
+ public:
+ GoToLocationState(QObject *tank, QState *parentState = 0)
+ : QState(parentState), m_tank(tank), m_distance(0.0)
+ {
+ }
+
+ void setDistance(qreal distance) { m_distance = distance; }
+
+ protected:
+ void onEntry()
+ {
+ QMetaObject::invokeMethod(m_tank, "moveForwards", Q_ARG(qreal, m_distance));
+ }
+
+ private:
+ QObject *m_tank;
+ qreal m_distance;
+ };
+
+public:
+ ChaseState(QObject *tank, QState *parentState = 0) : QState(parentState), m_tank(tank)
+ {
+ QState *fireCannon = new QState(this);
+ connect(fireCannon, SIGNAL(entered()), tank, SLOT(fireCannon()));
+ setInitialState(fireCannon);
+
+ m_goToLocation = new GoToLocationState(this);
+ fireCannon->addTransition(tank, SIGNAL(actionCompleted()), m_goToLocation);
+
+ m_turnToDirection = new QState(this);
+ m_goToLocation->addTransition(tank, SIGNAL(actionCompleted()), m_turnToDirection);
+
+ QFinalState *finalState = new QFinalState(this);
+ m_turnToDirection->addTransition(tank, SIGNAL(actionCompleted()), finalState);
+ }
+
+ void setDirection(qreal direction)
+ {
+ m_turnToDirection->assignProperty(m_tank, "direction", direction);
+ }
+
+ void setDistance(qreal distance)
+ {
+ m_goToLocation->setDistance(distance);
+ }
+
+private:
+ QObject *m_tank;
+ GoToLocationState *m_goToLocation;
+ QState *m_turnToDirection;
+
+};
+
+class TankSpottedTransition: public QSignalTransition
+{
+public:
+ TankSpottedTransition(QObject *tank, ChaseState *target) : QSignalTransition(tank, SIGNAL(tankSpotted(qreal,qreal))), m_chase(target)
+ {
+ setTargetState(target);
+ }
+
+protected:
+ bool eventTest(QEvent *event) const
+ {
+ bool b = QSignalTransition::eventTest(event);
+ if (b) {
+ QSignalEvent *se = static_cast<QSignalEvent *>(event);
+ m_chase->setDirection(se->arguments().at(0).toDouble());
+ m_chase->setDistance(se->arguments().at(1).toDouble());
+ }
+ return b;
+ }
+
+private:
+ ChaseState *m_chase;
+};
+
+class SeekAi: public QObject, public Plugin
+{
+ Q_OBJECT
+ Q_INTERFACES(Plugin)
+public:
+ SeekAi() { setObjectName("Seek and destroy"); }
+
+ virtual QState *create(QState *parentState, QObject *tank);
+};
+
+#endif
diff --git a/examples/statemachine/tankgameplugins/seek_ai/seek_ai.pro b/examples/statemachine/tankgameplugins/seek_ai/seek_ai.pro
new file mode 100644
index 0000000..0d8bf2e
--- /dev/null
+++ b/examples/statemachine/tankgameplugins/seek_ai/seek_ai.pro
@@ -0,0 +1,13 @@
+TEMPLATE = lib
+CONFIG += plugin
+INCLUDEPATH += ../..
+HEADERS = seek_ai.h
+SOURCES = seek_ai.cpp
+TARGET = $$qtLibraryTarget(seek_ai)
+DESTDIR = ../../tankgame/plugins
+
+#! [0]
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/statemachine/tankgame/plugins
+sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS seek_ai.pro
+sources.path = $$[QT_INSTALL_EXAMPLES]/statemachine/tankgameplugins/seek_ai \ No newline at end of file
diff --git a/examples/statemachine/tankgameplugins/spin_ai/spin_ai.cpp b/examples/statemachine/tankgameplugins/spin_ai/spin_ai.cpp
new file mode 100644
index 0000000..de95f41
--- /dev/null
+++ b/examples/statemachine/tankgameplugins/spin_ai/spin_ai.cpp
@@ -0,0 +1,29 @@
+#include "spin_ai.h"
+
+#include <QtPlugin>
+
+QState *SpinAi::create(QState *parentState, QObject *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/tankgameplugins/spin_ai/spin_ai.h b/examples/statemachine/tankgameplugins/spin_ai/spin_ai.h
new file mode 100644
index 0000000..a331a3e
--- /dev/null
+++ b/examples/statemachine/tankgameplugins/spin_ai/spin_ai.h
@@ -0,0 +1,46 @@
+#ifndef SPIN_AI_H
+#define SPIN_AI_H
+
+#include <tankgame/plugin.h>
+
+#include <QObject>
+#include <QState>
+#include <QVariant>
+
+class SpinState: public QState
+{
+ Q_OBJECT
+public:
+ SpinState(QObject *tank, QState *parent) : QState(parent), m_tank(tank)
+ {
+ }
+
+public slots:
+ void spin()
+ {
+ m_tank->setProperty("direction", 90.0);
+ }
+
+protected:
+ void onEntry(QEvent *)
+ {
+ connect(m_tank, SIGNAL(actionCompleted()), this, SLOT(spin()));
+ spin();
+ }
+
+private:
+ QObject *m_tank;
+
+};
+
+class SpinAi: public QObject, public Plugin
+{
+ Q_OBJECT
+ Q_INTERFACES(Plugin)
+public:
+ SpinAi() { setObjectName("Spin and destroy"); }
+
+ virtual QState *create(QState *parentState, QObject *tank);
+};
+
+#endif
diff --git a/examples/statemachine/tankgameplugins/spin_ai/spin_ai.pro b/examples/statemachine/tankgameplugins/spin_ai/spin_ai.pro
new file mode 100644
index 0000000..8ab4da0
--- /dev/null
+++ b/examples/statemachine/tankgameplugins/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 = ../../tankgame/plugins
+
+#! [0]
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/statemachine/tankgame/plugins
+sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS spin_ai.pro
+sources.path = $$[QT_INSTALL_EXAMPLES]/statemachine/tankgameplugins/spin_ai \ No newline at end of file
diff --git a/examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.cpp b/examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.cpp
new file mode 100644
index 0000000..5499ba3
--- /dev/null
+++ b/examples/statemachine/tankgameplugins/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, QObject *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/tankgameplugins/spin_ai_with_error/spin_ai_with_error.h b/examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.h
new file mode 100644
index 0000000..5cfb364
--- /dev/null
+++ b/examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.h
@@ -0,0 +1,46 @@
+#ifndef SPIN_AI_WITH_ERROR_H
+#define SPIN_AI_WITH_ERROR_H
+
+#include <tankgame/plugin.h>
+
+#include <QObject>
+#include <QState>
+#include <QVariant>
+
+class SpinState: public QState
+{
+ Q_OBJECT
+public:
+ SpinState(QObject *tank, QState *parent) : QState(parent), m_tank(tank)
+ {
+ }
+
+public slots:
+ void spin()
+ {
+ m_tank->setProperty("direction", 90.0);
+ }
+
+protected:
+ void onEntry(QEvent *)
+ {
+ connect(m_tank, SIGNAL(actionCompleted()), this, SLOT(spin()));
+ spin();
+ }
+
+private:
+ QObject *m_tank;
+
+};
+
+class SpinAiWithError: public QObject, public Plugin
+{
+ Q_OBJECT
+ Q_INTERFACES(Plugin)
+public:
+ SpinAiWithError() { setObjectName("Spin and destroy with runtime error in state machine"); }
+
+ virtual QState *create(QState *parentState, QObject *tank);
+};
+
+#endif
diff --git a/examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.pro b/examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.pro
new file mode 100644
index 0000000..124cf98
--- /dev/null
+++ b/examples/statemachine/tankgameplugins/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 = ../../tankgame/plugins
+
+#! [0]
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/statemachine/tankgame/plugins
+sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS spin_ai_with_error.pro
+sources.path = $$[QT_INSTALL_EXAMPLES]/statemachine/tankgameplugins/spin_ai_with_error \ No newline at end of file
diff --git a/examples/statemachine/tankgameplugins/tankgameplugins.pro b/examples/statemachine/tankgameplugins/tankgameplugins.pro
new file mode 100644
index 0000000..a098e03
--- /dev/null
+++ b/examples/statemachine/tankgameplugins/tankgameplugins.pro
@@ -0,0 +1,11 @@
+TEMPLATE = subdirs
+SUBDIRS = random_ai \
+ spin_ai_with_error \
+ spin_ai \
+ seek_ai
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/statemachine/tankgameplugins
+sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS tankgameplugins.pro
+sources.path = $$[QT_INSTALL_EXAMPLES]/statemachine/tankgameplugins
+INSTALLS += target sources