B站视频
单个状态实现IState接口,只需要维护自身逻辑,状态切换通过状态机触发。
1 2 3 4 5 6 7 8
| public interface IState { void Enter(); void Exit(); void LogicUpdate(); void PhysicsUpdate(); }
|
状态机继承StateMachine
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| using System.Collections; using System.Collections.Generic; using UnityEngine;
public class StateMachine : MonoBehaviour { IState currentState;
public Dictionary<System.Type, IState> stateTable;
void Update() { currentState.LogicUpdate(); }
void FixedUpdate() { currentState.PhysicsUpdate(); }
public void SwitchOn(IState newState) { currentState = newState; currentState.Enter(); }
public void SwitchOn(System.Type newStateType) { currentState = stateTable[newStateType]; currentState.Enter(); }
public void SwitchState(IState newState) { currentState.Exit(); SwitchOn(newState); }
public void SwitchState(System.Type newStateType) { currentState.Exit(); SwitchOn(newStateType); } }
|
每个状态都继承PlayerState,实现自己的逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| using UnityEngine;
public class PlayerState : ScriptableObject, IState { [SerializeField] string stateName;
[SerializeField, Range(0f, 1f)] float transitionDuration = 0.1f;
float stateStartTime;
int stateHash;
protected float currentSpeed; protected Animator animator; protected PlayerController player; protected PlayerInput input; protected PlayerStateMachine stateMachine;
protected bool IsAnimationFinished => StateDuration >= animator.GetCurrentAnimatorStateInfo(0).length;
protected float StateDuration => Time.time - stateStartTime;
public void Initialize( Animator animator, PlayerController player, PlayerInput input, PlayerStateMachine stateMachine ) { this.animator = animator; this.player = player; this.input = input; this.stateMachine = stateMachine; }
void OnEnable() { stateHash = Animator.StringToHash(stateName); }
public virtual void Enter() { animator.CrossFade(stateHash, transitionDuration); stateStartTime = Time.time; }
public virtual void Exit() { }
public virtual void LogicUpdate() { }
public virtual void PhysicsUpdate() { } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| using UnityEngine; using UnityEngine.InputSystem;
[CreateAssetMenu(fileName = "PlayerState_Idle", menuName = "Data/StateMachine/PlayerState/Idle")] public class PlayerState_Idle : PlayerState { [SerializeField] float deceleration = 5f;
public override void Enter() { base.Enter();
currentSpeed = player.MoveSpeed; }
public override void LogicUpdate() { if (input.Move) { stateMachine.SwitchState(typeof(PlayerState_Run)); }
if (input.Jump) { stateMachine.SwitchState(typeof(PlayerState_Jump)); }
if (!player.IsGrounded) { stateMachine.SwitchState(typeof(PlayerState_Fall)); }
currentSpeed = Mathf.MoveTowards(currentSpeed, 0f, deceleration * Time.deltaTime); }
public override void PhysicsUpdate() { player.SetVelocityX(currentSpeed * player.transform.localScale.x); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| using System.Collections; using System.Collections.Generic; using UnityEngine;
public class PlayerStateMachine : StateMachine { [SerializeField] PlayerState[] states; protected Animator animator; PlayerController player; protected PlayerInput input;
private void Awake() { animator = GetComponentInChildren<Animator>();
input = GetComponent<PlayerInput>();
player = GetComponent<PlayerController>();
stateTable = new Dictionary<System.Type, IState>(states.Length);
foreach (PlayerState state in states) { state.Initialize(animator, player, input, this); stateTable.Add(state.GetType(), state); } }
void Start() { SwitchOn(typeof(PlayerState_Idle)); } }
|