- Home /
How to refactor nested switches
Hey guys. I need to update the character animation depending on the state (Idle, Walk, Interact) and the orientation (NE, E, SE, SW, W, NW). Animations are "IdleNE", "WalkW", etc.
In order to manage it I would like to have something similar to
private void UpdateAnimation()
{
spriteAnimator.Play(currentState.toString() + currentOrientation.toString())
}
but I came up with nasty nested switches instead...
private void UpdateAnimation()
{
switch (currentState)
{
case PlayerState.Idle:
switch (currentOrientation)
{
case PlayerOrientation.E:
spriteAnimator.Play(idleE);
break;
case PlayerOrientation.NE:
spriteAnimator.Play(idleNE);
break;
case PlayerOrientation.NW:
spriteAnimator.Play(idleNW);
break;
case PlayerOrientation.W:
spriteAnimator.Play(idleW);
break;
case PlayerOrientation.SW:
spriteAnimator.Play(idleSW);
break;
case PlayerOrientation.SE:
spriteAnimator.Play(idleSE);
break;
}
break;
case PlayerState.Walk:
{
switch (currentOrientation)
{
case PlayerOrientation.E:
spriteAnimator.Play(walkE);
break;
case PlayerOrientation.NE:
spriteAnimator.Play(walkNE);
break;
case PlayerOrientation.NW:
spriteAnimator.Play(walkNW);
break;
case PlayerOrientation.W:
spriteAnimator.Play(walkW);
break;
case PlayerOrientation.SW:
spriteAnimator.Play(walkSW);
break;
case PlayerOrientation.SE:
spriteAnimator.Play(walkSE);
break;
}
break;
}
break;
case PlayerState.Interact:
switch (currentOrientation)
{
case PlayerOrientation.E:
spriteAnimator.Play(interactE);
break;
case PlayerOrientation.NE:
spriteAnimator.Play(interactNE);
break;
case PlayerOrientation.NW:
spriteAnimator.Play(interactNW);
break;
case PlayerOrientation.W:
spriteAnimator.Play(interactW);
break;
case PlayerOrientation.SW:
spriteAnimator.Play(interactSW);
break;
case PlayerOrientation.SE:
spriteAnimator.Play(interactSE);
break;
}
break;
default:
break;
}
}
I'm not sure there is any proper alternative to do it, so any idea would be really welcome. Thanks in advance!
Answer by Hellium · Apr 29, 2021 at 06:42 PM
I see at least two possibilities (NOT TESTED)
private string[,] animations = new string[3,6]; // 3 states & 6 orientations
private void FillAnimationArray()
{
animations[PlayerState.Idle, PlayerOrientation.E] = idleE;
animations[PlayerState.Idle, PlayerOrientation.NE] = idleNE;
animations[PlayerState.Idle, PlayerOrientation.NW] = idleNW;
animations[PlayerState.Idle, PlayerOrientation.W] = idleW;
animations[PlayerState.Idle, PlayerOrientation.SE] = idleSE;
animations[PlayerState.Idle, PlayerOrientation.SW] = idleSW;
animations[PlayerState.Walk, PlayerOrientation.E] = walkE;
animations[PlayerState.Walk, PlayerOrientation.NE] = walkNE;
animations[PlayerState.Walk, PlayerOrientation.NW] = walkNW;
animations[PlayerState.Walk, PlayerOrientation.W] = walkW;
animations[PlayerState.Walk, PlayerOrientation.SE] = walkSE;
animations[PlayerState.Walk, PlayerOrientation.SW] = walkSW;
animations[PlayerState.Interact, PlayerOrientation.E] = interact;
animations[PlayerState.Interact, PlayerOrientation.NE] = interactNE;
animations[PlayerState.Interact, PlayerOrientation.NW] = interactNW;
animations[PlayerState.Interact, PlayerOrientation.W] = interactW;
animations[PlayerState.Interact, PlayerOrientation.SE] = interactSE;
animations[PlayerState.Interact, PlayerOrientation.SW] = interactSW;
}
private void UpdateAnimation()
{
spriteAnimator.Play(animations[currentState, currentOrientation]);
}
public class StateAnimationCollection
{
private Dictionary<PlayerOrientation, string> animations;
public StateAnimationCollection(
string eAnimation, string neAnimation, string seAnimation,
string wAnimation, string nwAnimation, string swAnimation
)
{
animations = new Dictionary<PlayerOrientation, string>()
{
{ PlayerOrientation.E, eAnimation }
{ PlayerOrientation.NE, neAnimation },
{ PlayerOrientation.SE, seAnimation },
{ PlayerOrientation.W, wAnimation },
{ PlayerOrientation.NW, nwAnimation },
{ PlayerOrientation.SW, swAnimation }
};
}
public string this[PlayerOrientation orientation] => animations[orientation];
}
// Could be simplified by using a Dictionary<PlayerOrientation, string> instead of a StateAnimationCollection
private Dictionary<PlayerState, StateAnimationCollection> animations = new Dictionary<PlayerState, StateAnimationCollection>
{
{ PlayerState.Idle, new StateAnimationCollection(idleE, idleNE, idleSE, idleW, idleNW, idleSW) },
{ PlayerState.Walk, new StateAnimationCollection(walkE, walkNE, walkSE, walkW, walkNW, walkSW) },
{ PlayerState.Interact, new StateAnimationCollection(interactE, interactNE, interactSE, interactW, interactNW, interactSW) },
};
private void UpdateAnimation()
{
spriteAnimator.Play(animations[currentState][currentOrientation]);
}
Answer by andrew-lukasik · Apr 29, 2021 at 08:08 PM
I suggest state machines. Even the simplest ever and half-broken ones will fold this spaghetti code into something you will be able to understand, maintain, reuse and expand on.
Your MonoBehaviour, responsible for changing states and calling their Tick
update methods:
PlayerAnimationController.cs
using System.Collections.Generic;
using UnityEngine;
public class PlayerAnimationController : MonoBehaviour, IPawnAnimationController
{
IAnimationControllerState _currentState = null;
[SerializeField] Animator _spriteAnimator = null;
Animator IPawnAnimationController.Animator => _spriteAnimator;
[SerializeField] Orientation _currentOrientation = Orientation.W;
Orientation IPawnAnimationController.Orientation => _currentOrientation;
[SerializeField][Range(-1f,1f)] float _horizontal = 0;
[SerializeField][Range(-1f,1f)] float _vertical = 0;
const float k_tick_rate = 1f / 30f;
void OnEnable ()
{
_currentState = new IdleState{ Owner=this };
InvokeRepeating( nameof(UpdateAnimation) , time:k_tick_rate , repeatRate:k_tick_rate );
}
void Update ()
{
_horizontal = Input.GetAxis("Horizontal");
_vertical = Input.GetAxis("Vertical");
}
void UpdateAnimation ()
{
// tick current state:
_currentState.Tick();
// test for state transitions:
var inputAxis = new Vector2{ x=_horizontal , y=_vertical };
if( _currentState is IdleState )
{
if( inputAxis.sqrMagnitude>0 )
_currentState = new WalkState{ Owner=this };
else if( Input.GetKey(KeyCode.E) )
_currentState = new InteractState{ Owner=this };
}
else if( _currentState is InteractState )
{
if( inputAxis.sqrMagnitude>0 )
_currentState = new WalkState{ Owner=this };
}
else if( _currentState is WalkState )
{
if( !(inputAxis.sqrMagnitude>0) )
_currentState = new IdleState{ Owner=this };
}
}
#if UNITY_EDITOR
void OnDrawGizmos ()
{
if( _currentState!=null )
UnityEditor.Handles.Label( transform.position , $"({_currentState.GetType().Name})" );
}
#endif
}
Minor types:
Orientation.cs
public enum Orientation { E , NE , NW , W , SW , SE }
IAnimationControllerState.cs
public interface IAnimationControllerState
{
IPawnAnimationController Owner { get; set; }
abstract void Tick ();
}
IPawnAnimationController.cs
public interface IPawnAnimationController
{
Animator Animator { get; }
Orientation Orientation { get; }
}
Your actual states that make the animator calls, and describe what happens in this state:
IdleState.cs
public struct IdleState : IAnimationControllerState
{
public IPawnAnimationController Owner { get; set; }
static Dictionary<Orientation,string> animationStates = new Dictionary<Orientation,string>{
{ Orientation.E , "idle E" } ,
{ Orientation.NE , "idle NE" } ,
{ Orientation.NW , "idle NW" } ,
{ Orientation.W , "idle W" } ,
{ Orientation.SW , "idle SW" } ,
{ Orientation.SE , "idle SE" } ,
};
public void Tick () => Owner.Animator.Play( animationStates[Owner.Orientation] );
}
WalkState.cs
public struct WalkState : IAnimationControllerState
{
public IPawnAnimationController Owner { get; set; }
static Dictionary<Orientation,string> animationStates = new Dictionary<Orientation,string>{
{ Orientation.E , "walk E" } ,
{ Orientation.NE , "walk NE" } ,
{ Orientation.NW , "walk NW" } ,
{ Orientation.W , "walk W" } ,
{ Orientation.SW , "walk SW" } ,
{ Orientation.SE , "walk SE" } ,
};
public void Tick () => Owner.Animator.Play( animationStates[Owner.Orientation] );
}
InteractState.cs
public struct InteractState : IAnimationControllerState
{
public IPawnAnimationController Owner { get; set; }
static Dictionary<Orientation,string> animationStates = new Dictionary<Orientation,string>{
{ Orientation.E , "interact E" } ,
{ Orientation.NE , "interact NE" } ,
{ Orientation.NW , "interact NW" } ,
{ Orientation.W , "interact W" } ,
{ Orientation.SW , "interact SW" } ,
{ Orientation.SE , "interact SE" } ,
};
public void Tick () => Owner.Animator.Play( animationStates[Owner.Orientation] );
}
Your answer
Follow this Question
Related Questions
Tried to make a List.>(), it doesn't work, so how do I do that? 1 Answer
nested triggers and an OnMouseDown event 0 Answers
Assign 1st array of materials into multiple gameobjects. 0 Answers
Case Statement Error? 3 Answers
How to get smooth 4 directional movement in top down without diagonal movement? 2 Answers