一个状态机分为四种状态:状态输入、状态进入、状态更新、状态退出。
案例:现在我们要实现主角的移动、跳跃、攻击之间的状态切换。
1)先把动画控制器做好
2)开始写脚本了,我们先写一个状态基类:定义状态进入、状态更新、状态退出(虚方法,以便在状态子类中重写),初始化(让每个状态脚本获取到animator跟管理者)
1 public enum PlayerState 2 { 3 Idle, 4 Run, 5 Jump, 6 AttackA, 7 AttackB, 8 AttackC 9 } 10 public class PlayerStateBase : MonoBehaviour 11 { 12 public PlayerStateManager manager; 13 public Animator animator; 14 public virtual void Init() 15 { 16 animator = GetComponent<Animator>(); 17 manager = GetComponent<PlayerStateManager>(); 18 } 19 //状态进入 20 public virtual void OnEnter() { } 21 //状态更新 22 public virtual void OnUpdate() { } 23 //状态退出 24 public virtual void OnExit() { } 25 }
2)再写负责状态切换的管理者类
1 public class PlayerStateManager : MonoBehaviour 2 { 3 Dictionary<Type, PlayerStateBase> states = new Dictionary<Type, PlayerStateBase>(); 4 PlayerStateBase currentState; 5 private void Awake() 6 { 7 AddState<PlayerStateIdle>(); 8 AddState<PlayerStateRun>(); 9 AddState<PlayerStateJump>(); 10 AddState<PlayerStateAttackA>(); 11 AddState<PlayerStateAttackB>(); 12 AddState<PlayerStateAttackC>(); 13 ChangeState<PlayerStateIdle>(); 14 } 15 private void AddState<T>() where T : PlayerStateBase 16 { 17 PlayerStateBase state = gameObject.AddComponent<T>(); 18 state.Init(); 19 states.Add(typeof(T), state); 20 } 21 public void ChangeState<T>() where T : PlayerStateBase 22 { 23 if (currentState != null) 24 currentState.OnExit(); 25 currentState = states[typeof(T)]; 26 currentState.OnEnter(); 27 } 28 private void Update() 29 { 30 if (currentState != null) 31 currentState.OnUpdate(); 32 } 33 }
3)然后是针对每种状态创建一个脚本
a. Idle 站立状态
1 public class PlayerStateIdle : PlayerStateBase 2 { 3 private float h; 4 private float v; 5 6 public override void OnEnter() 7 { 8 animator.SetBool("Run", false); 9 } 10 public override void OnUpdate() 11 { 12 if (Input.GetKeyDown(KeyCode.Alpha1)) 13 { 14 manager.ChangeState<PlayerStateAttackA>(); 15 return; 16 } 17 if (Input.GetKeyDown(KeyCode.Alpha2)) 18 { 19 manager.ChangeState<PlayerStateAttackB>(); 20 return; 21 } 22 if (Input.GetKeyDown(KeyCode.Alpha3)) 23 { 24 manager.ChangeState<PlayerStateAttackC>(); 25 return; 26 } 27 if (Input.GetKeyDown(KeyCode.Space)) 28 { 29 manager.ChangeState<PlayerStateJump>(); 30 return; 31 } 32 33 h = Input.GetAxis("Horizontal"); 34 v = Input.GetAxis("Vertical"); 35 if (h != 0 || v != 0) 36 { 37 manager.ChangeState<PlayerStateRun>(); 38 return; 39 } 40 } 41 }
b. Run 奔跑状态
1 public class PlayerStateRun : PlayerStateBase 2 { 3 private float h; 4 private float v; 5 private Vector3 direction; 6 private CharacterController cc; 7 8 public override void Init() 9 { 10 base.Init(); 11 cc = GetComponent<CharacterController>(); 12 } 13 public override void OnEnter() 14 { 15 animator.SetBool("Run", true); 16 } 17 public override void OnUpdate() 18 { 19 if (Input.GetKeyDown(KeyCode.Alpha1)) 20 { 21 manager.ChangeState<PlayerStateAttackA>(); 22 return; 23 } 24 if (Input.GetKeyDown(KeyCode.Alpha2)) 25 { 26 manager.ChangeState<PlayerStateAttackB>(); 27 return; 28 } 29 if (Input.GetKeyDown(KeyCode.Alpha3)) 30 { 31 manager.ChangeState<PlayerStateAttackC>(); 32 return; 33 } 34 if (Input.GetKeyDown(KeyCode.Space)) 35 { 36 manager.ChangeState<PlayerStateJump>(); 37 return; 38 } 39 h = Input.GetAxis("Horizontal"); 40 v = Input.GetAxis("Vertical"); 41 direction.Set(h, 0, v); 42 if (h == 0 && v == 0) 43 { 44 manager.ChangeState<PlayerStateIdle>(); 45 return; 46 } 47 direction = Camera.main.transform.TransformDirection(direction); 48 direction.y = 0; 49 transform.rotation = Quaternion.LookRotation(direction); 50 cc.SimpleMove(direction * 3.0f); 51 } 52 }
c. Jump 跳跃状态
1 public class PlayerStateJump : PlayerStateBase 2 { 3 private Vector3 startPos; 4 private Vector3 endPos; 5 bool isJump; 6 public override void OnEnter() 7 { 8 startPos = transform.position; 9 endPos = startPos + transform.forward * 5.0f; 10 isJump = true; 11 animator.SetTrigger("Jump"); 12 } 13 public override void OnUpdate() 14 { 15 AnimatorStateInfo info = animator.GetCurrentAnimatorStateInfo(0); 16 if (info.IsName("Jump")) 17 return; 18 if (isJump && info.normalizedTime > 0.2f) 19 { 20 transform.position = Vector3.Lerp(startPos, endPos, info.normalizedTime); 21 if (info.normalizedTime > 0.7f) 22 isJump = false; 23 } 24 if (info.normalizedTime > 0.9f) 25 manager.ChangeState<PlayerStateIdle>(); 26 } 27 }
d. Attack 攻击状态(这里只展示一个,另外两个是一样的)
1 public class PlayerStateAttackA : PlayerStateBase 2 { 3 public override void OnEnter() 4 { 5 animator.SetTrigger("AttackA"); 6 } 7 public override void OnUpdate() 8 { 9 AnimatorStateInfo info = animator.GetCurrentAnimatorStateInfo(0); 10 if (info.IsName("AttackA")) 11 return; 12 13 if (info.normalizedTime > 0.9f) 14 manager.ChangeState<PlayerStateIdle>(); 15 } 16 }