聊聊状态机——Unity探索笔记之动画解决方案(一)

聊聊状态机——Unity探索笔记之动画解决方案(一)

1. 总体介绍

要做个Demo,结果又碰到个比较生的领域——怎么做出我想要的动画状态机,这里面包括了几个我不是很懂的方面:如何划分动作使用情形?需不需要用Blend Tree及怎么使用?动作间的过度怎么搞?变量的一些细微区别?


2. 拿到一些Animation Clip,如何在不知道游戏规则的前提下,分门别类?

下图是我拿到Animation Clip,开始看到它们的时候我的内心是崩溃的(此处应有表情包,可惜我没有……)。

(图1)

我感觉这些应该是在某种游戏规则下才能搞定的分类,如战斗或非战斗两大类,可我目前还没确定游戏规则(希望在做出最小系统的情况下,用系统套规则,而不是直接上特定的系统,这样的好处是将来换个规则我还能用大部分系统)。

所以目前情况是没有策划案就要写代码,怎么办?运用“奥卡姆剃刀”原理(如无必要,勿增实体),首先分为人是生是死两大类,在生的情况下,再根据传统游戏经验,分为持续动作和非持续动作两大类,分到这里就可以了,再下去就是具体游戏规则的调用了。

注意:

1) 这里写的很简略,但当时做的时候非常纠结,心里老是预设游戏规则,一直陷在战斗和非战斗两大类里,折腾来折腾去没有头绪。

2) 我也找了教程看其他人怎么做的,可惜那些动作都非常有自我针对性,一般都是拿对称动作举例,如向左跑、向右跑,后来只好请教别人,知道了“奥卡姆剃刀”后,才想出这个分类法。

3. 知道分类后,判断需不需要Blend Tree?

下图是我提出的分类法实例展示。

(图2)

虽然在网上找的资料对我分类动作没有帮助,不过倒把Blend Tree给学习了一下。

持续性动作包括很多:Walk,Run,Dash很明显是因为速度不同(典型的控制单一变量)而产生的不同动作,所以非常适合用Blend Tree(1D)来实现,最终我又把持续性的动作分为了Stay和Move两种,一种保持在原地,一种需要移动。

注意:

1) 究竟要不要把Idle算到Move这个Blend Tree里?

纠结这点是因为很多视频教程都算进去了,但是他们和我的动作区别是,我这边除了速度为零的Idle,还有Defend等这些动作,这些速度也为零,但除此之外和Walk就没有共同点了,我尝试了硬把Defend放进去,把Blend Tree Type设置为2D Freeform Cartesian(横纵坐标不代表方向,而是自设的变量),结果复杂无比不说,动画也很奇怪,最后就把Defend、Upset、Victory、Idle放到Stay这个子状态机里。

2) 关于Blend Tree Type的一些解释

1D——只根据一个变量就能改变的几种动作,通常是速度或角度。

2D Simple Directional——可以在众多方向上设置动作,但是每个方向只能代表一个动作,另外千万不要认为只能在横纵坐标上设置动作。

2D Freeform Directional——也可以在多方向上设置动作,但每个方向可以代表多个,就是说每个方向是一个1D。

2D Freeform Cartesian——横纵坐标的意义可以自己设置,也可以有多方向,当时我为了把Idle、Defend合进去,就把横纵标设为speed,纵坐标设为StayType。

Direct——首先每个动作要设置一个变量,每个动作根据这个变量的权重来互相影响。

注意一个小问题:设置的变量只能用float型,我也不知道为什么。

4. 子动作状态机之间的变量设置及连接

变量设置如下图

(图3)

解释(从上到下):

根据人物是否活着分类,肯定需要个布尔值来判断;图2中,Stay子状态机里有多个动作,要区分只能用一个int型了;不能持续的动作,只触发一次用trigger,区别触发哪个也是用int;最后一个speed用来在Blend Tree里控制不同的移动。

1) 动作之间的连接

从图2大方面说:一开始必定是触发Stay里的默认状态Idle;任何状态都会触发Die或OnceAction里的动作,都需要两个条件(isLive和triggerOnceAction),它们只是isLive条件不同;Stay和Move里动作的切换通过判断speed是不是为0.001;最后Die和Stay直接一定要连接(isLive==true),否则从复活后就回不到默认Idle了。

注意:

