角色动作控制接口的设计
http://cache.baiducontent.com/c?m=9f65cb4a8c8507ed4fece7631046893b4c4380146d8a864e2381cf15d6735b36163bbca62720120ecec77c7a52f31203b9b66d2f6b1427c3c094ce1d9defc179789f27432a5ad91f069644ef9d49759f7dc54de8df0ee7cde733e3ff8f85840806dd527526ddb59c5b7003ca18e71541b2fbc65f152e10b8ef3a72ff287529882331e51ba2bf61285cdcaaca5d3cd42da47610e7f469f66952e140f5480c2534b74cc61f50566aa61079&p=8b2a970a86cc41ac5abbce3d51518d&newp=c974c10486cc41ac5aa4882d021483231610db2151d4da116b82c825d7331b001c3bbfb42323130ed0cf7f6d0aae4e58e1f03079370923a3dda5c91d9fb4c57479c13b&user=baidu&fm=sc&query=http%3A//blog%2Ecodingnow%2Ecom/2008/02/animation_interface%2Ehtml&qid=ac23e1e8000037b9&p1=1
最近一段时间工作的侧重点转移,我从服务器的设计转到客户端这边来。
自从最底层引擎的架构完成后,我们的客户端留了两个人在全职写代码,一个人负责 C 层面的 3d engine ,另一个人负责设计高层面的应用接口,并在此基础上完成游戏 demo 。
从程序员角度上看,人都是相当不错的。要设计有设计能力,有编码有编码能力。代码审美观上大家也很一致,所以我们几个人一起工作的很愉快。
唯一美中不足的是,做 demo 的程序员游戏接触的太少,所以在操作手感调整方面有点相形见绌。需要经常性的跟负责战斗动作系统的策划沟通调整。而策划毕竟不太明白程序背后再用怎样的方法实现,最终还是有点不尽人意。如果有足够的时间,倒总是能调整好。不过现在项目进度比原来计划有所提前,我就直接参于进来参合了。加快这方面的开发进度。
上周花了几天时间通读了所有相关的代码,整体设计还是不错的。不过这两天用下来,还是觉得有点别扭。
我希望游戏能有比较流畅的操作感,可以灵活的操作游戏角色做出各种动作,包括战斗时的腾挪,组合技能等。写了不少脏代码尝试我希望的手感时,终于发现一些设计问题了。
所谓操作手感,其实不仅仅在于对输入设备的控制。它包括输入设备(键盘鼠标)的数据采集分析和输出设备(显示器)做的视觉反馈。希望获得良好的手感,就一定要准确的控制每个动作的视觉反馈。
这里,3d engine 的角色动作控制接口设计就很重要。需要做的简单易用,又能保持健壮。
其实到了这个层面,已经不关乎 engine 是 2d 还是 3d 的事了。只是 3d 在精灵的空间位置和摄象机的控制上,比 2d 多出一个自由度来,运用要复杂很多。保持接口简洁就更为困难了。
今天暂时先谈谈简单的部分,如何控制角色的动作播放。(2d engine 也用的到)
本质上,图象 engine 是一帧帧渲染角色精灵的。渲染这个操作必须被隐藏起来为应用层不可见。3d engine 有更高的表现力,比如可以做骨骼动画,以此做动作间融合。这些应该归到渲染层面做。绝大多数情况下,应用层不应该关心。
以前,我们的引擎接口提供了方法设置角色当前应该播放的动画系列。其参数有播放指定次数还是循环播放。仔细考虑,其实不太合理。
实际应用中,将角色动画播放指定次数完成后,引擎应该做什么?让精灵的画面停留在最后一帧吗?显然是不合理的。如果精灵全部是模拟的活物的话,正常状态下它们永远都应该在运动。
接口设计的健壮性应在于,我们无论怎样调用,都不应出现错误的状态。角色的画面静止就是这样一种错误状态。
我曾经想实现一个很复杂的动画序列驱动引擎,相信很多人也干过。就是在引擎中支持播放一个列表的动画序列,并可以为每个节点附加控制器,决定是反复播放还是单次播放。并让控制器可以控制角色的内部属性。甚至这个播放列表还可以是树状结构。
这个想法大约在 01 年的时候我曾经实现出来,发现并不好用。它完全违背了 KISS 原则。越是将功能做的全面,就越难二次扩展。用它的程序员也很难控制内部细节。(btw, 我在去年初放弃 boost jam 而换用 gmake 做 build tool 也是因为类似的理由)
如果真正的考虑,其实我们所有需要控制的角色动画其实都可以分解成这样一种元动画:即一组循环动画前置一个进入这个动画的引导动画序列。比如坐下,就是从直立状态经历一个坐下的过程,最终让角色一直持续在坐着的状态。又比如人物站立时,做一些花哨的手势,也应在完成之后,又恢复成普通的站立形态。
至于前导动画系列怎么和之前的角色状态衔接,我们可以交给引擎的底层考虑。而一切复杂动作皆可被如上分解。及时有一些特例,也可以做一些变通。比如最终的循环播放阶段可以只有一帧,那么角色就静止在那里了。
如此设计,我们并不需要让引擎提供接口去控制一组动画需要播放一次还是多次,或是不停播放下去。对一个动画元组永远都只有一个播放指令。
那么,交互式的动画如何控制?
这需要增加一个调用参数。虽然,一组动画总可以无限播放,但引导这段动画的前序动作却只有一次。使用回调函数的设计是很难看的。比较漂亮的方法是可以指定前序动作的时间长度。因为这个时间往往跟逻辑紧密相关,比如挥刀劈砍,完成整个动作的时间需要程序控制精准,而不能由着美术人员制作。许多动作的播放时间长度都会随着游戏开发不断调整,我们不可能每次调整都去要求美术人员改变。所以引擎应该内部支持动画序列的播放时长,按程序运行时参数要求拉伸或压缩。
对于循环播放的动画有时也有必要精确控制每个循环的时长。最典型的例子是跑步动画。当我们设定为角色一秒跑动两步跨过 10 米时,就需要精确控制 0.5 秒播放一个跑步循环。这样的控制可以避免视觉上感觉角色在虚拟世界的地面上滑动。
总的来说,游戏逻辑应该和画面脱离。时间控制是放在逻辑上的,而画面表现则配合逻辑实现。