diff options
-rw-r--r-- | doc/src/examples/stickman.qdoc | 115 | ||||
-rw-r--r-- | doc/src/images/stickman-example.png | bin | 0 -> 18867 bytes | |||
-rw-r--r-- | doc/src/images/stickman-example1.png | bin | 0 -> 8311 bytes | |||
-rw-r--r-- | examples/animation/stickman/lifecycle.cpp | 48 | ||||
-rw-r--r-- | examples/animation/stickman/lifecycle.h | 2 |
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 Binary files differnew file mode 100644 index 0000000..a40f37b --- /dev/null +++ b/doc/src/images/stickman-example.png diff --git a/doc/src/images/stickman-example1.png b/doc/src/images/stickman-example1.png Binary files differnew file mode 100644 index 0000000..7b9a5b8 --- /dev/null +++ b/doc/src/images/stickman-example1.png 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; |