Question

I need to create a shooting delay for the hero. So as the user touches the screen and moves their fingers, the hero will begin shooting but there will be a continual delay between each shot fired.

So to be quite explicit:

user touches screen 
timer begins 
timer reached 
hero fires 
timer resets
timer reached hero fires

My first thoughts were to use a performSelector/dispatch. But after using dispatch I realised it queues up the block calls and fires them off after the delay has been reached. (i'm new to dispatch)

So now my thoughts are to create some timer variables and call the "fire" function when the timer has been reached. The timer starts when the user touches the screen.

I'm just looking for the best approach for this, as using timer variables can get quite messy. What are your thoughts?

Was it helpful?

Solution

It seems like this would be solved well with a finite state machine (FSM). You have several states, conditions for entering/exiting them, and well defined behaviors while in them.

I put together the diagram below to show you the model I am thinking about:

enter image description here

Notes:

  1. Each state has an "Enter" and "Execute" operation.
  2. The "ticks" variable is an integer counter. Set it to the number of "Update" cycles you want to wait to fire the bullet (e.g. 60 ticks = 1 second) in the Enter function when you enter a state that needs it ("Firing Start Delay" and "Inter Fire Delay". You set it differently for the states based on what you need for that state. Then decrement it in the Execute function for the states where it is used.
  3. Alternatively, you can use machine time (like a stopwatch) and compare it to the time when you entered the state. This gets you out of FPS tie to "time".
  4. There are four fundamental ways to implement simple FSMs (without getting into exotic prebuilt solutions or macros). In this case I would use a switch statement (see below).
  5. Call the ExecuteState(...) function in your Scene Update call (or schedule some other way).

Some Example Code:

typedef enum
{
   IDLE,
   FINGER_DOWN,
   FIRING_START_DELAY,
   FIRE_BULLET,
   INTER_FIRE_DELAY,
} FIRING_STATE;


// Class Variables:
FIRING_STATE _firingState = IDLE;
int _ticks = 0;
bool _fingerDown = false;

const int FRAMES_PER_SECOND = 60;
const float FIRING_START_DELAY_SECONDS = 1.5f;
const float INTER_FIRE_DELAY_SECONDS = 0.5;

void EnterState(FIRING_STATE firingState);
void ExecuteState(FIRING_STATE firingState);
void ChangeState(FIRING_STATE firingState);

void EnterState(FIRING_STATE firingState)
{
   switch(firingState)
   {
      case IDLE:
         break;
      case FINGER_DOWN:
         break;
      case FIRING_START_DELAY:
         // NOTE: You may change/start an animation or
         // play a sound here to give some feedback
         // to the player.
         _ticks = FIRING_START_DELAY_SECONDS*FRAMES_PER_SECOND;
         break;
      case FIRE_BULLET:
         break;
      case INTER_FIRE_DELAY:
         _ticks = INTER_FIRE_DELAY_SECONDS*FRAMES_PER_SECOND;
         break;
   }
}

void ExecuteState(FIRING_STATE firingState)
{
   // If the counter is running, decrement it.
   if(_ticks > 0)
   {
      _ticks--;
   }
   switch(firingState)
   {
      case IDLE:
         if(_fingerDown)
         {
            ChangeState(FINGER_DOWN);
         }
         break;
      case FINGER_DOWN:
         if(!_fingerDown)
         {
            ChangeState(IDLE);
         }
         else
         {

            ChangeState(FIRING_START_DELAY);
         }
         break;
      case FIRING_START_DELAY:
         if(_ticks == 0)
         {  //  Move on to next state
            ChangeState(FIRE_BULLET);
         }
         if(!_fingerDown)
         {
            ChangeState(IDLE);
         }
         break;
      case FIRE_BULLET:
         if(!_fingerDown)
         {
            ChangeState(IDLE);
         }
         ChangeState(INTER_FIRE_DELAY);
         break;
      case INTER_FIRE_DELAY:
         if(_ticks == 0)
         {  //  Move on to next state
            ChangeState(FIRE_BULLET);
         }
         if(!_fingerDown)
         {
            ChangeState(IDLE);
         }
         break;
   }
}

void ChangeState(FIRING_STATE firingState)
{
   EnterState(firingState);
   _firingState = firingState;
   ExecuteState(firingState);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top