• 游戏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 }
  • 相关阅读:
    c语言 数组合并
    c++ 静态函数
    c++ 多继承 公有,私有,保护
    c++ 多继承 public
    stat用法:获取文件对应权限的数字
    sublime text3 (Mac) 快捷键
    c++ 多继承
    C++ 在继承中虚函数、纯虚函数、普通函数,三者的区别
    更换主机后SSH无法登录的问题
    ssh 连接不同无线网且IP以及用户名都相同
  • 原文地址:https://www.cnblogs.com/tomatokely/p/15891931.html
Copyright © 2020-2023  润新知