当前位置:   article > 正文

unity animator 动画 结束后保持位移_Unity 系列:游戏状态机,有效地管理游戏状态。

unit 按钮悬浮动画结束不变

v2-8d3cd007713f8d3f68164e32429746de_1440w.jpg?source=172ae18b

更优雅的阅读 :

安全提示 - Sina.lt短网址

欢迎你在Notion里和我们互动,如有疑问也可评论提出。

前提介绍

游戏编程的开发中比较复杂的是状态处理(State Management)。参考以下片段:

  1. private void HandleInput() {
  2. if(Input.GetKeyDown(KeyCode.B){
  3. vY = JUMP_VELOCITY;
  4. setGraphics(IMAGE_JUMP);
  5. }
  6. }

按照代码所示,如果玩家按下B键,就会跳起,但这里存在一个(bug),如果玩家是不断的按下B键或是长按B键,那么游戏角色就会不断地往上飞。

这时候我们可以在代码中添加加一个旗标(Flag),如下:

  1. // isJumping 是记录玩家是否跳起的旗标
  2. bool isJumping = false;
  3. private void HandleInput() {
  4. if(Input.GetKeyDown(KeyCode.B) && !isJumping){
  5. // 跳起来。。。
  6. isJumping = true;
  7. }
  8. }
  9. // 省略重置 isJumping = false

但是这个解决方案很快就会在之后需要增加的功能上遇到瓶颈,比如如果要做双跳(Double Jump)怎么办?还是要蹲下怎么办?这个时候我们就会选用状态模式 (State Pattern)。

v2-e28803971dcef2e65469dbed7b1e8201_b.jpg

这种设计也称状态机(State Machine),需要具备以下几种自定规则:

  1. 状态(State)是有限,并且可被定义清楚。
  2. 在任意一个时刻只能存在一种状态。
  3. 玩家的输入引发状态的变化。

状态机最佳实践

  • 将短暂性和持久性的定义与参数区分开。

v2-397e1a9756e6510e588c12a916a34cec_b.jpg
上图所示的游戏中已解锁的枪种是一种持久性参数,地牢内得到的枪是短暂性参数
  • 短暂性参数在重新进入游戏后需要重置。
  • 持久性参数在进入状态时会先对参数做不可变动的原始备份(immutable copy),然后状态结束时再更新参数(参数可能会与原始备份不一样)。
  • 避免在状态和状态机内使用旗标(Flag),需要时制作新的状态。

用Unity Animator做可视状态机

接下来,我想着重介绍如何在Unity内,不用任何外部代码,架构起一个可视化的状态机。

在Unity技术的工作面试时,难免会慌手慌脚,所以如果用这个把游戏内部状态和视觉状态连接在一起,让你轻松有条理的地解释你的思维模式,让面试官大跌眼镜。(比如制作《糖果传奇》的King公司的其中一个技术面试题目就是当场制作宝石方块游戏,如果用状态机基本都会加分。)

v2-0dac6d9bcebdfd290f0074eb84fb9303_b.jpg

我们将会自定义Unity动画系统里的状态机,同时额外添加一些功能,让我们能够更好的利用它。

事前准备

  1. 在场景(Scene)内制造一个GameObject,然后加上Animator组件。
  2. 在项目文件夹内制造一个AnimatorController并把它链接到上述Animator中。
  3. 打开Window→Animator→Parameters然后制造一个叫Next的Trigger。

如何自定义

首先编写一个继承 StateMachineBehaviour 的对象,这样我们就可以创建自定义状态,为状态机提供更简洁的界面,并启用可视化调试。这个新的对象我们可称为 LogicalStateMachineBehaviour,然后参考以下编码。

  1. protected abstract void OnLogicalStateEnter(GameObject gameObject);
  2. protected abstract void OnLogicalStateUpdate(GameObject gameObject);
  3. protected abstract void OnLogicalStateExit(GameObject gameObject);
  4. protected abstract void ResetState(GameObject gameObject);
  5. public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
  6. {
  7. ResetState(ActiveAnimator.gameObject);
  8. OnLogicalStateEnter(ActiveAnimator.gameObject);
  9. }
  10. public override void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
  11. {
  12. OnLogicalStateUpdate(ActiveAnimator.gameObject);
  13. }
  14. public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
  15. {
  16. OnLogicalStateExit(ActiveAnimator.gameObject);
  17. }

为了方便调试,我们可以用我们已经做好的Trigger来做一个叫 Continue 的方法,可以迫使状态机进入下一个状态。

  1. protected Animator ActiveAnimator;
  2. protected void Continue()
  3. {
  4. if (!ActiveAnimator) return;
  5. ActiveAnimator.SetTrigger(Animator.StringToHash(Next));
  6. }
  7. public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
  8. {
  9. ActiveAnimator = animator;
  10. ResetState(ActiveAnimator.gameObject);
  11. ActiveAnimator.ResetTrigger(Animator.StringToHash(Next));
  12. OnLogicalStateEnter(ActiveAnimator.gameObject);
  13. }

通过在编辑器中为其创建 一个Inspector 按钮,可以更进一步地改良状态机。就我个人而言,我会直接使用Odin Inspector创建一个,但如果希望使用本机解决方案,也可以在这里找到。

瞧! 在项目中可使用的简单可视状态机,也可以在这里找到代码。

没有意外的话,每礼拜更新。

v2-7c3a246407d43404a43d3107a2d35fbc_b.jpg
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/114625?site
推荐阅读
相关标签
  

闽ICP备14008679号