其他
《ReCore》中Mecanim的重用技巧
一条狗,一只猿猴,一只蜘蛛和一个人携手走进了游戏……然后问题就来了,他们该怎么一起“走”?在《ReCore》项目中涌现了一系列这样有趣的问题。而这些问题直指Unity的动画状态机系统Mecanim。事实上,Mecanim早已备好应对之策,我们与Armature和Microsoft Game Studios密切合作一段时间了,尽可能地利用内置工具来解决这个小故事中的问题。
火箭靴乐趣横生!
对于如此多样的动作组合而言,您如果要想将这么多的状态和转换进行复制,并应用到每一个Joule能使用的武器上——这是不现实的。可喜的是,Mecanim系统在Syncd层(同步层)提供了很好的解决方案。一旦勾选该复选框,Mecanim就会复制一层中的所有状态机布局,您就可以根据需要更换对应的动画剪辑,而不需要维护多个状态。当然,层中无用的状态和转换会产生一点额外的开销。但瑕不掩瑜,这样做带来的好处是显而易见的:bug更少,迭代速度更快,这项功能还是很物有所值的。
同步层中的空闲状态 - 基础层,掏枪动作层和瞄准动作层
Joule与她的一些被称为Corebot的新朋友结伴开始冒险。它们会跟随着她走过这片土地,参与战斗,与环境互动。它们由一套复杂的AI状态机驱动,后者的运行将与相应的Mecanim状态机同步。理想状态下,这种复杂的结构只需要创建一次,并作为现成的“轮子”供所有其他的AI使用。
让人欣慰的是,这些AI变体全都有着近似的特征,它们都要移动、战斗、跟随,然后进入一种非常自然的“空闲”状态。尽管这些动画看起来会截然不同,不过它们基础上建立的逻辑是一样的。这样一来AI就非常适合做运行时重载控制器(Runtime Override Controller)了。该控制器允许您从一个动画剪辑指定到另一个特定Animator的进行重定向。对于《ReCore》的问题,开发团队设置好了狗的动画控制器,然后让猿猴和蜘蛛使用实际应用的动画进行简单地重载。
相同的Animator Controller,相同的“空闲”状态。
通过这一解决方案Armature得以发布他们的游戏,然而这时遇到了一些限制。如果在三个Corebot类型中仅有一个类型需要某个状态,那么这个状态将不得不放在共用的Animator Controller中,因为只有一个控制器。这就会导致一些额外的性能开销,因此这种技术最好在拥有大量共享状态和转换时使用。不过对于Corebot来说,这点开销还是可以接受的。
Armature意识到他们必须谨慎处理动画的转换。因为当在Animator中重载动画时,您将无法更改转换设置。《ReCore》中绝大多数转换都是固定的时长,这就给了Animator调控最终效果更大的控制权。也就是说,对重载动画的剪辑时间进行大量修改,将会导致转换发生在错误的时间。因此当使用实时重载控制器时,尽量不要或少量修改多个剪辑之间的时间间隔。
当该状态机中开始涉及到相当大数量的动画剪辑时,实时重载状态机很快就变得臃肿繁复,以至于很难去修改。《ReCore》通过将代码中的实时重载状态机,按字母进行排序处理才得以解决这个问题。随着剪辑数量的增长,最终编写了一套工具,来根据用户定义的模式对动画剪辑进行重映射。
即使有了所有的引擎核心提升,Armature的数据仍然需要尽可能地进行优化。为了避免在主线程UpdateTransform块中产生上百个骨骼Transform更新,Armature还需要确保所有的模型都导入时,勾选Optimize Game Objects选项。它会合并SkinnedMesh的骨骼,也允许Mecanim直接向图形内存中写入数据,在更新动画时,您就不再需要每帧都更新所有骨骼的GameObject了。Armature同样还需要避免使用OnStateMachineEnter和OnStateMachineExit回调。Mecanim能够检测到这些回调是否存在,如果存在则阻止状态机线程进行评估,而这个操作不是线程安全的。尽管这一操作会占用总动画系统开销的5%,但就是每帧中的这一点点占用,这些对于主机游戏而言都将是不小的问题。
为了加快载入速度,Armature为所有的AI使用了对象池,从而避免实时生成新的实例,然后再根据需要,激活或禁用整个游戏对象。这一操作将导致Animator组件重建所有其内部的状态。这种做法的目标是通过保持禁用Animator,来避免占用过多运行时内存,然后在禁用后完全重置Animator的状态。禁用Animator Controller本身而不禁用整个GameObject就可以避免这种现象。Armature已经不能接受任何额外内存占用或代码,来处理重置Animator的状态了。
在激活期间,分配每个状态使用的所有状态机行为实例,占用了过多的帧时间。这部分内存消耗对于任何需要有其自身实例或数据的状态机行为是必要的,每个Animator都需要一份新的数据拷贝,而不仅仅是每个AnimatorController。为此我们对所有不需要自身状态的行为,添加了 [SharedBetweenAnimators]属性,这样可以节省这部分时间开销。这一属性将会通知Mecanim系统不需要对这部分实例状态进行追踪,仅为相关的类创建一个静态实例就好了。
Unity官方教师培训即将于10月27~28日在上海举办,火热报名中。
Vision VR/AR Summit Asia 2016门票八折优惠即将截止,不要错过。