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.
Related
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?
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
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'm attempting to forward all key press events from my QGraphicsView to a widget that is currently on the scene.
My QGraphicsView looks like this:
Character_controller::Character_controller(Game_state * game_state) : Game_base_controller(game_state) {
this->character = new Character(this->game_state);
this->scene->addWidget(this->character);
connect(this, SIGNAL(keyPress(QKeyEvent *)), this->character, SLOT(update()));
}
And then, my character which subclasses QWidget, which should recieve all keypress events
Character::Character(Game_state * game_state) : Base_object(game_state) {
}
Character::~Character() {
}
void Character::update() {
cout << "HELLO FROM TIMER CONNECTED ITEM" << endl;
}
For some reason, this isn't working. How can I forward all keypress events from the view to my character?
The error I get is this:
Object::connect: No such signal game::Character_controller::keyPress(QKeyEvent *) in implementation/game_controllers/character_controller.cpp:21
keyPress(QKeyEvent*) doesn't exist as a signal, hence the error message that you're getting. As such, you can't do this:
connect(this, SIGNAL(keyPress(QKeyEvent *)), this->character, SLOT(update()));
In order to capture key press events in your graphics view, you will need to override the keyPressEvent function:
void Character_controller::keyPressEvent(QKeyEvent* event)
{
// Call functions on your character here.
switch (event->key())
{
case Qt::Key_A:
character->moveLeft(); // For example
break;
case Qt::Key_D:
character->moveRight(); // For example
break;
...
}
// Otherwise pass to QGraphicsView.
QGraphicsView::keyPressEvent(event);
}
You could just pass the QKeyEvent to the character to manage its own key presses, but you might find it difficult to ensure that different items in your scene don't rely on the same key(s) if you don't keep all your key press handling code in one place.
You have to override the keyPressEvent event to capture key press events
OS:: win xp sp3.
Qt:: 4.6
I have class Gameboard in which i have some rectangle.I defined keyPressEvent for that rectangle in order to move him around the screen.Key_A :: rectangle.moveToLeft & Key_D :: rectangle.moveToRight.Problem is that keys work with delay.
When i release one key and press another one it takes some time before that one begin to work.I checked Qt online documentation and now for that effect but dont now how to make those keys to work instantly without delay beetween them?
code snippet:
//in Gameboard class
ship = new QRect(x,y,w,h);
void Gameboard::keyPressEvent(QKeyEvent* event)
{
switch(event->key()) {
case Qt::Key_A :
{
x = x-10;
ship->moveTo(x,y);
break;
}
case Qt::Key_D :
{
x = x+10;
ship->moveTo(x,y);
break;
}
}
}
Put input cursor into any applicable text box and press the 'A' key. What you'll see is once you press the key, letter 'A' will be printed, then there will be a pause, and then first letter 'A' will be quickly followed by many others. That's the way keypress events work. And your program is receiving them exactly like this. First you receive one event when the key is actually pressed, and then after a delay you get a lot of automatically repeated events, in case user wants to enter one character many-many times.
It works perfectly for text input, but in games you usually need smooth movement. If that's the case, you need to move your ship not upon receiving the event, but regularly, for example, on timer event. And you will need to catch both keyPressEvent and keyRelease event and use them to remember what movement keys are currently pressed. So you could for example do this:
struct Ship {
bool is_moving_left, is_moving_right;
QPoint position;
int speed;
...
void timerEvent()
{
if (is_moving_left) position.setX (position.x() - speed);
if (is_moving_right) position.setX (position.x() + speed);
}
...
};
...
void Gameboard::keyPressEvent (OKeyEvent *_event)
{
switch(event->key()) {
case Qt::Key_A :
ship->is_moving_left = true;
break;
case Qt::Key_D :
ship->is_moving_right = true;
break;
}
}
...
void Gameboard::keyReleaseEvent (OKeyEvent *_event)
{
switch(event->key()) {
case Qt::Key_A :
ship->is_moving_left = false;
break;
case Qt::Key_D :
ship->is_moving_right = false;
break;
}
}
Then just make sure Ship::timerEvent() gets called on every timer event in the game.