10. 动画-让3D元素动起来
本教程翻译自官方教程Animations,各位也可以进入本站镜像站点查看
本文目录
动画Animation
我们的场景已经越来越有模有样了,但它还是没有灵魂,因为里面的元素都是静态的。本章将会学习如何把动力注入到你的物体中,选择你想要的方式来移动物体。
最终结果
在场景中添加动画主要有2种方法。第一种方法:定义一组keys,同时在每一个key中设置物体的状态,例如位置、旋转、缩放等。第二种方法:往往是对于更加复杂的动画,例如按照sin曲线运动,那就是通过在场景运行的循环中(runRenderLoop、onBeforeStepObservable)直接设置和改变物体的状态值。
基本动画
动画的创建基于一个Animation对象,由各种属性和一组keys组成。每个key代表其在给定时间的动画状态值。为了完成本章介绍的动画场景,咱们需要创建一个场景并添加若干元素:
//记得先创建: [scene, light, camera]
//创建一个立方体,name为Box1
var box1 = BABYLON.Mesh.CreateBox("Box1", 10.0, scene);
box1.position.x = -20;
我们的目标:移动上面创建的立方体“ box1”。 首先,创建我们的Animation对象:
var animationBox = new BABYLON.Animation("myAnimation", "scaling.x", 30, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
让我们对Animation的参数进行详细解读:
- 参数1 - 动画的名称
- 参数2 - 这个与对象的属性相关,可以是任何物体的属性,具体取决于需要更新的内容,上例中我们要在X轴的方向缩放 Box1,所以这里设置为 scaling.x 。
- 参数3 - 每秒请求的帧数:动画中可能达到的最高FPS。
- 参数4 - 数值变化类型。根据参数3的配置,决定要修改的值类型:浮点数(例如x轴位置Position.x),矢量(例如位置Position)还是四元数(例如旋转rotationQuaternion)。 确切的值是:
- 浮点数:BABYLON.Animation.ANIMATIONTYPE_FLOAT
- 二维向量:BABYLON.Animation.ANIMATIONTYPE_VECTOR2
- 三维向量:BABYLON.Animation.ANIMATIONTYPE_VECTOR3
- 四元数:BABYLON.Animation.ANIMATIONTYPE_QUATERNION
- 矩阵:BABYLON.Animation.ANIMATIONTYPE_MATRIX
- 颜色:BABYLON.Animation.ANIMATIONTYPE_COLOR3
请注意有一个坑,也就是设置为 矩阵BABYLON.Animation.ANIMATIONTYPE_MATRIX 的情况下,即使两个key之间的值都是矩阵,也不会进行插值计算,而是把前后两个矩阵中的每个值进行单独的插值计算,这可能不符合咱们的要求。如果需要开启矩阵的插值计算,请使用 Animation.AllowMatricesInterpolation=true 来启用此功能。如果启用了矩阵插值,系统默认使用 matrix.Lerp 方法来进行插值计算,但是这个方法精度不够,所以也可以设置 Animation.AllowMatrixDecomposeForInterpolation=true 来选择 matrix.DecomposeLerp 作为插值计算方法,这个方法精度高,但是效率低,请各位做好取舍。
另外还请注意,如果 animation.loopMode === BABYLON.Animation.ANIMATIONLOOPMODE_RELATIVE 也就是第五个参数的值,则不会开启矩阵插值。
- 参数5 - 动画的在执行完一个周期后,需要执行的行为模式,例如继续运动、从头开始执行还是立即停止,可选三种模式:
- 相对,相对运动,即:执行完一次,在接着最后的状态,继续执行:BABYLON.Animation.ANIMATIONLOOPMODE_RELATIVE
- 循环,动画循环执行,即:执行完一次,从头开始再执行:BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
- 常量,动画执行一次就停止不动了:BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT
现在我们学习了新建Animation对象,是时候来点实际应用了。在下面的案例中,我们想做一个立方体缩放的动画,但是不只是简单的均速缩放,需要做一些修饰:放大的过程应该快一些,而缩小的过程应该慢一些。所以我们可以这么做:
// An array with all animation keys
var keys = [];
//在动画key==0时, 缩放scaling的值是"1"
keys.push({
frame: 0,
value: 1
});
//在动画key==20时, 缩放scaling的值是"0.2"
keys.push({
frame: 20,
value: 0.2
});
//在动画key==100时, 缩放scaling的值是"1"
keys.push({
frame: 100,
value: 1
});
请注意!加入keys中的frame有顺序要求,必须是升序。
对于二维向量Vector2、 三维向量Vector3 和 四元数Quaternion,动画默认是使用线性插值的计算方法,即均速;我们可以在key中设置正切 inTangent 和 外切outTangent属性,以此来使用spline插值,即可以由正切inTangent 和 外切outTangent来设置变速。
var keys = [];
keys.push({
frame: 0,
value: BABYLON.Vector3.Zero(),
outTangent: new BABYLON.Vector3(1, 0, 0)
});
keys.push({
frame: 20,
inTangent: new BABYLON.Vector3(1, 0, 0),
value: new BABYLON.Vector3(1, 1, 1),
outTangent: new BABYLON.Vector3(-1, 0, 0)
});
keys.push({
frame: 100,
inTangent: new BABYLON.Vector3(-1, 0, 0),
value: BABYLON.Vector3.Zero()
});
接下来是两个重要的步骤:
- 把之前定义的keys动画组加入到Animation对象中
animationBox.setKeys(keys);
- 把动画和物体关联起来
box1.animations = [];
box1.animations.push(animationBox);
最后,我们需要一行代码来启动自己的动画:
scene.beginAnimation(box1, 0, 100, true);
我们还能够直接逆向运行动画,也就是从尾到头的顺序执行动画:
//只需要交换第二个和第三个参数
scene.beginAnimation(box1, 100, 0, true);
以上的函数beginAnimation,它所包含的参数如下表所示:
名称 | 类型 | 描述 | 可选参数 |
---|---|---|---|
target | any | 执行动画的对象,例如物体 | 必选 |
from | number | 开始的帧 | 必选 |
to | number | 结束的帧 | 必选 |
loop | boolean | 假如为true,则动画循环 (依赖BABYLON.Animation.ANIMATIONLOOPMODE) | 可选 |
speedRatio | number | 默认 : 1,动画执行的速度比例,可以减慢或加快动画 | 可选 |
onAnimationEnd | () => void | 在动画执行结束时触发的回调函数, 如果动画是手动停止也会触发 (也依赖于 ANIMATIONLOOPMODE) | 可选 |
animatable | Animatable | 指定执行动画的实例Animatable | 可选 |
stopCurrent | boolean | 调用执行函数时,如果该对象的动画还在运行中,是否停止这个运行中的动画,默认为true | 可选 |
beginAnimation函数返回一个 BABYLON.Animatable 对象,代表 执行动画实例 (例如使用getAnimationByTargetProperty函数)。
而BABYLON.Animatable对象,支持以下的方法:
- pause()暂停
- restart()重新开始
- stop()停止
- reset()重置
下面的例子展示了在执行beginAnimation方法时,用变量把执行动画的实例保存了下来,之后再做进一步的动画控制。
var newAnimation = scene.beginAnimation(box1, 0, 100, true);
然后暂停正在运行的动画
newAnimation.pause();
各位可能已经发现立方体的 属性animations 是一个数组, box1.animations.push(animationBox); ,这意味着一个物体时可以加入多个动画形成动画组合,所以执行 beginAnimation 方法时,形成的 BABYLON.Animatable 对象也可能包含一组动画,它们被存储在 Animatable's ._animations 数组中,当我们执行BABYLON.Animatable 对象的pause、restart、stop、reset方法时,Animatable's ._animations 中的所有动画都会响应这些命令。总之,各位可以发现BABYLON.Animatable 对象非常有用,我们控制动画时常都要访问这些它们,可以通过 **scene.getAnimatableByTarget() **这个场景的方法来枚举所有的这些对象。
现在,我们已经完成了对于物体的x轴缩放动画 box1.scaling.x ,但这有点单调,可以再加一个y轴的缩放动画,或者为了更有趣可以使它动起来。可以通过加入更多的动画到物体的animations属性中 box1.animations.push(animationBox);,来实现多个动画的组合,快试试吧!
动画和异步promises
自从Babylon.js3.3版本以后,我们能使用promises等待动画实例animatable执行完成:
var anim = scene.beginAnimation(box1, 0, 100, false);
console.log("before");
await anim.waitAsync();
console.log("after");
控制动画-差值函数Controlling animations
每一个BABYLON.Animation对象都有一个被称为 当前帧 的属性,对应当前动画中keys数组中的一个key。
对于高级的关键帧动画,我们可以在keys中的两个key之间自定义插值函数,也就是两个关键帧之间定义一个过渡的算法函数,实现特殊的动画过渡效果。在默认情况下的插值函数如下所示:
//表示浮点数的插值,例如scaling.x,position.y
BABYLON.Animation.prototype.floatInterpolateFunction = function (startValue, endValue, gradient) {
return startValue + (endValue - startValue) * gradient;
};
//表示四元数的插值,例如matrix
BABYLON.Animation.prototype.quaternionInterpolateFunction = function (startValue, endValue, gradient) {
return BABYLON.Quaternion.Slerp(startValue, endValue, gradient);
};
//表示三维向量Vector3的插值,例如scaling,postion,rotation
BABYLON.Animation.prototype.vector3InterpolateFunction = function (startValue, endValue, gradient) {
return BABYLON.Vector3.Lerp(startValue, endValue, gradient);
};
以上的函数,都可以进行重写,定义自己的插值函数,我们可以自定义的所有插值函数如下所示:
- floatInterpolateFunction
- quaternionInterpolateFunction
- quaternionInterpolateFunctionWithTangents
- vector3InterpolateFunction
- vector3InterpolateFunctionWithTangents
- vector2InterpolateFunction
- vector2InterpolateFunctionWithTangents
- sizeInterpolateFunction
- color3InterpolateFunction
- matrixInterpolateFunction
动画辅助创建函数Helper function
各位可能觉得上面创建动画的步骤很繁琐,所以这里介绍一个扩展函数来快速创建动画:
Animation.CreateAndStartAnimation = function(name, mesh, targetProperty, framePerSecond, totalFrame, from, to, loopMode);
我们来详细剖析一下这个函数:
- 函数只能定义2个关键帧,也就是对应之前的keys数组,只能插入两个key对象。
- 函数只能针对物体起作用。
- 函数一执行,动画就马上生效,新建和执行两个步骤被统一在一起了。
这是使用 CreateAndStartAnimation() 函数的简单示例:
BABYLON.Animation.CreateAndStartAnimation('boxscale', box1, 'scaling.x', 30, 120, 1.0, 1.5);
以上等价于
var animationBox = new BABYLON.Animation("myAnimation", "scaling.x", 30, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
var keys = [];
keys.push({
frame: 30,
value: 1
});
keys.push({
frame: 120,
value: 1.5
});
animationBox.setKeys(keys);
box1.animations = [];
box1.animations.push(animationBox);
scene.beginAnimation(box1, 0, 120, true);
是不是简单粗暴?
动画混合Animation blending
使用 enableBlending = true 来开启动画混合模式,混合模式就是说动画的执行时以当前物体的状态为准,例如物体初始位置是在(0, 0, 0)原点,先执行一个动画改变位置,当运动到(0, 20, 0)的时候,停止原有动画,加入一个新动画,新动画的第一帧从(0,0,0)开始,默认情况下物体会回到(0,0,0)从头执行新动画,但是如果设置了 enableBlending = true ,则新动画会从(0, 20, 0)的当前位置执行。这个特性对动画改变时的平滑过渡,非常有效。
在下面的Playground演示中,新动画都会与框的当前位置混合在一起,可以点击右上角的FPS数字标记看到效果。Playground 动画混合演示
以上的例子是把相同的动画混合在一起,但是将不同的动画混合到一起更加常见,例如当步行角色变为跑步时:Playground 步行跑步动画切换示例,这样的效果需要自己用代码实现。
动画权重Animation weights
自从Babylon.jsv3.2版本开始,我能使用指定的权重来执行动画,这意味着可以使用这个方法在同一对象上同时执行多个动画。最终的效果是根据所有动画的权重混合而来。
要使用权重执行动画,可以使用新的scene.beginWeightedAnimation API:
// 权重是1,全部效果
var idleAnim = scene.beginWeightedAnimation(skeleton, 0, 89, 1.0, true);
//权重是0,等于没效果
var walkAnim = scene.beginWeightedAnimation(skeleton, 90, 124, 0, true);
// 权重是0,等于没效果
var runAnim = scene.beginWeightedAnimation(skeleton, 125, 146, 0, true);
beginWeightedAnimation函数接受一下的参数内容:
参数名称 | 参数类型 | 描述 | 是否可选 |
---|---|---|---|
target | any | 动画作用的对象 | 必填 |
from | number | 开始的帧 | 必填 |
to | number | 结束的帧 | 必填 |
weight | number | 动画执行的权重,默认为1,0为暂停状态 | 可选 |
loop | boolean | 假如为true,则动画循环 (依赖BABYLON.Animation.ANIMATIONLOOPMODE) | 可选 |
speedRatio | number | 默认 : 1,动画执行的速度比例,可以减慢或加快动画 | 可选 |
onAnimationEnd | () => void | 在动画执行结束时触发的回调函数, 如果动画是手动停止也会触发 (也依赖于 ANIMATIONLOOPMODE) | 可选 |
animatable | Animatable | 指定执行动画的实例Animatable | 可选 |
和 beginAnimation 方法一样, beginWeightedAnimation 也返回一个BABYLON.Animatable 对象,不过会多出一个weight属性,来决定执行的权重。
我们可以随时对BABYLON.Animatable 对象的 weight 值进行更改,以此来改变动画执行的效果, weight 取值范围是0到1,如果要关闭权重模式可以设置为-1,如果设置为0则此动画被视为暂停状态。
var idleAnim = scene.beginWeightedAnimation(skeleton, 0, 89, 1.0, true);
var runAnim = scene.beginWeightedAnimation(skeleton, 125, 146, 0, true);
idleAnim.weight = 0.5;
runAnim.weight = 0.5
加入在设置动画时长度不一致(一致是指keys包含的frame长度一致),则需要开启动画同步:
//使idleAnim动画的长度与runAnim相同
idleAnim.syncWith(runAnim);
要禁用动画同步,只需调用 animation.syncWith(null) 。完整的演示可以在这里找到:Playground 权重动画综合示例
叠加混合动画Additive animation blending
目前为止,我们讨论过的动画混合类型是 覆盖混合。这意味着添加一个动画到物体时,这个物体正在执行的动画影响力将会减弱。执行的结果总是标准化的,所以同时播放的动画越多,每个动画对最终结果的影响力就越小,覆盖混合动画的所有关键帧都是存储于对象的父元素上。我们举一个栗子,有一个对象带有2个覆盖混合动画,第一个动画的关键帧是:
[
{
frame: 0,
value: [0, 0 ,0]
},
{
frame: 30,
value: [0, 2 ,0]
},
{
frame: 60,
value: [0, 0, 0]
},
]
第二个动画的关键帧是:
[
{
frame: 0,
value: [0, 0 ,0]
},
{
frame: 30,
value: [0, 0 ,2]
},
{
frame: 60,
value: [0, 0, 0]
},
]
区别在frame是第30帧,如果我们同时播放这两个动画,并且权重都为1的情况下,第0帧和第60帧相同,但是第30帧会移动到[0, 1, 1]的位置,可见虽然两个动画在的第30帧的Y和Z轴都是2,但是如果同时执行它们永远到不了2。这样的行为很适合在动画之间进行切换,例如从步行到跑步的混合动画,但是如果我们不希望这样,想要它们都达到2怎么办?所以这就是叠加混合动画发挥作用的时候了。
叠加混合动画是唯一的,因为它不会进行标准化。我们可以同时播放N个叠加动画,而且它们各自都有确切的影响权重,所以,叠加动画的值是相对于覆盖动画的当前结果值,而不是其父类,因此如果上面的例子不是使用覆盖动画,而时使用叠加动画,则第30帧的结果将是[0, 2, 2],也就是第二个动画的值会在第一个动画的基础上相加。
有几种方法可以指定叠加动画。首先,是可以在beginAnimation中指定isAdditive参数。可以看看场景Scene的API文档。默认情况下,isAdditive参数都为false。isAdditive属性决定Animatable对象是否应该进行叠加评估,而且能够在任何时候被修改。 AnimationGroups 动画组对象,可以添加多个对象的多个动画到一个动画分组中,也有一个isAdditive属性访问器,默认为false。设置isAdditive属性访问器将修改动画组中所有Animatables对象的isAdditive属性。
叠加动画的一个难题是层级的管理问题,因为叠加动画的评估不是根据其对象的父层级,而是其他动画的相对结果,因此直接创建叠加动画不是很直观。为了让使用变得容易,在AnimationGroup动画组、 Skeleton骨骼动画 、Animation动画中添加了一个MakeAnimationAdditive静态方法,它允许你指定现有动画中的某一个帧,并将其从动画的关键帧中减去,以便于使其他动画都相对这一帧的特定效果起作用。
以上的例子展示了如何把动画转换为叠加动画,然后将其混合到替代动画中。点击示例中的按钮,可以在多个替代动画中切换,使用滑块可以在叠加动画的基础上进行动画混合。
属性重写Overriding properties
当有一个物体,物体上存在多个动画或者一个骨骼动画(所有的骨骼都能运动),我们可以使用animationPropertiesOverride方法来为物体中的所有子动画指定一个默认属性。这些属性将重写子动画的属性值:
var overrides = new BABYLON.AnimationPropertiesOverride();
overrides.enableBlending = true;
overrides.blendingSpeed = 0.1;
skeleton.animationPropertiesOverride = overrides;
以下是能够被重写的属性清单:
- enableBlending
- blendingSpeed
- loopMode
请注意,如果animation的对象没有包含以上属性,scene.animationPropertiesOverride还是能够生效。
缓动函数Easing functions
我们可以在动画中加入一些缓动函数。如果想了解更多关于缓动函数的信息,可以阅读以下的网站:
所有的缓动函数都已经在Babylon中实现了,当然,定义自己的数学函数也是可以的。
以下是预定义好的缓动函数:
- BABYLON.CircleEase()
- BABYLON.BackEase(amplitude)
- BABYLON.BounceEase(bounces, bounciness)
- BABYLON.CubicEase()
- BABYLON.ElasticEase(oscillations, springiness)
- BABYLON.ExponentialEase(exponent)
- BABYLON.PowerEase(power)
- BABYLON.QuadraticEase()
- BABYLON.QuarticEase()
- BABYLON.QuinticEase()
- BABYLON.SineEase()
- BABYLON.BezierCurveEase()
可以使用EasingMode属性更改缓动函数的行为,即动画的插值更新方式。有三种可选的值:
- BABYLON.EasingFunction.EASINGMODE_EASEIN:插值遵循与缓动函数关联的数学公式。
- BABYLON.EasingFunction.EASINGMODE_EASEOUT:插值遵循100%插值减去与缓动函数关联的公式输出值,可以理解为最大值减去运行中的值,相当于EASEIN倒过来。
- BABYLON.EasingFunction.EASINGMODE_EASEINOUT:插值将EaseIn用于动画的前半部分,将EaseOut用于后半部分。
以下是一个简单的实例,在CircleEase缓动函数中为圆环设置动画:
//创建一个位置的三维向量Vector3动画, FPS为30
var animationTorus = new BABYLON.Animation("torusEasingAnimation", "position", 30, BABYLON.Animation.ANIMATIONTYPE_VECTOR3, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
// 圆环的目标位置
var nextPos = torus.position.add(new BABYLON.Vector3(-80, 0, 0));
// 动画的keys
var keysTorus = [];
keysTorus.push({ frame: 0, value: torus.position });
keysTorus.push({ frame: 120, value: nextPos });
animationTorus.setKeys(keysTorus);
// 穿件缓动函数CircleEase
var easingFunction = new BABYLON.CircleEase();
//对于每个缓动函数,可以选择 EASEIN (默认), EASEOUT, EASEINOUT三种
easingFunction.setEasingMode(BABYLON.EasingFunction.EASINGMODE_EASEINOUT);
// 添加缓动函数到动画中
animationTorus.setEasingFunction(easingFunction);
// 向圆环中的动画集合中添加动画实例
torus.animations.push(animationTorus);
// 最后,执行圆环动画,从0到120帧,循环播放
scene.beginAnimation(torus, 0, 120, true);
也可以用BezierCurveEase(x1, y1, x2, y2) 函数,用 贝塞尔曲线BezierCurve 算法来实现一个缓动函数。为此,有一个创建曲线算法很好的小工具,推荐给大家。
下面是一个非常酷的代码实现,使用了贝塞尔曲线算法:
//创建一个贝塞尔曲线函数
var bezierEase = new BABYLON.BezierCurveEase(0.32, -0.73, 0.69, 1.59);
//为动画对象设置一个缓动函数
animationTorus.setEasingFunction(bezierEase);
最后,我们可以扩展EasingFunction函数来创建自己的缓动函数,如下所示:
var FunnyEase = (function (_super) {
__extends(FunnyEase, _super);
function FunnyEase() {
_super.apply(this, arguments);
;}
FunnyEase.prototype.easeInCore = function (gradient) {
//这里是一个核心方法,你可以在这里修改代码,定义自己的缓动函数
// gradient参数是值改变的百分比,如果总体是100,现在运行到20,那么gradient就是20%
return Math.pow(Math.pow(gradient, 4), gradient);
};
return FunnyEase;
})(BABYLON.EasingFunction);
你可以在下面的Playground中查看缓动函数行为的完整示例。
复杂动画
复杂的动画可以在每一帧进行内容的修改,所以运行时计算的代码必须在此函数中:
scene.registerBeforeRender(function () {
//在这里实现动画计算
});
registerBeforeRender() 设置的功能在每帧之前运行(通常fps是60),所以可以通过对对象的属性进行更改,来实现动画效果。
这里是一个关于复杂动画的简单例子:
复杂动画示例
此功能对于复杂动画(例如游戏)非常有用,因为游戏中角色的移动,需要依靠许多参数来决定。
将所有动画结合起来,如果做得很好,这个功能将发挥很大作用。
请别忘了访问我们的API文档,这里可以学习更多关于Babylon.js动画类和动画执行实例类Animatable的相关知识。
为动画添加事件
从Babylon.js V2.3版本开始,我们可以添加一个事件到动画的特定帧。
事件是一个函数,它将在特定的帧时被执行。
使用起来非常简单:
// 用3个参数来创建事件:
// - 事件触发时的帧,一个数值
// - 执行的动作,在这里写代码
// - 事件是否只执行一次,默认是false,每次都执行
var event1 = new BABYLON.AnimationEvent(50, function() { console.log("Yeah!"); }, true);
// 把事件添加到动画中
animation.addEvent(event1);
帧同步Deterministic lockstep
有时确保动画,物理和游戏逻辑代码的同步,是非常重要的事,而且需要通过 帧速率方差frame-rate variance 解耦。这在两方面是非常有用的,首先是能够重放场景的演变过程,其次是在多用户环境下,让各个客户端之间的差异状态最小化,就像王者荣耀、魔兽、星际、CS等游戏一样,双方联机,两边的状态必须是一致的内容。
帧同步的原理是量化状态的执行时间,具体方法为:在固定的频率下,以离散的时间步长来更新状态,并且保留一个累加器,以便将超过的时间带入下一帧更新。
为此,需要通过以下两个选项来初始化Babylon引擎:
this.engine = new BABYLON.Engine(theCanvas, true, {
deterministicLockstep: true,
lockstepMaxSteps: 4
});
这样做的话,场景将以离散的 时间步长量timeStep amount chunks,来渲染量化的物理和动画步骤。就像在物理引擎中设置的那样,例如:
var physEngine = new BABYLON.CannonJSPlugin(false);
newScene.enablePhysics(this.gravity, physEngine);
physEngine.setTimeStep(1/60);
使用上面的代码,引擎将以60Hz的频率,来运行离散步骤,如果帧渲染时间较晚,引擎将在渲染帧之前,尝试计算最多4个步骤(lockstepMaxSteps)以恢复最终累积的延迟。也就是在用户的客户端保持一致的状态,慢的人会接受最新的状态进行更新。
请注意,当显式创建CannonJSPlugin时,必须在其构造函数中传递false作为_useDeltaForWorldStep参数,以禁用CannonJS内部累加器。
为了与步骤同步运行逻辑代码,场景中有以下两种observables方法:
newScene.onBeforeStepObservable.add(function(theScene){
console.log("Performing game logic, BEFORE animations and physics for stepId: "+theScene.getStepId());
});
newScene.onAfterStepObservable.add(function(theScene){
console.log("Performing game logic, AFTER animations and physics for stepId: "+theScene.getStepId());
});
以上方法在动画和物理运行之前或之后,允许运行任意的逻辑代码。
在以下示例中,可以在控制台console中看到将球体视为静止的stepId以及旋转框的旋转值。 无论帧速率如何,多次运行都将始终产生相同的值。
下一步
 现在我们已经知道如何创建一个完整的简单动态场景,所以讨论动画运行时的物体碰撞以及重力等,是很重要的!请阅读下一章FPS游戏必备-相机、形状碰撞、重力。
延伸阅读
贡献者
参与贡献? 联系alexads@foxmail.com