diff options
Diffstat (limited to 'examples/scxml/pinball/doc/src/pinball.qdoc')
-rw-r--r-- | examples/scxml/pinball/doc/src/pinball.qdoc | 551 |
1 files changed, 551 insertions, 0 deletions
diff --git a/examples/scxml/pinball/doc/src/pinball.qdoc b/examples/scxml/pinball/doc/src/pinball.qdoc new file mode 100644 index 0000000..f67b66d --- /dev/null +++ b/examples/scxml/pinball/doc/src/pinball.qdoc @@ -0,0 +1,551 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example pinball + \title Qt SCXML: Pinball Example + \ingroup examples-qtscxml + \brief Demonstrates how the internal + logic of the application can be encapsulated in a scxml file. + + This example demonstrates a clear separation + between chosen user interface, which may be easily replaced, + and internal logic encapsulated in the scxml file, + which may stay the same for different user interface. + + \section1 Pinball Features + + \image pinball.png Screenshot of the Pinball example + + \e {Pinball Example} mimics a pinball game. + The targets on the pinball table are substituted here by GUI controls, + mainly by push buttons. Display elements, including current + score, highscore and targets' lights are substituted by labels. + Usually targets' lights state changes very often during game: + they get turned on or off, they blink slowly, fast or + with any intermediate speed indicating a game (or a certain target) + entered some temporary state. The state of each target light + is presented here as an enabled or a disabled label. + There is no real ball here, but one can easily imagine that + pressing a target's button can mimic hitting a real pinball + target with a ball. + + Our pinball contains the following features: + \list + \li Initially and when the game ends the pinball table + goes into \c offState. In that state all lights on the table + blink slowly (with 1s interval). + \li After pressing the \uicontrol START button the pinball table + goes into \c onState. All lights get turned off and the + pinball table is ready to be played. + \li Whenever the table is in \c onState and the inattentive player + presses the \uicontrol {BALL OUT} button, the game ends + and enters into the \c offState. If the player's score is + higher than the current highscore, the highscore is updated. + \li The goal is to collect the \uicontrol JACKPOT. In order to do that, + the player must hit all five \uicontrol CRAZY letters twice. + He has unlimited time for collecting it for the first time. + However, after he has collected all the letters for the first time, + he enters the \c hurryState and he must quickly collect + all the letters again within 5s. If the time passed and + the letters were not collected again, the player must + start collecting the letters from scratch. + \li Scores: + \list + \li 1.000 per letter hit when not in \c hurryState. + \li 10.000 per letter hit when in \c hurryState. + \li 100.000 bonus for all 5 letters when not in \c hurryState. + \li 1.000.000 bonus for all 5 letters when in \c hurryState (\uicontrol JACKPOT) + \endlist + \li When not in \c hurryState, the letters already hit should blink + with intermediate speed (500ms). Letters not hit yet should stay off. + \li When in \c hurryState, the letters already hit should + stay on. Letters not hit yet should blink fast (200ms). + In addition the \uicontrol HURRY light should blink with the same speed. + \li When jackpot gets collected the \uicontrol JACKPOT light should stay on. + \endlist + + \section1 SCXML Part: Internal Logic Description + + The pinball.scxml file describes the internal logic implemented for + pinball game. + + \quotefromfile pinball/pinball.scxml + \skipto scxml + \printuntil guiControl + \dots 12 + \skipto /^\ {8}<\// + \printuntil internalState + \dots 12 + \skipto /^\ {8}<\// + \printuntil scxml + + In this example we've chosen ecmascript data model + ("datamodel" attribute of <scxml> element). + This data model enables declaring variables with + initial values, which can be modified later. + We declare two variables in our model: "highscore" and "score", + with initial values of 0 (two <data> elements inside <datamodel> element). + We also define a root parallel state "global", + with two child states: \c guiControl and \c internalState, + which are also parallel. Since the top \c global state is + parallel, it means that whenever a \c global state is active + all of its direct children are also active. In fact, + the top level \c global is always active, meaning + all of its children are always active, too. + The \c global state in our example doesn't play any other role apart from + collecting its two children and making them both active at a time. + + \quotefromfile pinball/pinball.scxml + \skipto guiControl + \printuntil rLight + \dots 16 + \skipto /^\ {12}<\// + \printuntil aLight + \dots 16 + \skipto /^\ {12}<\// + \printuntil zLight + \dots 16 + \skipto /^\ {12}<\// + \printuntil yLight + \dots 16 + \skipto /^\ {12}<\// + \printuntil hurryLight + \dots 16 + \skipto /^\ {12}<\// + \printuntil jackpotLight + \dots 16 + \skipto /^\ {12}<\// + \printuntil gameOverLight + \dots 16 + \skipto /^\ {12}<\// + \printuntil /^\ {8}<\// + + The \c guiControl part is responsible for maintaining the current + state of every light control, which is visible on the pinball table. + We have eight different lights, five for letters: C, R, A, Z and Y, + and three for whole texts: \uicontrol HURRY!, \uicontrol JACKPOT + and \uicontrol {GAME OVER}. Every light + corresponds to its state, e.g. for light of letter \uicontrol C we have + a \c cLight state and so on. Every light state has two child states + indicating whether the light is on or off, e.g. \c cLight state + contains \c cLightOn and \c cLightOff states. The content + for other light states, which is omitted here, is analogous. + As mentioned before, \c guiControl state is always active, and since + it is of parallel type, all its direct children are always active too. + So, \c cLight state is always active, however, + only one of \c cLightOn or \c cLightOff it active at a time. + The same applies to other children of \c guiControl state. + In addition, we define transitions between on and off substates, e.g. + whenever the active state is \c cLightOn and someone posts \c turnOffC event, + we change the active substate of \c cLight into \c cLightOff, and vice versa: + whenever the active state is \c cLightOff and we receive \c turnOnC event, + we change the active substate of \c cLight into \c cLightOn. + + In case of our application, we use instances of QLabel class in C++ + to pretend real lights on the table. Whenever the light transitions + into "on" or "off" state we enable or disable the particular label + accordingly. The glue connection between the state machine and the GUI + part of the application will be shown in \l {cpp} {cpp code} later on. For now + it is enough to realize that changes to active states inside + the state machine will serve as external interface of the state machine, + so that other parts of the application (e.g. GUI part) can listen to. + + All of the mentioned events which switch the state of every light + will be generated by this state machine inside \c internalState + in reaction to running timers or to external triggers. + + \quotefromfile pinball/pinball.scxml + \skipto internalState + \printuntil logicalState + \dots 16 + \skipto /^\ {12}<\// + \printuntil workflow + \dots 16 + \skipto /^\ {12}<\// + \printuntil /^\ {8}<\// + + The \c internalState consists of two main parts: \c logicalState and \c workflow. + The \c logicalState holds the definitions for modes the game is able + to go into and logical states of collected targets, while \c workflow state + implements generator for light blinking and calculates most of new states + machine should go into depending on incoming events and on currently active states. + As mentioned already, \c internalState is always active, and since + it is of a parallel type, \c logicalState and \c workflow children are always active too. + + \quotefromfile pinball/pinball.scxml + \skipto logicalState + \printuntil letterState + \dots 20 + \skipto /^\ {16}<\// + \printuntil modeState + \dots 20 + \skipto /^\ {16}<\// + \printuntil /^\ {12}<\// + + The \c logicalState consist of two parts: \c letterState and \c modeState. + As previously mentioned, \c logicalState is always active, and since + it is of parallel type, \c letterState and \c modeState children are always active too. + Now let's look at the first part of it, the \c letterState: + + \quotefromfile pinball/pinball.scxml + \skipto letterState + \printuntil lettersState + \printuntil rLetter + \dots 28 + \skipto /^\ {24}<\// + \printuntil aLetter + \dots 28 + \skipto /^\ {24}<\// + \printuntil zLetter + \dots 28 + \skipto /^\ {24}<\// + \printuntil yLetter + \dots 28 + \skipto /^\ {24}<\// + \printuntil /^\ {16}<\// + + It contains one parallel \c lettersState. + The \c lettersState maintains the logical state of + buttons pretending targets which were pressed by the user. + Please notice the difference between lettersStates and lightStates + which were mentioned earlier. E.g. the letter state for \uicontrol C letter + holds the info if the target for \uicontrol C letter was hit or not, + while the light state for \uicontrol C letter holds the info if the + light for the target for \uicontrol C letter should be currently on or off. + In the real pinball game these states are usually orthogonal, + e.g. if you didn't hit any target yet, the target is blinking, + indicating that it's currently worth to hit it. This blinking + means the light state switches between on or off in some short time interval, + while the target state is continouosly off, since it was not hit yet. + The author of a pinball table can decide e.g. that + after hitting the target (so after the target state goes to on) + he turns target's light continouosly off or on or just increase + of decrease the interval of blinking. + + As mentioned before, \c letterState is always active, so it means + that its only child \c lettersState should always be active too. However, + there is an exception here: for a short while the \c lettersState may + end up being \e {not active}. This happens when the transition for + \c lettersState is being performed. This transition is triggered when + the \c resetLetters event will appear and it instructs the state machine + to exit \c lettersState and all its descendant states and reenter + \c lettersState and set up all its descendant states with their initial states. + In short, the \c resetLetters event resets the \c lettersState and all its + descendant states to the default configuration. + + The \c lettersState contains five direct substates which + correspond to five different letters. The content + for other letters states than C, which is omitted here, is analogous. + + The \c cLetter contains two substates reflecting its off and on states: + \c cLetterOff and \c cLetterOn. The "cLetter" state inside its parallel + parent \c lettersState is always active (under condition that + \c lettersState is active, too, what was described before), however, + only one of its child states is active at a time: \c cLetterOff or \c cLetterOn. + The initial substate of \c cLetter state is \c cLetterOff meaning + that whenever \c cLetter state is being activated (what happens + initially and after \c resetLetters event) its active + substate will be set to \c cLetterOff. + + The \c cLetterOff defines a transition, which will be triggered by + the \c {cLetterTriggered} event. This transition activates the \c cLetterOn, + the other child of \c cLetter, only when the machine is in \c onState + (which will be defined later, but in short: when the pinball game is running). + The \c {cLetterTriggered} event is expected to be an event posted into the state machine + from outside of the state machine. This event should be generated when + the ball hits the \uicontrol C letter target. In our example we mimic + it by the pressing \uicontrol C letter button. + + The \c cLetterOn state defines additional action. The action is defined + inside \c onentry element of the state, which means it will be executed + when the state machine enters this state. The action will generate + the event \c letterOn, which is common for all other letter states + defined in \c lettersState. This event will be used in further part + for updating the current score. So, whenever any of letters will switch + its state from off to on (but not vice versa), the \c letterOn + event will be posted. + + The \c cLetterOn state is defined as a final state, which means that + whenever this state is activated the \c {done.state.cLetter} event + will be automatically posted by the state machine. Moreover, + when all \c lettersState children reach their final state, + the state machine will automatically post \c {done.state.lettersState} event. + Later, we will make use of it. + + \quotefromfile pinball/pinball.scxml + \skipto modeState + \printuntil offState + \dots 24 + \skipto /^\ {20}<\// + \printuntil onState + \dots 24 + \skipto /^\ {20}<\// + \printuntil /^\ {16}<\// + + The \c modeState consists of two substates, \c offState and \c onState. + The \c offState describes what should happen when the pinball game + is not yet started or when it is over, + while \c onState represents the logic appropriate for the active game. + + \quotefromfile pinball/pinball.scxml + \skipto offState + \printuntil /^\ {20}<\// + + Whenever the pinball application starts or when the game ends the machine goes into the + \c offState. Entering that state invokes some actions, which are + enclosed inside <onentry> element. First, we update the highScore + variable in case the current highScore value is less than current score value. + This is being checked inside the "cond" attribute of <if> element + (please note that we need to escape the "<" character with "<"). + When being in off state, we still want to show the last reached score, + so we don't clear it here - we will do that when we enter the on state. + Next, we raise two events: \c resetLetters for logical reset of + all letter which were possibly hit during last game and \c update + for immediate generation of light blink and update of all lights. + When the machine is in \c offState it is ready to go into the + \c onState if only the \c startTriggered event occurs, which is described + by <transition> element. This event is expected to be generated externally + after pressing the \uicontrol START button on the pinball table. + + \skipto onState + \printuntil /^\ {20}<\// + + The fist thing which is being done by the state machine after in enters + the \c onState is clearing the current score variable. The \c onState + is of parallel type and consists of two direct child states: + \c hurryState and \c jackpotState, which both are always active as long as + their parent "onState" is active too. Both \c hurryState and \c jackpotState + contain two substates which reflects the off and the on state of each of them. + Only one substate of \c hurryState and one substate of \c jackpotState + can be acitve at a time and initially the off substates are active. + + Whenever we enter \c hurryStateOff or \c hurryStateOn we generate two events: + \c resetLetters and \c update (the same as we generate when entering the \c onState). + In addition when we enter the \c hurryStateOn we send a delayed event + \c goToHurryOff with a delay of 5s, marked with \c hurryId. It means, that after 5s we just + switch the state back to \c hurryStateOff. In this way we implement + the five seconds hurry feature of the pinball table. + We also define transitions: \c hurryStateOff -> \c hurryStateOn + when \c goToHurryOn event occurs and the opposite, + \c hurryStateOn -> \c hurryStateOff when \c goToHurryOff event occurs. + When we exit the \c hurryStateOn we cancel possibly pending delayed + event which was marked with \c hurryId. This is important in case + the 5s time hasn't elapsed yet, but we've collected all the five letters + in the hurry state - we then collect the jackpot and want the pending + timer to finish. + + The substates of \c jackpotState generate the request for update the state + of lights. The \c jackpotStateOff defines the transition to \c jackpotStateOn + when the \c goForJackpot event occurs. The opposite transition isn't + needed, since when the jackpot gets collected the corresponding light + is left lit until the end of game. When the new game starts, the \c jackpotState + is entered again which causes that its initial active substate will be + \c jackpotStateOff. + + In addition, the \c onState defines one transition in reaction to + \c ballOutTriggered event which instructs the machine to go into the \c offState. + The \c ballOutTriggered event is expected to be an event posted into the state machine + from outside of the state machine. This event should be generated when + the ball gets out of playing area of the table. In our example we mimic + it by the pressing \uicontrol {BALL OUT} button. Posting the event from outside of state + machine will be shown in \l {cpp} {cpp code} later on. + + \quotefromfile pinball/pinball.scxml + \skipto workflow + \printuntil letterOn + \dots 20 + \skipto /^\ {16}<\// + \printuntil lettersState + \dots 20 + \skipto /^\ {16}<\// + \printuntil updateLights + \dots 20 + \skipto /^\ {16}<\// + \printuntil /^\ {12}<\// + + The \c workflow state is responsible for generator for light blinking, + which is defined in its \c lightImpulseGenerator substate, and + for reacting for events which were posted so far from other parts + of the state machine. + + The \c lightImpulseGenerator contains two child states: + \c lightImpulseOn and \c lightImpulseOff, with only one active at a time. + Whenever the delayed \c lightImpulse event is being delivered, it immediately + cause the transition from \c lightImpluseOn into \c lightImpulseOff or vice versa, + depending on the state the machine was in. In effect, the \c lightImpulseGenerator + toggles between its on and off state. These transitions are defined inside + \c lightImpulseGenerator, so it means that during this toggling the machine + also exits \c lightImpulseGenerator and reenters it immediately afterwards. + Entering \c lightImpulseGenerator cause generation of the \c update event. + The \c update event triggers the targetless transition and posts two other + events: \c scheduleNewImpulse and \c updateLights. The first one, + \c scheduleNewImpulse returns back to the \c lightImpulseGenerator and + it posts delayed \c lightImpulse event. After the desired delay, + the \c lightImpulse gets delivered to \c lightImpulseGenerator back + and it causes to toggle its substate again. In this way the machine + gets into the cycle. The current delay of the \c lightImpulse + event depends on the state in which the machine was in time of posting that delayed event. + Please also notice, that the next \c scheduleNewImpulse + event may occur on demand, before the next delayed \c lightImpulse + event gets delivered, so for that case we cancel any possible pending event. + + \quotefromfile pinball/pinball.scxml + \skipto workflow + \skipto letterOn + \printuntil /^\ {16}<\// + \printuntil /^\ {16}<\// + + Whenever we receive the \c letterOn event we update the current score. + The transition for the \c letterOn event is targetless, since we just + listen for that event and update the internal data accordingly + without changing any active state. The new score is being + increased by 1.000 or 10.000 points, depending if we currently are + in \c hurryStateOff or \c hurryStateOn. + After the score is updated we generate the \c updateLights event + in order to update immediately letters' lights accordingly. + We don't generate "update" here, since we don't want to toggle + light impulse now, but just update the lights according to + the current impulse state. + + We also intercept the \c {done.state.lettersState} event, + which is being generated when all the letters were hit. + Depending on which state we are currently in, we grant + a small bonus of 100.000 or a big one of 1.000.000 (jackpot). + In addition we toggle \c hurryState substate by + sending \c goToHurryOn or \c goToHurryOff event. + When all letters were collected being in \c hurryStateOn + we also raise the \c goForJackpot event which instructs + the machine to activate the \c jackpotStateOn. + + \skipto updateLights + \printuntil rLetterOn + \dots 32 + \skipto /^\ {28}<\// + \printuntil aLetterOn + \dots 32 + \skipto /^\ {28}<\// + \printuntil zLetterOn + \dots 32 + \skipto /^\ {28}<\// + \printuntil yLetterOn + \dots 32 + \skipto /^\ {28}<\// + \printuntil rLetterOn + \dots 32 + \skipto /^\ {28}<\// + \printuntil aLetterOn + \dots 32 + \skipto /^\ {28}<\// + \printuntil zLetterOn + \dots 32 + \skipto /^\ {28}<\// + \printuntil yLetterOn + \dots 32 + \skipto /^\ {28}<\// + \printuntil /^\ {16}<\// + + When we receive \c updateLights event, we first want to send a + \c updateScore signal outside of the state machine. We pass + to the signal current value of highscore and score variables. + This signal is received by the cpp part. + + Next, depending if we are in \c jackpotStateOn or \c jackpotStateOff + we send the \c turnOnJackpot or the \c turnOffJackpot signal, + which instructs the \c guiControl state to transition to + \c jackpotLightOn or \c jackpotLightOff respectively. + + When the machine is in \e idle state, i.e. in off state + or when the game is on, but no interaction is being performed, + the \c updateLights event is delivered periodically + during the game, each time with \c lightImpulseOn or + \c lightImpulseOff state toggled. Depending on the + current state of light impulse, and depending also + on which of \c offState, \c hurryStateOff or \c hurryStateOn + is active we turn on or off all the lights accordingly + to the description of the pinball table. + + \section1 GUI Part: User Interface Description + + The GUI part of the application consists of a mainwindow.ui + file which describes the static user interface of the game. + + \target cpp + \section1 CPP Part: Glue GUI with SCXML + + The CPP part of the application consists of a + MainWindow class which glues the GUI part with the SCXML part. + The class is declared in mainwindow.h. + + \quotefromfile pinball/mainwindow.h + \skipto Pinball + \printuntil }; + + The MainWindow class holds the pointer to the + \c {Pinball *m_machine} which is the state machine + class automatically generated by Qt out of SCMXL file + and the pointer to the \c {Ui::MainWindow *m_ui} which + describes the GUI part. It also declares two helper methods. + + \quotefromfile pinball/mainwindow.cpp + \skipto #include + \printuntil /}$/ + + The constructor of the MainWindow class + instantiates the GUI part of the application + and stores the pointer to the passed \c Pinball state machine. + It also initializes the GUI part and glues the + GUI part with the state machine by connecting + their communication interfaces together. + + The \c initAndConnect() method connects + the state with the corresponding GUI widget by + binding its activity with the enablement of the widget, + so that whenever the state is active its corresponding + widget is enabled and whenever the state is inactive + the widget is disabled. We do that for all lights, targets + and description lables. + + We also connect to the \c updateScore propagated + by the state machine and update the score displays + with the values passed with the event. + + The info about hitting any GUI target needs to be passed + to the state machine and we do that by connecting + all target buttons' \c clicked signals to the lambda expressions + which submit corresponding event into the state machine. + + \quotefromfile pinball/main.cpp + \skipto #include + \printuntil /}$/ + + Finally, there is main.cpp file with the main() function + inside which we instantiate the + \c app application object, \c Pinball state machine + and \c MainWindow GUI class. We initialize and start the state machine, + show the main window and execute the application. +*/ |