Let's say I have been implementing a state machine in the C++ based on the switch statement i.e.
I have following piece of code
class StateMachine {
public:
enum class State {
State_A,
State_B,
State_C
};
enum class Event {
Event_Default,
Event_A,
Event_B,
Event_C
};
StateMachine();
void handleEvent(Event e);
private:
State state;
void doStateA();
void doStateB();
void doStateC();
};
StateMachine::StateMachine()
{
state = State::State_A;
}
void StateMachine::handleEvent(Event e)
{
switch (state)
{
case State::State_A:
doStateA(e);
break;
case State::State_B:
doStateB(e);
break;
case State::State_C:
doStateC(e);
break;
}
}
void StateMachine::doStateA(Event e)
{
// once upon entry into the state do some actions here
// the actions have to be done only once after state entry
switch(e) {
case Event::Event_Default:
break;
case Event::Event_A:
state = State::State_B;
break;
case Event::Event_B:
state = State::State_C;
break;
case StateMachine::Event::Event_C:
state = State::State_A;
break;
}
}
void StateMachine::doStateB(Event e)
{
// ...
}
void StateMachine::doStateC(Event e)
{
// ...
}
I have found that I need to have the ability to do some actions once upon entry into the states. Of cource I could do those entry actions before the state transitions in the doStateX() methods. But I see following disadvantage of this approach. The entry action of a given state isn't done in that state but in the state (or states) from which the transition occurs. This approach seems to me to be error prone. So I have been looking for a solution where the entry action of a given state is localized directly in that state. Does anybody have any idea?
Related
I am programming an LED Cube I have designed. The cube has a "pause" button and a "play/next" button. Unless the cube is paused, it will cycle through all of the different effects (animations) I've made for it. If you press the pause button, the cube will no longer transition between effects and will instead repeat the current effect. Pressing the 'play/next' button will unset the pause feature and will advance to the next effect immediately.
Some of these effects are pretty complex and require a large number of variables to be kept between frames of animation. In order to easily destroy all of these variables at a moment's notice (like when the next button is pressed), I'm instantiating the current animation as an object and destroying it when the effect is complete or the skip button is pressed.
I'm trying to set my main loop up as follows:
void loop() {
//create an effect object
switch(effectIndex){
case 0:
EF_GROWFRAME effect;
break;
case 1:
EF_RANDOMFILL effect;
break;
}
bool proceed;
do{
//returns false until the effect has completed
proceed=effect.step();
//push this cube update and wait for it to display
cube.update();
cube.waitForFrame();
}
while ((!proceed)&&(!skipflag));
//skipflag is set true during a timer interrupt if the skip button is freshly pressed
skipflag=false;
cube.clearPattern();
if (play) effectIndex++;
if (effectIndex=effectCount) effectIndex=0;
}
That fails because of my conflicting definitions of effect though. You can probably see what I'm going for, so what's the proper way to approach this?
This is a use case for polymorphism.
Define a base class, Animation that defines a shared interface and have your various animation types derive from it. For example:
class Animation {
public:
virtual ~Animation() {
// any generic cleanup shared by all animation types
}
virtual bool step() = 0;
};
class AnimationA : public Animation {
public:
bool step() override {
// logic for this type of animation
}
};
class AnimationB : public Animation {
public:
bool step() override {
// logic for this type of animation
}
};
void loop() {
std::unique_ptr<Animation> effect;
switch (effectIndex) {
case 0:
effect = std::make_unique<AnimationA>();
break;
case 1:
effect = std::make_unique<AnimationB>();
break;
}
//...
}
Live Demo
Since it seems like this may be an embedded environment, you could avoid the dynamic memory allocation from my first example by factoring your animation playing logic out into a separate function:
void playAnimation(Animation& effect) {
bool proceed;
do{
//returns false until the effect has completed
proceed=effect.step();
//push this cube update and wait for it to display
cube.update();
cube.waitForFrame();
} while (!proceed && !skipFlag);
//skipflag is set true during a timer interrupt if the skip button is freshly pressed
skipflag=false;
cube.clearPattern();
}
void loop() {
switch (effectIndex) {
case 0:
{
AnimationA effect;
playAnimation(effect);
break;
}
case 1:
{
AnimationB effect;
playAnimation(effect);
break;
}
}
if (play) effectIndex++;
if (effectIndex == effectCount) effectIndex=0;
}
Live Demo
I am trying to clean up movement code I followed from a video tutorial series that was never finished. My intent is for the character to only ever be able to move on X or Y at any given time (so no diagonal). The character has direction facing to keep in mind.
My issue is the player can still press any key they want, or accidently press two keys at the same time.
Ex. if you move Up and make a right turn, accidentally press Right before letting go of Up.
Or if you press Up, press and let go Right to make a slight movement right while continuing to press Up, the player should continue to move up after letting go of Right without having to re-press Up. etc.
Just to make sure all possible input cases are handled intuitively.
EDIT: This is the code so far and I'm getting weird errors I don't know what's wrong
#pragma once
#include "../game.h"
#include "ECS.h"
#include "Components.h"
#include <list>
using namespace std;
class KeyboardController : public Component
{
public:
TransformComponent *transform;
SpriteComponent *sprite;
std::list<SDL_Event> keyDownList;
SDL_Event lastDirection;
void updateKeyState()
{
if (Game::event.type == SDLK_ESCAPE) {
Game::isRunning = false;
}
else if (Game::event.type == SDL_KEYDOWN) {
keyDownList.push_back(Game::event.key.keysym.sym);
}
else if (Game::event.type == SDL_KEYUP) {
keyDownList.remove(Game::event.key.keysym.sym);
}
}
void init() override
{
transform = &entity->getComponent<TransformComponent>();
sprite = &entity->getComponent<SpriteComponent>();
}
void update() override
{
void updateKeyState();
void updateMovement();
}
};
Severity Code Description Project File Line Suppression State
Error (active) E0304 no instance of overloaded function "std::list<_Ty, _Alloc>::push_back [with _Ty=SDL_Event, _Alloc=std::allocator]" matches the argument list Sandbox C:\file_path\KeyboardController.h 31
Severity Code Description Project File Line Suppression State
Error (active) E0415 no suitable constructor exists to convert from "SDL_Keycode" to "SDL_Event" Sandbox C:\file_path\KeyboardController.h 34
You should basically clean up your code by separating the logic between key events and player movement. So your update() method could look like this:
void update() override
{
updateKeyState();
updateMovement();
}
Since you want the player to move only vertically or horizontally (never diagonally), you have to store the key press order in a data structure that can be easily accessed. I think you could use a doubly-linked list:
std::list<SDL_Event> keyDownList;
and we should also store the last key pressed in order to restore the idle animation of the player:
SDL_Event lastDirection;
The updateKeyState() method should add or remove the key to/from the linked list. We should also check if the player wants to leave the game by pressing ESC:
void updateKeyState() {
if (Game::event.type == SDLK_ESCAPE) {
Game::isRunning = false;
} else if (Game::event.type == SDL_KEYDOWN) {
keyDownList.push_back(Game::event.key.keysym.sym);
} else if (Game::event.type == SDL_KEYUP) {
keyDownList.remove(Game::event.key.keysym.sym);
}
}
The updatePlayerMovement() method is where the magic happens. We should basically check which key was pressed first and update the player position accordingly. We also save the down key in the lastDirection field in order to use it when no key is pressed.
void updateMovement() {
// if any key is down
if (keyDownList.size() > 0) {
const SDL_Event downKey = keyDownList.front();
switch (downKey) {
case SDLK_w:
transform->velocity.y = -1;
transform->velocity.x = 0;
sprite->Play("BackWalk");
lastDirection = downKey;
break;
case SDLK_a:
transform->velocity.x = -1;
transform->velocity.y = 0;
sprite->Play("SideWalk");
sprite->spriteFlip = SDL_FLIP_HORIZONTAL;
lastDirection = downKey;
break;
case SDLK_s:
transform->velocity.y = 1;
transform->velocity.x = 0;
sprite->Play("FrontWalk");
lastDirection = downKey;
break;
case SDLK_d:
transform->velocity.x = 1;
transform->velocity.y = 0;
sprite->Play("SideWalk");
sprite->spriteFlip = SDL_FLIP_NONE;
lastDirection = downKey;
break;
}
} else {
// no key is down, handle idle situations
transform->velocity.x= 0;
transform->velocity.y = 0;
switch (lastDirection) {
case SDLK_w:
sprite->Play("BackIdle");
break;
case SDLK_a:
sprite->Play("SideIdle");
break;
case SDLK_s:
sprite->Play("FrontIdle");
break;
case SDLK_d:
sprite->Play("SideIdle");
break;
}
}
}
Note: I haven't tested this code because I don't have the code and structures from your game. So you may have to edit a piece here and there to make it work for you.
My question is:
I am trying to implement basic state management in my project and i stuck at changing states.
I have all my states in std::stack<State*> container, and push/pop them directly from Application class or from State class.
Problem is when i change current state from State class, it can be destroyed before render method called, whitch results in exeption. So how do i avoid this?
PS sorry for my english and please say me if something in my problem/code isn clear
Application class:
void Application::pushState(State* state)
{
this->m_states.push(state);
this->m_states.top()->open();//enter state
}
void Application::popState()
{
if (!this->m_states.empty())
{
this->m_states.top()->close();//leave state
delete this->m_states.top();
}
if (!this->m_states.empty())
this->m_states.pop();
}
void Application::changeState(State* state)
{
if (!this->m_states.empty())
popState();
pushState(state);
}
State* Application::peekState()
{
if (this->m_states.empty()) return nullptr;
return this->m_states.top();
}
void Application::mainLoop()
{
sf::Clock clock;
while (this->m_window.isOpen())
{
sf::Time elapsed = clock.restart();
float delta = elapsed.asSeconds();
if (this->peekState() == nullptr)
this->m_window.close();
this->peekState()->update(delta)//if i change state in State.update(), it may be that code below will now point to not existing state
if (this->peekState() == nullptr)
this->m_window.close();
this->peekState()->render(delta);
}
}
State class:
void EditorState::update(const float delta)
{
sf::Event event;
while (this->m_application->m_window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
this->m_application->popState();
return;
}
}
}
Okay maybe this is not really a problem, but something like "how to" question. As you can see in my code, i update and render states in mainLoop() method. What im tying to figure out is how to manage those updates, asuming that state can be changed from state itself, not only from stateManager (in my case Application class)
Ok, so I'm guessing this is for a game (but it doesn't have to be). Instead of doing what you're doing for switching between states, I use an enum.
enum class GameState {
MENU, PLAY, PAUSE
}
Then, in your main header
GameState m_gameState = GameState::MENU;
In your main loop, you can check what the current state is by simply doing
if (m_gameState == GameState::MENU)
{
...
}
or you can use a switch statement
switch (m_gameState)
{
case GameState::MENU:
...
break;
case GameState::PLAY:
...
break;
case GameState::PAUSE:
...
break;
}
And, if you ever want to switch the state, you can just do
m_gameState = GameState::PAUSE;
Hope this answered your question :D
If not, I must have misunderstood (sorry).
I want to handle the Green Key Event in Symbian. I have handled Red Key(End Button) with the help of KAknUidValueEndKeyCloseEvent. Can you suggest me what is the name of the event of Green Key. Here is the necessary code.
void CMobileDialerAppUi::HandleWsEventL(const TWsEvent &aEvent, CCoeControl *aDestination)
{
switch (aEvent.Type())
{
case KAknUidValueEndKeyCloseEvent:
{
TUid KMyAppUid = { 0x20070DF6 };
TApaTaskList tasklist(CCoeEnv::Static()->WsSession());
TApaTask task = tasklist.FindApp(KMyAppUid);
if(task.Exists())
{
task.BringToForeground();
}
symbian_ua_endcall();
break;
}
default:
CAknAppUi::HandleWsEventL(aEvent, aDestination);
break;
}
}
on your container override method OfferKeyEventL
TKeyResponse CMobileDialerContainer::OfferKeyEventL(const TKeyEvent &aKeyEvent, TEventCode aType)
{
if (EStdKeyDevice0==aKeyEvent.iScanCode){
if (aType == EEventKeyUp) {
//a green key has press
return EKeyWasConsumed;
}
}
return EKeyWasConsumed;
}
I had a hard time understand exactly what an observer pattern was, but I produced the following code for my project. It uses SDL. I am using the boost library to implement signals and therefore implementing my observer pattern. Does this look correct?
/* This is setting up our signal for sending observations */
boost::signals2::signal<void (char, int, int)> sig;
/* Subjects the Observer will connect with */
sig.connect(&setChest);
sig.connect(&setNonTraverse);
sig.connect(&setEntry);
sig.connect(&setExit);
std::cout << "Waiting for user-interaction. Press on the 'X' to quit" << std::endl;
while ( !quit ) {
status = SDL_WaitEvent(&event); //wait for an event to occur
switch (event.type) { //check the event type
case SDL_KEYDOWN: //Check if a key was pressed.
key = SDL_GetKeyName(event.key.keysym.sym);
break;
case SDL_MOUSEBUTTONUP:
sig(key[0],event.button.x/32,event.button.y/32);
break;
case SDL_QUIT: // Click on the 'X' to close the window.
exit ( 1 );
break;
}
} //while
return true;
}
Your posted code is that of the Observer.
In the Observer pattern, the observer doesn't react directly to the subjects' state changes. Instead, the subject informs the observer of any changes by invoking the observer's callback. This is why the observer must register with the subject, instead of merely polling (checking the state in a while loop) the subject.
I'm not too familiar with C++, but here is some Java-like pseudocode that outlines the basic idea:
class Observer{
public Observer(Subject subject){
subject.register(this);
}
public void updateFromSubject(Subject subject){
//respond to change
}
}
class Subject{
List<Observer> observers;
public void register(Observer observer){
observers.add(observer);
}
private void notifyObservers(){
for(Observer obs : observers){
obs.updateFromSubject(this);
}
}
public void changeStateToNewState(Object o){
.... //business logic
notifyObservers();
}
Notice the lack of a while loop, which means that the observer simply doesn't do any work until an actual event occurs, instead of checking a flag a million times a second just to see if it changed.