Tiny是怎样走路的

关注目标:SimpleAnimation范例

范例内容为读取Tiny.x中的动画并渲染出来。
这个范例使用SampleFramework(以下简称SFK)来实现基本的功能,所以大多数地方不必关心,我们只关心最关键的部分:是什么让Tiny动了起来。首先大概的看一下这个程序最主要的类——SimpleAnimation——都有哪些成员。我第一个注意到的是Main(),不过很遗憾,这里面只是对SFK的初始化。看来渲染循环被隐藏了,即程序应该没有自己管理渲染循环,而是通过响应SFK的事件来绘制场景。那么既然如此,控制场景绘制的代码到底在哪里?OnFrameMove和OnFrameRender是最可疑的了。果然,我在OnFrameMove里找到了关键的代码。


TIP:主框架使用事件来调度渲染过程,这是我一直在使用的方法。最近看了看XNA和SFK,感觉这个做法应该是没错的。XNA的Game类里有Update和Draw这两个可重载的方法,它们的职能正好就是OnFrameMove和OnFrameRender——前者负责在渲染过程开始前更新场景,后者负责渲染场景。我们自己在构建框架的时候也建议采用这种更新和渲染分开进行的机制,简单说就是在帧刷新前发出更新场景事件,随后再发出渲染场景事件。



OnFrameMove里面的代码不多,elapsedTime顾名思义是上一帧到这一帧的间隔时间。打头的camera操作不用管因为我们只关心Tiny的运动。接下来的代码涉及到了rootFrame.AnimationController。rootFrame是类AnimationRootFrame的实例,这个类来自Microsoft.DirectX.Direct3D。AnimationController是它的成员,自然就是用来控制动画的。代码里有这么一个操作:


rootFrame.AnimationController.AdvanceTime(elapsedTime);


我很怀疑这个操作跟动画有关,因为除了这里就再也没有对动画进行过操作的代码了。试着更改一下参数,把“elapsedTime”改成“0.1”,运行——Ok,现在Tiny的动作就像抽风一样。看来我们猜对了,AdvanceTime就是关键所在,它控制模型的动画。但这个知道了以后我们还什么也做不了,因为我们还不知道它的参数——elapsedTime也好、0.1也好——对于这个方法到底有什么意义。首先我猜测这个参数是帧滚动时间:指定一个时间t,它就在时间轴上滚动到t时刻,然后模型就变成了t时刻的动作。到底是不是这样呢?否定。因为如果是这样的话,我们指定0.1位参数的情况下,Tiny应该是静止不动的,而实际上0.1会导致他抽风——其实是动作过快而已。我们可以检查一下运行时elapsedTime的值,0.1比它大太多了。数值越大动作越快,这也是一个线索,记下。

综合以上的信息,我再进行一次猜测:AdvanceTime是一个累加操作,每次对他的调用就是让模型以参数指定的幅度,在时间轴上向前推进。而且,它还是周期性的,否则Tiny不可能一直走个不停。

让我们想个办法来验证这个猜测:如果我们知道动画本身的周期T,那么在OnFrameMove里面AdvanceTime(T),Tiny应该是不会动的。我用3DMAX打开Tiny.x,发现他的动作一共有31帧。那么接下来修改代码:


rootFrame.AnimationController.AdvanceTime(31);


运行——成功,Tiny不动了。

考虑到3DMAX时间轴的单位可能和D3D不一样,所以31可能实际上是10个周期或者100个周期或者更多。我尝试了一下3.1、0.31,发现3.1应该就是最小周期——0.31的时候Tiny又抽风了。

 

现在简单做下总结:Microsoft.DirectX.Direct3D.AnimationRootFrame AnimationController.AdvanceTime(double)是用来滚动模型时间轴的方法,它的参数代表滚动幅度。D3D的时间轴是3DMAX的十分之一,以0~10为一个完整周期。

 

延伸:如果要实现多个实例的不同动作,是否可以采用这样一种办法:把滚动操作放在场景绘制阶段,同时每个实例记忆自己的动作所处的时刻。场景绘制时将时间轴滚动一个完整的周期,其间各实例在时间轴滚动到自己所需时刻的时候进行绘制。

 

posted @ 2006-12-12 19:55  Cloudage  阅读(856)  评论(0)    收藏  举报