动画可以让您的游戏或应用程序栩栩如生。在 Babylon.js 中制作动画有两种主要方式。您可以在渲染周期期间更改场景中对象的属性,我们将在之后看一看 Animation 方法的描述。
排序动画组合多个剪辑以形成动画的一种直接方法是为每个动画剪辑指定开始时间。
设计 概述摄像机显示一栋带门的建筑物。摄像机靠近门并停了下来。门打开,摄像机进入房间。当摄像机进入房间时,房间里的灯亮了。门关上,摄像机环顾四周。
由于目的是展示如何制作剪辑,房间和门将简单地使用没有纹理的网格制作。
表演者Camera - 相机 - 通用
Door - 门 - 铰链在右侧,向内打开
Spot Lights - 聚光灯 - 用球体显示位置
为每个表演者创建一个动画,其中包含每个定时事件的关键点。
动画 对于相机移动相机会改变相机的位置(vector3)。相机环顾四周是围绕 y 轴(float)的旋转。
由于动画只能更改一个属性,因此相机需要两个动画。
移动摄像机的关键点将在时间 0 开始,摄像机将开始远离建筑物并上下移动,直到它在 3 秒的时间刚好在门外。
当门打开时,摄像机将保持其位置 2 秒,然后在 5 秒时将向前移动到与门成一定角度的房间,在 8 秒时停止。
相机在 9 秒时不旋转,然后相机将在 14 秒时旋转 180 度以面对门。
相机的关键点的值将是它在 0、 3、 5 和 8 秒处的帧中的位置以及它在 0、 9 和 14 秒处的帧中的旋转。
// 让相机向前移动
var movein = new BABYLON.Animation(
"movein",
"position",
frameRate,
BABYLON.Animation.ANIMATIONTYPE_VECTOR3,
BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT
);
var movein_keys = [];
movein_keys.push({
frame: 0,
value: new BABYLON.Vector3(0, 5, -30)
});
movein_keys.push({
frame: 3 * frameRate,
value: new BABYLON.Vector3(0, 2, -10)
});
movein_keys.push({
frame: 5 * frameRate,
value: new BABYLON.Vector3(0, 2, -10)
});
movein_keys.push({
frame: 8 * frameRate,
value: new BABYLON.Vector3(-2, 2, 3)
});
movein.setKeys(movein_keys);
// 让相机扫一圈
var rotate = new BABYLON.Animation(
"rotate",
"rotation.y",
frameRate,
BABYLON.Animation.ANIMATIONTYPE_FLOAT,
BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT
);
var rotate_keys = [];
rotate_keys.push({
frame: 0,
value: 0
});
rotate_keys.push({
frame: 9 * frameRate,
value: 0
});
rotate_keys.push({
frame: 14 * frameRate,
value: Math.PI
});
rotate.setKeys(rotate_keys);
对于门
门以 y 轴为轴围绕铰链进行旋转。打开和关闭门的旋转将分别需要 2 秒。
关键点是 0、 3、 5、 13 和 15 秒。
关键点的值将是它在关键点处的帧中围绕 y 轴的旋转。
// 用于打开和关闭门
var sweep = new BABYLON.Animation(
"sweep",
"rotation.y",
frameRate,
BABYLON.Animation.ANIMATIONTYPE_FLOAT,
BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT
);
var sweep_keys = [];
sweep_keys.push({
frame: 0,
value: 0
});
sweep_keys.push({
frame: 3 * frameRate,
value: 0
});
sweep_keys.push({
frame: 5 * frameRate,
value: Math.PI / 3
});
sweep_keys.push({
frame: 13 * frameRate,
value: Math.PI / 3
});
sweep_keys.push({
frame: 15 * frameRate,
value: 0
});
sweep.setKeys(sweep_keys);
对于灯光
灯光的强度会有所不同。这需要一系列聚光灯的数组。
灯的关键点是保持关闭到 7 秒,在 10 秒达到全强度,直到 14 秒熄灭。
// 让光线变亮变暗
var lightDimmer = new BABYLON.Animation(
"dimmer",
"intensity",
frameRate,
BABYLON.Animation.ANIMATIONTYPE_FLOAT,
BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT
);
var light_keys = [];
light_keys.push({
frame: 0,
value: 0
});
light_keys.push({
frame: 7 * frameRate,
value: 0
});
light_keys.push({
frame: 10 * frameRate,
value: 1
});
light_keys.push({
frame: 14 * frameRate,
value: 1
});
light_keys.push({
frame: 15 * frameRate,
value: 0
});
lightDimmer.setKeys(light_keys);
卡通片
现在只需同时运行所有剪辑:
scene.beginDirectAnimation(camera, [movein, rotate], 0, 25 * frameRate, false);
scene.beginDirectAnimation(hinge, [sweep], 0, 25 * frameRate, false);
scene.beginDirectAnimation(
spotLights[0],
[lightDimmer],
0,
25 * frameRate,
false
);
scene.beginDirectAnimation(
spotLights[1],
[lightDimmer.clone()],
0,
25 * frameRate,
false
);
完成的动画序列示例:
示例链接
示例代码:
Babylon.js sample code
分组动画
AnimationGroup 允许您将动画和网格链接在一起并作为一个动画组播放、暂停和停止它们。
组成一个动画组例如创建了animation1、 animation2 和 animation3 以及网格 mesh1、 mesh2、 mesh3 和 mesh4,您可以形成以下动画组:
var animationGroup1 = new BABYLON.AnimationGroup("Group1");
var animationGroup2 = new BABYLON.AnimationGroup("Group2");
然后使用 addTargetedAnimation 方法将动画与网格链接起来,并将它们添加到组中:
animationGroup1.addTargetedAnimation(animation1, mesh1); animationGroup1.addTargetedAnimation(animation3, mesh1); animationGroup1.addTargetedAnimation(animation2, mesh2); animationGroup2.addTargetedAnimation(animation2, mesh3); animationGroup2.addTargetedAnimation(animation1, mesh4); animationGroup2.addTargetedAnimation(animation2, mesh4); animationGroup2.addTargetedAnimation(animation3, mesh4);
由于动画可能是使用不同的时间线创建的,因此必须使用 normalize 规范这些动画。
规范化动画组可能 animation1、 animation2 和 animation3 都是使用不同帧数创建的。例如,animation1 可能从第 0 帧到第 80 帧,animation2 从第 0 帧到第 75 帧,animation3 从第 0 帧到第 100 帧。您可以使用 normalize 方法使所有动画的帧数相同,如:
animationGroup2.normalize(0, 100);
一般来说,规范化的参数是数字 beginFrame 和 endFrame。
beginFrame - 开始帧编号 - 必须小于或等于所有动画的最小开始帧,以上示例不大于 0。
endFrame - 结束帧编号 - 必须大于或等于所有动画的最大结束帧,以上示例不小于 100。
动画组 1 的示例:
示例链接
示例代码:
Babylon.js sample code
动画组 2 的示例:
示例链接
示例代码:
Babylon.js sample code
动画组 3 的示例:
示例链接
示例代码:
Babylon.js sample code
动画组的速率比
可以设置组中所有动画的 speedRatio 来加快或减慢动画播放,例如:
animationGroup1.speedRatio = 0.25; animationGroup2.speedRatio = 3;
动画组速率比的示例:
示例链接
示例代码:
Babylon.js sample code
从现有动画创建动画组
您可以通过枚举动画表中包含的动画来从动画表中创建新的 AnimationGroup:
var animationGroup = new BABYLON.AnimationGroup("my-animation-group");
for (anim of idleAnim.getAnimations()) {
animationGroup.addTargetedAnimation(anim.animation, anim.target);
}
从 Animatable 创建 AnimationGroup 的示例:
示例链接
示例代码:
Babylon.js sample code
在动画组结束时
onAnimationEndObservable 可以用来在动画组结束时触发一个函数:
animationGroup1.onAnimationEndObservable.add(function () {
mesh2.material = redMaterial;
});
动画组结束的示例:
示例链接
示例代码:
Babylon.js sample code
在动画组循环时
onAnimationLoopObservable 可用于在动画组循环时触发一个函数:
animationGroup1.onAnimationLoopObservable.add(function (targetAnimation) {
console.log(targetAnimation.animation.name);
});
onAnimationGroupLoopObservable 可用于在动画组的所有动画循环播放时触发一个函数:
animationGroup1.onAnimationGroupLoopObservable.add(function (group) {
console.log("Group looped!");
});