a) 从Any State到Die,除了判断isLive==FALSE之外,一定要加上triggerOnceAction这个条件,否则人物死亡会一直重复动作Die,因为Any State状态里也包括Die,就是说死亡后又判断条件,结果符合就继续死亡动画,用trigger来控制,我的理解是trigger能保证动作只能执行一次。

b) 为什么speed==0.001,而不是0?因为误差,这个小数点后几位是自己调出来的。

2) 从小的方面来说

(图4)

(图5)

为什么有的动作用橘色表示?

首先整个状态机必须只有个一个默认状态,这个默认状态用橘色表示,图2中,这个动作在Stay-Idle上,所以图5中,就算动作通过trigger触发了一次后也可以自动回到Idle上。

为什么图4,图5中有的transition的线是深橘色,并且有多个箭头?

深橘色表示连接到默认动作,但是如果外部有变量控制得话,默认会不起效果,如图5中的状态;多个箭头表示这上面有多个transition或有transition脚本。

图4中,从Entry到每个动作、每个动作间的transition就是stayState的切换;从每个动作到Exit,条件就变为speed>0.001。

为什么图4出Stay状态机的条件仅仅是速度?

因为这个圈子包括了整个状态机的默认状态,打破这个平衡就两个条件:要不去执行OnceAction里某个动作,但是执行完了还会回来;要不就是变到另一种持续状态,速度不为零的某个运动状态,然后保持住。前一种状态有Any State来保证了,所以这里就不需要再写。

图5中,从Entry到每个状态,只需要判断OnceActionType的int值,出去不用做什么判断,因为(再强调一遍)这里的动作都是用trigger触发的,注意这里,如果没有连接各个动作到Exit的话(这里的transition不需要有条件),执行一遍这里动作就回不到默认Idle动作了,因为没有出口。

到这里,(其实中间的每一步都和别人讨论了很久)一个比较复杂(不是轴对称动作)的状态机就完成了。



三个星期没更新专栏真是抱歉了呢~Blender上手虽然比较快,但是我离熟悉还有段距离。

给你们看看我的成果,一个low-poly的牛仔,讲真,蛮自豪的呢~也就新手班排名前五吧!


最后想说点政治不正确的话

开专栏了这么久,会遇到一些热心网友给我的各种留言和私信,我其实挺感激的,因为代表了他们对你的信任,中间有我能回答的我第一时间看到后都会回答,希望能力所能及的帮上忙,可是呢,总会有不太开心的情况出现。

第一种比较常见的就是,没头没脑的问你“我没实现XXX这是为什么”,问出这种问题的,通常是学生或刚工作没多久的人,首先我建议你读一本书叫《学会提问》,同时请问你,不知道你任何操作步骤的情况下我怎么回答你,什么都不描述,你就是在知乎发个问题,请个高手来回答,我估计喷你的可能性会高一些吧。我也关注了一些大神回答一些比较初级的问题,能回答的都是问题本身是有理有据,有详细的步骤或代码描述的。

第二种,我不回答用Google能搜出来的问题,因为没什么好回答的,如“请问XXX是什么/怎么配置/怎么实现?”

第三种,目前碰到单例,十分让我不爽,一定要我当时帮他解决某某问题,还说这问题一定对我来说很简单,我就问你你的判断标准是什么?!你怎么知道有我空没空呢?!我客气得表示目前没空,就不理我+把我删了,真没礼貌呢小伙子!

我以前在知乎或者某学习研究群里也问过问题,最终的结果是别人能帮你的真的很少,就算帮能不能帮到点上也很愁人,因为要描述清楚问题怎么出现的,出现在哪,整个问题的流程是个需要功力的活,往详细的讲,大家的注意力和精力很宝贵,没空看,往简略的讲,大家最多给你个可能的方向,怎么解决还是得靠自己。

目前,我在学习中也遇到很多问题,往近了说就是学Blender的时候,各种对新手来说很神奇的坑,我只有靠我自己的英文水平和翻Google的能力,那种实在太特定并且复杂的问题,推荐当面找熟人问吧!

我目前还在学习英文,虽然体力不如以前,但是分析能力上了很大一个台阶,感觉英语学起来不是那么枯燥难懂了,因为需要不停的去用、去练,当我能听懂YouTube上的视频时,成就感爆棚啊!~


好了,不小心扯了这么多,本篇就到这里,欢迎您的收看,我们下次再见!~

编辑于 2016-12-13

文章被以下专栏收录