« 记录几个近期碰到的 bug | 返回首页 | 游戏的帧率控制 »

负反馈系统在模型动画控制中的应用

最近在解决 3d engine 接口的一处设计问题。我们知道,在 3d 游戏中,engine 的接口设计往往比性能更加重要,这决定了 engine 是否好用,甚至是否能用。简单的功能堆砌不是 engine 。

目前我想弄清楚的一个技术点就是,当模型置入场景后,如果播放的动画本身有位移,引擎应提供怎样的接口,让使用者最为方便的表达他意图。

具体到一个问题点来说,当我们的美术人员制作了一组四足动画奔跑的动画后,怎么在游戏中最自然的表现出来。

就我有限的眼界所知,有许多人在制作 3d engine 时是这么规定的:美术需要把动画调整成原地奔跑的样子。(或者换个方式,在从制作软件中导出时,让原本具有位移的动画变为原地移动)如此,就可以在最终表现时,自由控制播放的速度。

但是稍加思考,就知道这样做导致的图象表现和我们期待的样子是有偏差的。下面让我们来分析一下。

生物在奔跑的时候,重心的移动线速度不可能保持一个完全均匀的速度。脚掌在着地的那一刹那,产生了极大的推力,身体拥有了最大的加速度。但速度本身却是在腾空那一刻加到最大,而到下次着地的过程中是做一个减速运动的。另外,重心也非保证完全平行于地面运动而是有上下起伏。

硬把跑步的动作拉回原地,而在用的时候拉成匀速运动是不准确的。如果美术提供的素材足够真实(比如使用了动作捕捉器)。那么这些变了形的动画信息在游戏中就会被表现成身体在地面滑行,而没有跑步的力量感。

如果让 engine 直接按带位移的方式播放走路、跑步(可能还有格斗时的各种腾挪)动画,那么这些动画实际在世界中移动的距离就需要被了解并参与计算。尤其是有些动画,并没有播放完整一个循环,就被中间打断(往往是改变了移动方向或插入新的动作),导致无法预知其中间状态。

我们在引擎早期设计时,曾经考虑过导出动画信息中的位移信息(其实还包括模型的角度信息,一共是 6 个自由度)。期望中间层可以利用这些信息将动画表现的更真实。

可是在编写代码时,我们发现,利用这些数据并不容易。

经过一番思考后,我发现应该换一条路来解决这个问题。

如果我们了解一丁点自动化控制的理论,就可以知道,提供一个“负反馈”的机制,对系统稳定运行是非常重要且必要的。开环系统远远不如闭环系统稳定,是我在刚进大学不久,课堂上学到的一条重要知识。

其实我们完全不需要知道美术制作的跑步动画,模型在一个循环中移动了多远,节奏有多快。更不需要知道每个时刻动画相对于起始帧(或上一帧)做了怎样了状态(位置和方向)改变。

我们只需要通过接口让 engine 播放指定的动画,并可以随时取得模型的状态信息就够了。也就是说,指定 engine 播放一匹狼奔跑的动画,那么不下达新的指令前,engine 就一直不断的让这匹狼向前奔跑就好了。另外我们需要的是可以查到这匹狼的坐标位置,并可以控制改变动画播放的速度。

注意,这里改变动画播放的速度,并不会改变狼奔跑中每一步跨出的距离,而只是改变了步伐的节奏。

