Skip to content

Commit 88c3a2f

Browse files
committed
Fix #250.
1 parent 5431deb commit 88c3a2f

File tree

3 files changed

+90
-34
lines changed

3 files changed

+90
-34
lines changed

book/state.markdown

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -335,12 +335,17 @@ one heroine, but if we try to add two-player co-op and have two heroines on
335335
screen at the same time, we'll have problems.
336336

337337
In that case, we have to <span name="fragment">create</span> a state
338-
object when we transition to it, like:
338+
object when we transition to it. This lets each FSM have its own instance of the state. Of course, if we're allocating a *new* state, that means we need to free the *current* one. We have to be careful here, since the code that's triggering the change is in a method in the current state. We don't want to delete `this` out from under ourselves.
339+
340+
Instead, we'll allow `handleInput()` in `HeroineState` to optionally return a new state. When it does, `Heroine` will delete the old one and swap in the new one, like so:
341+
342+
^code swap-instance
343+
344+
That way, we don't delete the previous state until we've returned from its method. Now, the standing state can transition to ducking by creating a new instance:
339345

340346
^code duck
341347

342-
This lets each FSM have its own instance of the state. When I can, I prefer to
343-
use static states since they don't burn memory and CPU cycles allocating objects
348+
When I can, I prefer to use static states since they don't burn memory and CPU cycles allocating objects
344349
each state change. For states that are more, uh, *stateful*, though, this is the
345350
way to go.
346351

code/cpp/state.h

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -429,29 +429,66 @@ namespace State
429429
int chargeTime_;
430430
};
431431
//^ducking-state
432+
}
432433

