I Carried the Game Qt Does It Again

The Rogue example shows how to apply the Qt country machine for effect handling.

This example implements a simple text based game. Do you see the @ in the screenshot? That'southward you lot, the rogue. The # characters are walls, and the dots correspond flooring. In a real game, other ASCII characters would represent all kinds of objects and creatures, for instance, ancient dragons (Ddue south) or food rations (%south). Just let'due south not get carried away. In this game, the rogue is only running effectually in an empty room.

The rogue is moved with the keypad (2, 4, 8, 6). That aside, we take implemented a quit control that triggers if the player types q. The player is then asked if he/she really wants to quit.

Virtually games have commands that need more than i key press (we call up of sequent presses, i.e., not of several keys being pressed at the same time). In this game, only the quit command falls under this category, but for the sake of statement, permit's imagine a fully-fledged game with a rich fix of commands. If nosotros were to implement these by catching key events in keyPressEvent(), we would accept to keep a lot of class member variables to rail the sequence of keys already typed (or find some other way of deducing the electric current state of a command). This can easily pb to spaghetti, which is--equally nosotros all well know, I'm sure--unpleasant. With a state machine, on the other hand, divide states tin can wait for a single cardinal printing, and that makes our lives a lot simpler.

The example consists of two classes:

  • Window draws the text display of the game and sets up the state machine. The window also has a status bar above the area in which the rouge moves.
  • MovementTransition is a transition that carries out a unmarried movement of the rogue.

Before nosotros commence on a code walkthrough, it is necessary to have a closer look at the design of the automobile. Hither is a state chart that shows what nosotros desire to achieve:

The input country waits for a key press to start a new control. When receiving a key it recognizes, it transitions to 1 of the two commands of the game; though, as we will come across, movement is handled by the transition itself. The quit state waits for the actor to answer aye or no (by typing y or n) when asked whether he/she actually wants to quit the game.

The chart demonstrates how we use one state to wait for a single key press. The press received may trigger one of the transitions connected to the state.

Window Class Definition

The Window class is a widget that draws the text display of the game. It likewise sets up the state auto, i.e., creates and connects the states in the machine. Information technology is the key events from this widget that are used past the machine.

                        course            Window :            public            QWidget            {     Q_OBJECT     Q_PROPERTY(QString            status READ status WRITE setStatus)            public:            enum            Direction { Upwardly,            Downwards,            Left,            Right };      Window();            void            movePlayer(Direction direction);            void            setStatus(const            QString            &condition);            QString            status()            const;            QSize            sizeHint()            const            override;            protected:            void            paintEvent(QPaintEvent            *event) override;

Direction specifies the direction in which the rogue is to move. We use this in movePlayer(), which moves the rogue and repaints the window. The game has a condition line in a higher place the area in which the rogue moves. The status property contains the text of this line. We use a belongings because the QState course allows setting any Qt property when entered. More on this later.

                        individual:            void            buildMachine();            void            setupMap();            QChar            map[WIDTH]            [Tiptop];            int            pX,            pY;            QStateMachine            *machine;            QString            myStatus; };

The map is an array with the characters that are currently displayed. Nosotros ready upward the array in setupMap(), and update it when the rogue is moved. pX and pY is the electric current position of the rogue. WIDTH and Superlative are macros specifying the dimensions of the map.

The paintEvent() function is left out of this walkthrough. We also practise not discuss other lawmaking that does not concern the state machine (the setupMap(), status(), setStatus(), movePlayer(), and sizeHint() functions). If you wish to accept a look at the code, click on the link for the window.cpp file at the top of this page.

Window Class Implementation

Here is the constructor of Window:

            Window::Window() {     pX            =            v;     pY            =            5;     ...     setupMap();     buildMachine(); }

