Implementing Character Jumping with a Hierarchical State Machine in Unity
In a 2D platformer, character actions such as idle, move, jump, and fall benefit from a hierarchical state machine. High‑level super states (Grounded, Airborne) encapsulate related sub‑states and enforce transition rules—jumping is only permitted while grounded, avoiding infinite airborne jumps.
Super States for Ground and Air
Create two abstract states that inherit from CharacterState:
public class GroundedState : CharacterState
{
public GroundedState(CharacterStateMachine stateMachine, Character character, string animBoolName)
: base(stateMachine, character, animBoolName) { }
public override void Update()
{
base.Update();
if (Input.GetKeyDown(KeyCode.Space))
stateMachine.ChangeState(character.jumpState);
}
}
public class AirborneState : CharacterState
{
public AirborneState(CharacterStateMachine stateMachine, Character character, string animBoolName)
: base(stateMachine, character, animBoolName) { }
public override void Update()
{
base.Update();
if (rigidBody.velocity.y == 0f)
stateMachine.ChangeState(character.idleState);
}
}
Idle and move states now derive from GroundedState instead of the base class.
Animation Blend Tree for Jump & Fall
Inside the Animator, create a 1D Blend Tree named Jump/Fall controlled by a float parameter YSpeed. Add two motion fields:
- Jump clip (sprites 41–43, sample rate 10)
- Fall clip (sprites 46–48, sample rate 10)
Disable Automate Thresholds and set the threshold for the Fall clip to -1. The tree interpolates between the jump pose (positive speed) and the fall pose (negative speed).
Connect the Blend Tree to the state machine:
- Transition To Jump/Fall with condition
Jump(trigger). - Transition From Jump/Fall back to Idle with condition
Jump(trigger) whenYSpeedreaches 0.
Jump Implemantation
Add jump strength and state references to the Character script:
public float jumpStrength = 12f;
public CharacterStateMachine StateMachine { get; private set; }
public IdleState idleState { get; private set; }
public MoveState moveState { get; private set; }
public JumpState jumpState { get; private set; }
public AirborneState airState { get; private set; }
private void Awake()
{
StateMachine = new CharacterStateMachine();
idleState = new IdleState(StateMachine, this, "Idle");
moveState = new MoveState(StateMachine, this, "Move");
jumpState = new JumpState(StateMachine, this, "Jump");
airState = new AirborneState(StateMachine, this, "Jump");
anim = GetComponentInChildren<Animator>();
rigidBody = GetComponent<Rigidbody2D>();
}
JumpState applies vertical force and switches to AirborneState when descending:
public class JumpState : CharacterState
{
public JumpState(CharacterStateMachine stateMachine, Character character, string animBoolName)
: base(stateMachine, character, animBoolName) { }
public override void Enter()
{
base.Enter();
rigidBody.velocity = new Vector2(rigidBody.velocity.x, character.jumpStrength);
}
public override void Update()
{
base.Update();
if (rigidBody.velocity.y < 0f)
stateMachine.ChangeState(character.airState);
}
}
Updating the Blend Tree Parameter
In the base CharacterState, set the YSpeed parameter every frame so the Animator blends jump and fall correctly:
public virtual void Update()
{
horizontalInput = Input.GetAxisRaw("Horizontal");
character.anim.SetFloat("YSpeed", rigidBody.velocity.y);
}
Now the character transitions from idle/move → jump → airborne → idle, all controlled by the hierarchical state machine.