13
\$\begingroup\$

For example, I have a Game class and it keeps an int that tracks the player's lives. I have a conditional

if ( mLives < 1 ) {
    // Do some work.
}

However this condition keeps firing and the work is done repeatedly. For example, I want to set a timer to exit the game in 5 seconds. Currently, it will keep setting it to 5 seconds every frame and the game never ends.

This is just one example, and I have the same problem in several areas of my game. I want to check for some condition and then do something once and only once, and then not check or do the code in the if-statement again. Some possible solutions that come to mind are having a bool for each condition and set the bool when the condition fires. However in practice this gets very messy handling lots of bools, since they have to be stored as class fields, or statically within the method itself.

What is the proper solution to this (not for my example, but for the problem domain)? How would you do this in a game?

\$\endgroup\$
1
  • \$\begingroup\$ Thanks for the answers, my solution is now a combination of ktodisco and crancran answers. \$\endgroup\$
    – EddieV223
    Jan 7, 2013 at 6:36

3 Answers 3

13
\$\begingroup\$

I think that you can solve this problem simply by exerting more careful control over your possible code paths. For example, in the case where you're checking if the number of the player's lives has dropped below one, why not check only when the player loses a life, instead of every frame?

void subtractPlayerLife() {
    // Generic life losing stuff, such as mLives -= 1
    if (mLives < 1) {
        // Do specific stuff.
    }
}

This, in turn, assumes that subtractPlayerLife would only be called on specific occasions, which would perhaps result from conditions that you must check every frame (collisions, perhaps).

By carefully controlling how your code executes, you can avoid messy solutions like static booleans and also cut down bit-by-bit on how much code is executed in a single frame.

If there's something that just seems impossible to refactor, and you really need to only check once, then state (like an enum) or static booleans are the way to go. To avoid declaring the statics yourself, you can use the following trick:

#define ONE_TIME_IF(condition, statement) \
    static bool once##__LINE__##__FILE__; \
    if(!once##__LINE__##__FILE__ && condition) \
    { \
        once##__LINE__##__FILE__ = true; \
        statement \
    }

which would make the following code:

while (true) {
    ONE_TIME_IF(1 > 0,
        printf("something\n");
        printf("something else\n");
    )
}

print something and something else only once. It doesn't yield the best looking code, but it works. I'm also pretty sure there are ways to improve it, perhaps by better ensuring unique variable names. This #define will only work once during the runtime though. There is no way to reset it, and there is no way to save it.

Despite the trick, I strongly recommend better controlling your code flow first, and using this #define as a last resort, or for debug purposes only.

\$\endgroup\$
4
  • \$\begingroup\$ +1 nice one time if solution. Messy, but cool. \$\endgroup\$
    – kender
    Jan 4, 2013 at 10:08
  • 1
    \$\begingroup\$ there is one big problem with your ONE_TIME_IF, you can't make it happen again. For example player goes back to the main menu and later returns to the game scene. That statement won't fire again. and there are conditions you might need to keep between application runs, for example list of already acquired trophies. your method will reward the trophy twice if player gains it in two different application runs. \$\endgroup\$
    – Ali1S232
    Jan 4, 2013 at 14:53
  • 1
    \$\begingroup\$ @Gajoo That's true, it's a problem if the condition needs to be reset or saved. I've edited to clarify that. It's why I recommended it as a very last resort, or just for debugging. \$\endgroup\$ Jan 7, 2013 at 6:27
  • \$\begingroup\$ Can I suggest the do{} while(0) idiom? I know I'd personally screw something up and put a semicolon where I shouldn't. Cool macro though, bravo. \$\endgroup\$ Jan 7, 2013 at 6:58
5
\$\begingroup\$

This sounds like the classic case of using the state pattern. In one state you check your condition for lives < 1. If that condition becomes true, you transition to a delay state. This delay state waits the duration specified and then transitions to your exit state.

In the most trivial approach:

switch(mCurrentState) {
  case LIVES_GREATER_THAN_0:
    if(lives < 1) mCurrentState = LIVES_LESS_THAN_1;
    break;
  case LIVES_LESS_THAN_1:
    timer.expires(5000); // expire in 5 seconds
    mCurrentState = TIMER_WAIT;
    break;
  case TIMER_WAIT:
    if(timer.expired()) mCurrentState = EXIT;
    break;
  case EXIT:
    mQuit = true;
    break;
}

You could consider using a State class along with a StateManager/StateMachine to handle the changing/pushing/transition between states rather than a switch statement.

You can make your state management solution as sophisticated as you want, including multiple active states, a single active parent state with many active child states using some hierarchy, etc. Use what makes sense to you for now of course, but I truly think a state pattern solution will make your code much more reusable and easier to maintain and follow.

\$\endgroup\$
1
\$\begingroup\$

If you're worried about performance, the performance of checking multiple times is not usually significant; ditto for having bool conditions.

If you're interested in something else, you can use something similar to the null design pattern to solve this problem.

In this case, lets assume you want to call a method foo if the player has no lives left. You would implement this as two classes, one which calls foo, and one which does nothing. Lets call them DoNothing and CallFoo like so:

class SomeLevel {
  int numLives = 3;
  FooClass behaviour = new DoNothing();
  ...
  void tick() { 
    if (numLives < 1) {
      behaviour = new CallFoo();
    }

    behaviour.execute();
  }
}

This is a bit of a "raw" example; the key points are:

  • Keep track of some instance of FooClass which can be DoNothing or CallFoo. It can be a base class, abstract class, or interface.
  • Always call the execute method of that class.
  • By default, the class does nothing (DoNothing instance).
  • When lives run out, the instance is swapped for an instance that actually does something (CallFoo instance).

This is essentially like a mix of strategy and null patterns.

\$\endgroup\$
2
  • \$\begingroup\$ But it willl keep making new CallFoo() each frame, which you must delete before you new, and also keeps happening which doesn't fix the problem. For example if I had CallFoo() call a method that sets a timer to execute in a few seconds, that timer would be set to the same time each frame. As far as I can tell your solution is the same as if( numLives < 1 ) {// do stuff}else{//empty} \$\endgroup\$
    – EddieV223
    Jan 4, 2013 at 4:04
  • \$\begingroup\$ Hmm, I see what you're saying. The solution might be to move the if check into the FooClass instance itself, so it's only executed once, and ditto for instantiating CallFoo. \$\endgroup\$
    – ashes999
    Jan 4, 2013 at 4:06

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .