summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/src/examples/stickman.qdoc115
-rw-r--r--doc/src/images/stickman-example.pngbin0 -> 18867 bytes
-rw-r--r--doc/src/images/stickman-example1.pngbin0 -> 8311 bytes
-rw-r--r--examples/animation/stickman/lifecycle.cpp48
-rw-r--r--examples/animation/stickman/lifecycle.h2
5 files changed, 137 insertions, 28 deletions
diff --git a/doc/src/examples/stickman.qdoc b/doc/src/examples/stickman.qdoc
new file mode 100644
index 0000000..0313c81
--- /dev/null
+++ b/doc/src/examples/stickman.qdoc
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \example animation/stickman
+ \title Stickman Example
+
+ The Stickman example shows how to animate transitions in a state machine to implement key frame
+ animations.
+
+ \image stickman-example.png
+
+ In this example, we will write a small application which animates the joints in a skeleton and
+ projects a stickman figure on top. The stickman can be either "alive" or "dead", and when in the
+ "alive" state, he can be performing different actions defined by key frame animations.
+
+ Animations are implemented as composite states. Each child state of the animation state
+ represents a frame in the animation by setting the position of each joint in the stickman's
+ skeleton to the positions defined for the particular frame. The frames are then bound together
+ with animated transitions that trigger on the source state's polished() signal. Thus, the
+ machine will enter the state representing the next state in the animation immediately after it
+ has finished animating into the previous frame.
+
+ \image stickman-example1.png
+
+ The states for an animation is constructed by reading a custom animation file format and
+ creating states that assign values to the the "position" properties of each of the nodes in the
+ skeleton graph.
+
+ \snippet examples/animation/stickman/lifecycle.cpp 1
+
+ The states are then bound together with signal transitions that listen to the polished() signal.
+
+ \snippet examples/animation/stickman/lifecycle.cpp 2
+
+ The last frame state is given a transition to the first one, so that the animation will loop
+ until it is interrupted when a transition out from the animation state is taken. To get smooth
+ animations between the different key frames, we set a default animation on the state machine.
+ This is a parallel animation group which contain animations for all the "position" properties
+ and will be selected by default when taking any transition that leads into a state that assigns
+ values to these properties.
+
+ \snippet examples/animation/stickman/lifecycle.cpp 3
+
+ Several such animation states are constructed, and are placed together as children of a top
+ level "alive" state which represents the stickman life cycle. Transitions go from the parent
+ state to the child state to ensure that each of the child states inherit them.
+
+ \image stickman-example2.png
+
+ This saves us the effort of connect every state to every state with identical transitions. The
+ state machine makes sure that transitions between the key frame animations are also smooth by
+ applying the default animation when interrupting one and starting another.
+
+ Finally, there is a transition out from the "alive" state and into the "dead" state. This is
+ a custom transition type called LightningStrikesTransition which samples every second and
+ triggers at random (one out of fifty times on average.)
+
+ \snippet examples/animation/stickman/lifecycle.cpp 4
+
+ When it triggers, the machine will first enter a "lightningStrike" state which uses a timer to
+ pause for a brief period of time while the background color of the scene is white. This gives us
+ a flash effect when the lightning strikes.
+
+ \snippet examples/animation/stickman/lifecycle.cpp 5
+
+ We start and stop a QTimer object when entering and exiting the state. Then we transition into
+ the "dead" state when the timer times out.
+
+ \snippet examples/animation/stickman/lifecycle.cpp 0
+
+ When the machine is in the "dead" state, it will be unresponsive. This is because the "dead"
+ state has no transitions leading out.
+
+ \image stickman-example3.png
+
+*/
diff --git a/doc/src/images/stickman-example.png b/doc/src/images/stickman-example.png
new file mode 100644
index 0000000..a40f37b
--- /dev/null
+++ b/doc/src/images/stickman-example.png
Binary files differ
diff --git a/doc/src/images/stickman-example1.png b/doc/src/images/stickman-example1.png
new file mode 100644
index 0000000..7b9a5b8
--- /dev/null
+++ b/doc/src/images/stickman-example1.png
Binary files differ
diff --git a/examples/animation/stickman/lifecycle.cpp b/examples/animation/stickman/lifecycle.cpp
index 6b69a26..1feb31d 100644
--- a/examples/animation/stickman/lifecycle.cpp
+++ b/examples/animation/stickman/lifecycle.cpp
@@ -82,6 +82,7 @@ private:
Qt::Key m_key;
};
+//! [4]
class LightningStrikesTransition: public QEventTransition
{
public:
@@ -97,6 +98,7 @@ public:
return QEventTransition::eventTest(e) && ((qrand() % 50) == 0);
}
};
+//! [4]
LifeCycle::LifeCycle(StickMan *stickMan, GraphicsView *keyReceiver)
: m_stickMan(stickMan), m_keyReceiver(keyReceiver)
@@ -110,7 +112,10 @@ LifeCycle::LifeCycle(StickMan *stickMan, GraphicsView *keyReceiver)
}
// Set up intial state graph
+//! [3]
m_machine = new QStateMachine();
+ m_machine->addDefaultAnimation(m_animationGroup);
+//! [3]
m_alive = new QState(m_machine->rootState());
m_alive->setObjectName("alive");
@@ -122,11 +127,13 @@ LifeCycle::LifeCycle(StickMan *stickMan, GraphicsView *keyReceiver)
lightningBlink->assignProperty(m_stickMan, "fillColor", Qt::white);
lightningBlink->assignProperty(m_stickMan, "isDead", true);
+//! [5]
QTimer *timer = new QTimer(lightningBlink);
timer->setSingleShot(true);
timer->setInterval(100);
QObject::connect(lightningBlink, SIGNAL(entered()), timer, SLOT(start()));
QObject::connect(lightningBlink, SIGNAL(exited()), timer, SLOT(stop()));
+//! [5]
m_dead = new QState(m_machine->rootState());
m_dead->assignProperty(m_stickMan->scene(), "backgroundBrush", Qt::black);
@@ -141,9 +148,9 @@ LifeCycle::LifeCycle(StickMan *stickMan, GraphicsView *keyReceiver)
m_alive->setInitialState(m_idle);
// Lightning strikes at random
-//! [0]
m_alive->addTransition(new LightningStrikesTransition(lightningBlink));
- connectByAnimation(lightningBlink, m_dead, new QSignalTransition(timer, SIGNAL(timeout())));
+//! [0]
+ lightningBlink->addTransition(timer, SIGNAL(timeout()), m_dead);
//! [0]
m_machine->setInitialState(m_alive);
@@ -160,23 +167,10 @@ void LifeCycle::start()
m_machine->start();
}
-//! [1]
-void LifeCycle::connectByAnimation(QState *s1, QAbstractState *s2, QAbstractTransition *transition)
-{
- if (transition == 0) {
- transition = s1->addTransition(s2);
- } else {
- transition->setTargetState(s2);
- s1->addTransition(transition);
- }
- transition->addAnimation(m_animationGroup);
-}
-//! [1]
-
void LifeCycle::addActivity(const QString &fileName, Qt::Key key)
{
QState *state = makeState(m_alive, fileName);
- connectByAnimation(m_alive, state, new KeyPressTransition(m_keyReceiver, key));
+ m_alive->addTransition(new KeyPressTransition(m_keyReceiver, key, state));
}
QState *LifeCycle::makeState(QState *parentState, const QString &animationFileName)
@@ -193,26 +187,28 @@ QState *LifeCycle::makeState(QState *parentState, const QString &animationFileNa
const int frameCount = animation.totalFrames();
QState *previousState = 0;
for (int i=0; i<frameCount; ++i) {
- QState *frameState = new QState(topLevel);
- frameState->setObjectName(QString::fromLatin1("frame %0").arg(i));
-
animation.setCurrentFrame(i);
+
+//! [1]
+ QState *frameState = new QState(topLevel);
const int nodeCount = animation.nodeCount();
for (int j=0; j<nodeCount; ++j)
frameState->assignProperty(m_stickMan->node(j), "position", animation.nodePos(j));
+//! [1]
- if (previousState == 0) {
+ frameState->setObjectName(QString::fromLatin1("frame %0").arg(i));
+ if (previousState == 0)
topLevel->setInitialState(frameState);
- } else {
- connectByAnimation(previousState, frameState,
- new QSignalTransition(previousState, SIGNAL(polished())));
- }
+ else
+//! [2]
+ previousState->addTransition(previousState, SIGNAL(polished()), frameState);
+//! [2]
+
previousState = frameState;
}
// Loop
- connectByAnimation(previousState, topLevel->initialState(),
- new QSignalTransition(previousState, SIGNAL(polished())));
+ previousState->addTransition(previousState, SIGNAL(polished()), topLevel->initialState());
return topLevel;
diff --git a/examples/animation/stickman/lifecycle.h b/examples/animation/stickman/lifecycle.h
index e520402..8fd0fb2 100644
--- a/examples/animation/stickman/lifecycle.h
+++ b/examples/animation/stickman/lifecycle.h
@@ -63,8 +63,6 @@ public:
void start();
private:
- void connectByAnimation(QState *s1, QAbstractState *s2,
- QAbstractTransition *transition = 0);
QState *makeState(QState *parentState, const QString &animationFileName);
StickMan *m_stickMan;