我们不断的测算狼当前的线速度(这可以通过记录上一次查询的时间和坐标,同当前的量相减,并对过去一段时间的测算量做一个加权平均即可。如果发现速度达不到期望的速度,那么就把动画播放速度提高一点点;反之就降低一点的。

最终,这匹狼的奔跑速度就会稳定在我们期望的值上,或在这个值的周围做小幅度震荡。

除了位置的控制外,我们还可以讲角速度也纳入负反馈系统,即可以得到一个相对真实的转弯动画(或许要配合美术更多的资源)。这些道理相通,就不展开写了。

总结一下,最终我们需要的是 engine 提供一个动画播放并切换到其它动画的接口,并在内部完成动作融合。btw, 相关的问题前段时间我写过一篇 blog

engine 根据动画信息,负责修改模型的位置以及角度等状态信息。也就是说,动画数据不仅仅是用于表现,还会反作用于模型的状态属性。

需要可以通过 engine 查询模型的状态信息,包括其坐标(相对父节点的坐标),朝向角度。

可以自由的控制动画播放的速度。


我相信,一个优秀的 3d engine 是在这些细节设计的推敲中诞生的,其重要性绝不亚于去应用新型号显卡的高级特性。或许已经有很多制作 3d 游戏或引擎的人已经这样做了,但我也相信,还有人没有这样做,有如想通这个问题前的我。故作此文记录之。

Comments

在3D游戏中,角色的位置不是应该由物理系统决定么?如果让动作本身来决定角色的位置,那么,如何让一个角色爬上斜坡呢?难道云风大哥有更好的解决方法?望指教。

应该类似RootMotion或者AnimationDrivenMotion的概念吧,Unreal和Crysis都在使用这种技术,主要是通过动画来完成位移,并作用到角色身上,从而防止滑步吧?

这个问题主要是3D里的动作融合导致的。至于是否能取到动作完毕时刻的位移,我觉得,一个好的引擎基本都能给出。关键是,动作融合代码如何写?
动作融合代码是模型空间中进行的还是世界空间中进行的(视空间就没有必要了吧)?在世界空间中进行骨骼融合貌似可以解决这个问题,但是,似乎运算量大了不少,因为要把所有的骨骼的矩阵都在CPU里乘以一个矩阵,这个本应该在显卡里完成的,而显卡里乘世界矩阵还是免不了,哪怕这是个标准矩阵。
如果是在模型空间中做,引擎是无法区分一个骨骼的矩阵是因为动作还是因为位移而来的。在有位移的动作跟无位移的动作之间融合的时候,实际动画是一个位移出去,在回到原点的动画过程。如果在这个过程中有位置改变,表现出来就是动画移动了,而动作融合时间一般比较短,给人的视觉效果就是动画闪到别的地方去了一下。
考虑过,将跟骨骼的位移信息单独拿出来——就是“让原本具有位移的动画变为原地移动”,但是这个位移信息程序还可以查询到。因此,动作融合的代码都是在无位移的基础上进行的了,应该能保证动画的无闪烁问题。但是呢,需要项目人员不停的查询当前动画的位移,并乘上动画的世界矩阵,再给动画播放模块——貌似项目人员又不原意,我也不很愿意。
另外,还考虑是不是针对跟骨骼,不进行动作融合,貌似问题也不能解决:一些动作,本该平滑过渡的,现在反而不能平滑过渡了。
上述三个方案都是在发现问题后的设想,没有实现过。

我游戏 client 逻辑帧率为 100 fps, server 逻辑帧率为 10 fps 。

动画播放属于纯表现的东西,不适合在 server 做相同的运算。

从概率上来说是有可能出现的,只是刚巧震荡到最快时实际上正巧需要最慢的机率很小,100FPS的逻辑帧可以保证震荡范围非常小,应该是基本规避了这个问题,不过很感兴趣如果逻辑帧降到10帧左右效果如何,我们游戏逻辑帧不可能太高,因为服务端也在跑一样的逻辑。

to spe, 你说的两个问题,在我的实际操作中均未出现。

ps. 我们游戏的逻辑处理帧数被锁定在 100 fps ,调整可以保证是非常平滑的。

还有获取模型位置信息是通过什么方法?关键骨骼的位置可能在做部分动作,比如说有一定腾空效果时出问题。

这样做的确可以省不少美术工作量,不用去调整成原地跑的动作之类的,也不用去算什么逻辑移动速度和动画播放速度如何匹配更合适。是一个保证即使没人关注这块,也能达到一定效果的方法。

不过负反馈的震荡效果未必和真实狼奔跑时的不匀速匹配得上,如果脚撑地时是速度加快到超过逻辑速度的话,是不是会滑得更厉害?

的确,游戏拿过来一看,如果精灵在屏幕上滑行,眼睛总会感到有一种错觉

"一个优秀的 3d engine 是在这些细节设计的推敲中诞生的"

说的非常好,感谢你的博客一直以来给我的帮助。

不明白
最后是实现了狼从静止,加速到稳定速度的连续过程,消除了从静止直接到某一速度的跳变。
还是根据动画包含的位移信息,在狼弹跳到落地的一个连续动作内调整动画播放的速度以提高力量感。
???

具体需求具体分析具体实现,我不觉得跟物理引擎配合会更困难。

关注你的blog很久但是一直没留过言,呵呵
这样的设计确实在易用性方面比较有利,但是貌似灵活性上有一定的问题。毕竟隐藏了位置调整的细节,假如要和物理引擎配合的话,好像会比较难

似乎,你说的是基本的高中物理

哈哈,最近没人上班,我沙发了,哦!

Post a comment

非这个主题相关的留言请到:留言本