433-
class StandingState : public HeroineState
434+
namespace InstancedStates
435+
{
436+
class Heroine;
437+
438+
class HeroineState
434439
{
435440
public:
436-
virtual void handleInput(Heroine& heroine, Input input) {
437-
//^jump
438-
if (input == PRESS_B)
439-
{
440-
heroine.state_ = &HeroineState::jumping;
441-
heroine.setGraphics(IMAGE_JUMP);
442-
}
443-
//^jump
444-
//^duck
445-
// In StandingState:
446-
if (input == PRESS_DOWN)
447-
{
448-
delete heroine.state_;
449-
heroine.state_ = new DuckingState();
450-
// Other code...
451-
}
452-
//^duck
441+
virtual ~HeroineState() {}
442+
virtual HeroineState* handleInput(Heroine& heroine, Input input)
443+
{
444+
return NULL;
445+
}
446+
virtual void update(Heroine& heroine) {}
447+
};
448+
449+
class Heroine
450+
{
451+
public:
452+
virtual void handleInput(Input input);
453+
454+
private:
455+
HeroineState* state_;
456+
};
457+
458+
class DuckingState : public HeroineState {};
459+
460+
//^swap-instance
461+
void Heroine::handleInput(Input input)
462+
{
463+
HeroineState* state = state_->handleInput(*this, input);
464+
if (state != NULL)
465+
{
466+
delete state_;
467+
state_ = state;
453468
}
469+
}
470+
//^swap-instance
471+
472+
class StandingState : public HeroineState
473+
{
474+
public:
475+
virtual HeroineState* handleInput(Heroine& heroine, Input input);
454476
};
477+
478+
//^duck
479+
HeroineState* StandingState::handleInput(Heroine& heroine,
480+
Input input)
481+
{
482+
if (input == PRESS_DOWN)
483+
{
484+
// Other code...
485+
return new DuckingState();
486+
}
487+
488+
// Stay in this state.
489+
return NULL;
490+
}
491+
//^duck
455492
}
456493

457494
namespace StaticStateInstances

html/state.html

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -536,11 +536,7 @@ <h3><a href="#static-states" name="static-states">Static states</a></h3>
536536

537537
<p>Each of those static fields is the one instance of that state that the game
538538
uses. To make the heroine jump, the standing state would do something like:</p>
539-
<div class="codehilite"><pre><span class="k">if</span> <span class="p">(</span><span class="n">input</span> <span class="o">==</span> <span class="n">PRESS_B</span><span class="p">)</span>
540-
<span class="p">{</span>
541-
<span class="n">heroine</span><span class="p">.</span><span class="n">state_</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">HeroineState</span><span class="o">::</span><span class="n">jumping</span><span class="p">;</span>
542-
<span class="n">heroine</span><span class="p">.</span><span class="n">setGraphics</span><span class="p">(</span><span class="n">IMAGE_JUMP</span><span class="p">);</span>
543-
<span class="p">}</span>
539+
<div class="codehilite"><pre>
544540
</pre></div>
545541

546542

@@ -551,19 +547,37 @@ <h3><a href="#instantiated-states" name="instantiated-states">Instantiated state
551547
one heroine, but if we try to add two-player co-op and have two heroines on
552548
screen at the same time, we&#8217;ll have problems.</p>
553549
<p>In that case, we have to <span name="fragment">create</span> a state
554-
object when we transition to it, like:</p>
555-
<div class="codehilite"><pre><span class="c1">// In StandingState:</span>
556-
<span class="k">if</span> <span class="p">(</span><span class="n">input</span> <span class="o">==</span> <span class="n">PRESS_DOWN</span><span class="p">)</span>
550+
object when we transition to it. This lets each FSM have its own instance of the state. Of course, if we&#8217;re allocating a <em>new</em> state, that means we need to free the <em>current</em> one. We have to be careful here, since the code that&#8217;s triggering the change is in a method in the current state. We don&#8217;t want to delete <code>this</code> out from under ourselves.</p>
551+
<p>Instead, we&#8217;ll allow <code>handleInput()</code> in <code>HeroineState</code> to optionally return a new state. When it does, <code>Heroine</code> will delete the old one and swap in the new one, like so:</p>
552+
<div class="codehilite"><pre><span class="kt">void</span> <span class="n">Heroine</span><span class="o">::</span><span class="n">handleInput</span><span class="p">(</span><span class="n">Input</span> <span class="n">input</span><span class="p">)</span>
557553
<span class="p">{</span>
558-
<span class="k">delete</span> <span class="n">heroine</span><span class="p">.</span><span class="n">state_</span><span class="p">;</span>
559-
<span class="n">heroine</span><span class="p">.</span><span class="n">state_</span> <span class="o">=</span> <span class="k">new</span> <span class="n">DuckingState</span><span class="p">();</span>
560-
<span class="c1">// Other code...</span>
554+
<span class="n">HeroineState</span><span class="o">*</span> <span class="n">state</span> <span class="o">=</span> <span class="n">state_</span><span class="o">-&gt;</span><span class="n">handleInput</span><span class="p">(</span><span class="o">*</span><span class="k">this</span><span class="p">,</span> <span class="n">input</span><span class="p">);</span>
555+
<span class="k">if</span> <span class="p">(</span><span class="n">state</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span>
556+
<span class="p">{</span>
557+
<span class="k">delete</span> <span class="n">state_</span><span class="p">;</span>
558+
<span class="n">state_</span> <span class="o">=</span> <span class="n">state</span><span class="p">;</span>
559+
<span class="p">}</span>
560+
<span class="p">}</span>
561+
</pre></div>
562+
563+
564+
<p>That way, we don&#8217;t delete the previous state until we&#8217;ve returned from its method. Now, the standing state can transition to ducking by creating a new instance:</p>
565+
<div class="codehilite"><pre><span class="n">HeroineState</span><span class="o">*</span> <span class="n">StandingState</span><span class="o">::</span><span class="n">handleInput</span><span class="p">(</span><span class="n">Heroine</span><span class="o">&amp;</span> <span class="n">heroine</span><span class="p">,</span>
566+
<span class="n">Input</span> <span class="n">input</span><span class="p">)</span>
567+
<span class="p">{</span>
568+
<span class="k">if</span> <span class="p">(</span><span class="n">input</span> <span class="o">==</span> <span class="n">PRESS_DOWN</span><span class="p">)</span>
569+
<span class="p">{</span>
570+
<span class="c1">// Other code...</span>
571+
<span class="k">return</span> <span class="k">new</span> <span class="n">DuckingState</span><span class="p">();</span>
572+
<span class="p">}</span>
573+
574+
<span class="c1">// Stay in this state.</span>
575+
<span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
561576
<span class="p">}</span>
562577
</pre></div>
563578

564579

565-
<p>This lets each FSM have its own instance of the state. When I can, I prefer to
566-
use static states since they don&#8217;t burn memory and CPU cycles allocating objects
580+
<p>When I can, I prefer to use static states since they don&#8217;t burn memory and CPU cycles allocating objects
567581
each state change. For states that are more, uh, <em>stateful</em>, though, this is the
568582
way to go.</p>
569583
<aside name="fragment">

0 commit comments

Comments
 (0)