The actor starts off at position (v, 5). We so set upwards the map and statemachine. Allow'south go along with the buildMachine() function:

                        void            Window::buildMachine() {     machine            =            new            QStateMachine;            QState            *inputState            =            new            QState(machine);     inputState-            >assignProperty(this            ,            "status"            ,            "Move the rogue with 2, 4, vi, and 8");      MovementTransition            *transition            =            new            MovementTransition(this);     inputState-            >addTransition(transition);

We enter inputState when the machine is started and from the quitState if the user wants to go on playing. We so set the status to a helpful reminder of how to play the game.

First, the Movement transition is added to the input land. This will enable the rogue to be moved with the keypad. Notice that we don't set a target state for the movement transition. This will cause the transition to be triggered (and the onTransition() function to be invoked), just the machine volition not leave the inputState. If we had set up inputState every bit the target state, we would first accept left and so entered the inputState once more.

When we enter quitState, nosotros update the condition bar of the window.

QKeyEventTransition is a utility class that removes the hassle of implementing transitions for QKeyEvents. Nosotros only need to specify the central on which the transition should trigger and the target state of the transition.

The transition from inputState allows triggering the quit state when the role player types q.

The machine is set upward, and then it'southward fourth dimension to start it.

The MovementTransition Class

MovementTransition is triggered when the player request the rogue to be moved (by typing 2, 4, 6, or 8) when the car is in the inputState.

In the constructor, nosotros tell QEventTransition to only transport KeyPress events to the eventTest() office:

                        protected:     bool eventTest(QEvent            *event) override {            if            (event-            >type()            =            =            QEvent            ::StateMachineWrapped            &            &            static_cast            <            QStateMachine            ::WrappedEvent            *            >(issue)-            >event()-            >blazon()            =            =            QEvent            ::KeyPress) {            QEvent            *wrappedEvent            =            static_cast            <            QStateMachine            ::WrappedEvent            *            >(upshot)-            >upshot();            QKeyEvent            *keyEvent            =            static_cast            <            QKeyEvent            *            >(wrappedEvent);            int            cardinal            =            keyEvent-            >key();            return            key            =            =            Qt            ::Key_2            |            |            cardinal            =            =            Qt            ::Key_8            |            |            key            =            =            Qt            ::Key_6            |            |            central            =            =            Qt            ::Key_4            |            |            key            =            =            Qt            ::Key_Down            |            |            fundamental            =            =            Qt            ::Key_Up            |            |            key            =            =            Qt            ::Key_Right            |            |            key            =            =            Qt            ::Key_Left;         }            return            imitation;     }

The KeyPress events come wrapped in QStateMachine::WrappedEvents. outcome must exist confirmed to be a wrapped event because Qt uses other events internally. Afterwards that, information technology is simply a affair of checking which key has been pressed.

Let'due south move on to the onTransition() role:

                        void            onTransition(QEvent            *event) override {            QKeyEvent            *keyEvent            =            static_cast            <            QKeyEvent            *            >(            static_cast            <            QStateMachine            ::WrappedEvent            *            >(event)-            >event());            int            key            =            keyEvent-            >fundamental();            switch            (primal) {            case            Qt            ::Key_Left:            case            Qt            ::Key_4:                 window-            >movePlayer(Window::Left);            suspension;            case            Qt            ::Key_Up:            instance            Qt            ::Key_8:                 window-            >movePlayer(Window::Up);            break;            case            Qt            ::Key_Right:            instance            Qt            ::Key_6:                 window-            >movePlayer(Window::Correct);            break;            example            Qt            ::Key_Down:            instance            Qt            ::Key_2:                 window-            >movePlayer(Window::Down);            break;            default:                 ;         }     }

When onTransition() is invoked, we know that nosotros have a KeyPress upshot with 2, 4, half dozen, or 8, and can enquire Window to move the role player.

The Roguelike Tradition

You might have been wondering why the game features a rogue. Well, these kinds of text based dungeon exploration games appointment back to a game called, yes, "Rogue". Although outflanked by the technology of modern 3D estimator games, roguelikes have a solid community of hard-cadre, devoted followers.

Playing these games can be surprisingly addictive (despite the lack of graphics). Angband, the perhaps about well-known rougelike, is establish hither: http://rephial.org/.

Instance project @ code.qt.io

dickersonwher1959.blogspot.com

Source: https://doc.qt.io/qt-6/qtstatemachine-statemachine-rogue-example.html

0 Response to "I Carried the Game Qt Does It Again"

Postar um comentário

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel