游戏AI:(二)状态机

一个状态机分为四种状态:状态输入、状态进入、状态更新、状态退出。

案例:现在我们要实现主角的移动、跳跃、攻击之间的状态切换。

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 }
posted @ 2022-02-14 11:48  番茄玛丽  阅读(295)  评论(3编辑  收藏  举报