These are the discrete states our motorcycle can be in at any given moment.
Each state has specific behaviors and animations.
Finite State Machine (FSM): A system can only be in ONE state at a time.
State Behavior Definitions
π STOP State
Speed: Zero
Gears: Neutral
Animation: Engine idle (chassis vibrating)
Input: Can transition to START
βΆοΈ START State
Speed: Max speed
Movement: Forward motion
Animation: Wheels turning
Input: Can TURN or STOP
π TURN State
Direction: Left or Right
Movement: Lateral shift
Animation: Bike tilting
Input: Return to START
π₯ CRASH State
Speed: Decelerating
State: On fire, on side
Animation: Crash sequence
Input: No longer responds
Implementation Step 1: Interface
namespace Chapter.State
{
/// The state interface that all concrete states implementpublic interfaceIBikeState
{
// Handle the state behavior// Takes BikeController to access propertiesvoidHandle(BikeController controller);
}
}
Design Note: We pass BikeController to Handle() so state classes
can access bike properties (speed, direction, etc.). This is a Unity-adapted approach.
Implementation Step 2: Context
namespace Chapter.State
{
public classBikeStateContext
{
// Current state - publicly accessiblepublic IBikeState CurrentState { get; set; }
// Reference to the bike controllerprivate readonly BikeController _bikeController;
publicBikeStateContext(BikeController bikeController)
{
_bikeController = bikeController;
}
// Transition to current statepublic voidTransition()
{
CurrentState.Handle(_bikeController);
}
// Transition to a new statepublic voidTransition(IBikeState state)
{
CurrentState = state;
CurrentState.Handle(_bikeController);
}
}
}
Two Transition Methods: One cycles current state, one sets and transitions to new state.
Implementation Step 3: Controller
using UnityEngine;
namespace Chapter.State
{
public classBikeController : MonoBehaviour
{
// Public properties for configurationpublic float maxSpeed = 2.0f;
public float turnDistance = 2.0f;
// Current state datapublic float CurrentSpeed { get; set; }
public Direction CurrentTurnDirection { get; private set; }
// State referencesprivate IBikeState _startState, _stopState, _turnState;
private BikeStateContext _bikeStateContext;
voidStart()
{
// Initialize context
_bikeStateContext = new BikeStateContext(this);
// Initialize states as components
_startState = gameObject.AddComponent<BikeStartState>();
_stopState = gameObject.AddComponent<BikeStopState>();
_turnState = gameObject.AddComponent<BikeTurnState>();
// Set initial state
_bikeStateContext.Transition(_stopState);
}
// Public methods to change statespublic voidStartBike() => _bikeStateContext.Transition(_startState);
public voidStopBike() => _bikeStateContext.Transition(_stopState);
public voidTurn(Direction direction)
{
CurrentTurnDirection = direction;
_bikeStateContext.Transition(_turnState);
}
}
}
Implementation Step 4: Stop State
using UnityEngine;
namespace Chapter.State
{
public classBikeStopState : MonoBehaviour, IBikeState
{
private BikeController _bikeController;
public voidHandle(BikeController bikeController)
{
// Cache controller referenceif (!_bikeController)
_bikeController = bikeController;
// STOP behavior: set speed to zero
_bikeController.CurrentSpeed = 0;
Debug.Log("[BikeStopState] Bike stopped");
}
}
}
Stop State Behavior
Simply sets the bike's speed to zero. In a full implementation, this would
also trigger idle animations and reset gears.
Implementation Step 5: Start State
using UnityEngine;
namespace Chapter.State
{
public classBikeStartState : MonoBehaviour, IBikeState
{
private BikeController _bikeController;
public voidHandle(BikeController bikeController)
{
if (!_bikeController)
_bikeController = bikeController;
// START behavior: set to max speed
_bikeController.CurrentSpeed = _bikeController.maxSpeed;
}
voidUpdate()
{
// While in START state, move forward each frameif (_bikeController)
{
if (_bikeController.CurrentSpeed > 0)
{
_bikeController.transform.Translate(
Vector3.forward *
(_bikeController.CurrentSpeed * Time.deltaTime));
}
}
}
}
}
Note: Update() runs continuously while this component is active,
moving the bike forward!
Implementation Step 6: Turn State
using UnityEngine;
namespace Chapter.State
{
public classBikeTurnState : MonoBehaviour, IBikeState
{
private Vector3 _turnDirection;
private BikeController _bikeController;
public voidHandle(BikeController bikeController)
{
if (!_bikeController)
_bikeController = bikeController;
// Set turn direction (Left = -1, Right = 1)
_turnDirection.x = (float)_bikeController.CurrentTurnDirection;
// Only turn if movingif (_bikeController.CurrentSpeed > 0)
{
transform.Translate(_turnDirection *
_bikeController.turnDistance);
}
}
}
}
// Direction enum referenced by BikeControllerpublic enum Direction
{
Left = -1,
Right = 1
}
Testing the State Pattern
Test Setup Steps
Create new Unity scene
Add 3D Cube GameObject (visible to camera)
Attach BikeController script to cube
Attach test client script (next slide)
Play and use GUI buttons to test states
Visual Feedback: Watch the cube move forward (START),
shift sideways (TURN), and stop (STOP) based on button clicks!
Test Client Script
using UnityEngine;
namespace Chapter.State
{
public classClientState : MonoBehaviour
{
private BikeController _bikeController;
voidStart()
{
// Find the BikeController in the scene
_bikeController = (BikeController)
FindObjectOfType(typeof(BikeController));
}
voidOnGUI()
{
// Create GUI buttons to control the bikeif (GUILayout.Button("Start Bike"))
_bikeController.StartBike();
if (GUILayout.Button("Turn Left"))
_bikeController.Turn(Direction.Left);
if (GUILayout.Button("Turn Right"))
_bikeController.Turn(Direction.Right);
if (GUILayout.Button("Stop Bike"))
_bikeController.StopBike();
}
}
}
State-specific behaviors are isolated in their own classes.
No giant switch statements!
β Maintainability
Easy to add new states without modifying existing code.
Open/Closed Principle!
β Clarity
Each state class has a single responsibility.
Clear, focused code!
β Flexibility
States can be assigned dynamically at runtime.
Powerful composition!
Drawbacks & Limitations
β οΈ Limitation 1: Animation Blending
The State pattern doesn't provide built-in animation blending.
Transitioning from idle β walk β run requires smooth visual blending,
which needs extra code.
β οΈ Limitation 2: Complex Transitions
Defining relationships and conditions between states (state diagrams)
requires significant boilerplate code. Example: "Can only transition to Run
from Walk, not from Idle."
β οΈ Limitation 3: Many Classes
Complex characters with many states = many classes to manage.
Unity's Built-In Solution
Unity Animation System
Unity's animation system IS a Finite State Machine (FSM) with: