From 0f8de3b0b4ccccbeba97999635eb5716f561fb30 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Thu, 30 Apr 2009 09:13:44 +0200 Subject: 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. --- examples/statemachine/errorstate/gameitem.h | 5 -- examples/statemachine/errorstate/mainwindow.cpp | 19 ++++++-- examples/statemachine/errorstate/plugin.h | 2 + examples/statemachine/errorstate/rocketitem.cpp | 9 ++-- examples/statemachine/errorstate/rocketitem.h | 5 +- examples/statemachine/errorstate/tankitem.cpp | 56 ++++++++++++++++++---- examples/statemachine/errorstate/tankitem.h | 10 ++++ .../errorstateplugins/errorstateplugins.pro | 10 ++++ .../errorstateplugins/spin_ai/spin_ai.cpp | 29 +++++++++++ .../errorstateplugins/spin_ai/spin_ai.h | 44 +++++++++++++++++ .../errorstateplugins/spin_ai/spin_ai.pro | 13 +++++ .../spin_ai_with_error/spin_ai_with_error.cpp | 29 +++++++++++ .../spin_ai_with_error/spin_ai_with_error.h | 44 +++++++++++++++++ .../spin_ai_with_error/spin_ai_with_error.pro | 13 +++++ 14 files changed, 262 insertions(+), 26 deletions(-) create mode 100644 examples/statemachine/errorstateplugins/errorstateplugins.pro create mode 100644 examples/statemachine/errorstateplugins/spin_ai/spin_ai.cpp create mode 100644 examples/statemachine/errorstateplugins/spin_ai/spin_ai.h create mode 100644 examples/statemachine/errorstateplugins/spin_ai/spin_ai.pro create mode 100644 examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.cpp create mode 100644 examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.h create mode 100644 examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.pro (limited to 'examples/statemachine') 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 items = m_scene->items(); foreach (QGraphicsItem *item, items) { - GameItem *gameItem = qgraphicsitem_cast(item); + // ### + GameItem *gameItem = qgraphicsitem_cast(item); + if (gameItem == 0) + gameItem = qgraphicsitem_cast(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 + 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 #include @@ -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(collidedItem)) - gameItem->hitByRocket(); + if (TankItem *tankItem = qgraphicsitem_cast(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(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 + +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 +#include + +#include +#include + +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 + +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 +#include + +#include +#include + +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 -- cgit v1.2.3