目录
(1)组成一个动画组:addTargetedAnimation
(5)在动画结束时:onAnimationEndObservable
(6)在动画组循环时:onAnimationLoopObservable、onAnimationGroupLoopObservable
(1)用键盘控制动画角色的移动:animationGroups、ActionManager
(3)辅助函数:CreateAndStartAnimation
(5)动画权重:beginWeightedAnimation
(7)叠加动画混合:MakeAnimationAdditive
(8)覆盖属性:animationPropertiesOverride
9. 使用渲染循环的动画:registerBeforeRender
12. 将位置设置为音频侦听器:audioListenerPositionProvider
(1)弹跳行为:BABYLON.BouncingBehavior
(2)自动旋转行为:BABYLON.AutoRotationBehavior
(3) 框架行为:BABYLON.FramingBehavior
(2)六自由度拖动行为:SixDofDragBehavior
(3)多指针缩放行为:MultiPointerScaleBehavior
(4)附加到框行为(应用栏):AttachToBoxBehavior
(6)表面磁性行为:SurfaceMagnetismBehavior
(7)手约束行为:HandConstraintBehavior
1、第一章 动画
1. 设计动画
-
设计剪辑
沿 x
方向滑动项目的动画变为:
const frameRate = 10;
const xSlide = new BABYLON.Animation("xSlide", "position.x", frameRate, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
关键帧位于 0
、1
和 2
秒。要找到 t
秒后的帧号,将时间乘以帧速率,即 t x frameRate
。
在这种情况下,关键帧的帧号为 0
、1 x frameRate
和 2 x frameRate
。
关键帧被设置为具有帧(编号)和值属性的 JavaScript
对象数组,并添加到动画中:
const keyFrames = [];
keyFrames.push({
frame: 0,
value: 2,
});
keyFrames.push({
frame: frameRate,
value: -2,
});
keyFrames.push({
frame: 2 * frameRate,
value: 2,
});
xSlide.setKeys(keyFrames);
动画现在已完成制作,可以应用到盒子上:
box.animations.push(xSlide);
动画开始,参数二、三是关键帧列表中的起始帧和结束帧:
scene.beginAnimation(box, 0, 2 * frameRate, true);
-
反转动画
// 正向动画
scene.beginAnimation(box, startFrame, endFrame, false);
// 反向动画
scene.beginAnimation(box, endFrame, startFrame, false);
2. 动画方法描述
-
创建动画
const myAnim = new BABYLON.Animation(name, property, frames per second, property type, loop mode)
name:string,动画名称;
property:string,将应用动画的对象的属性。例如:position 是 Vector3 属性或 position.x 是浮点数属性;
frames per second:number,每秒动画帧数(与每秒的场景渲染帧数无关);
property type:属性参数的属性类型;
loop mode:循环模式。
-
设置关键帧
keyFrames
是一个对象数组。每个对象都有两个属性:
(1)frame:帧号
(2)value:用于被更改的属性
一旦构建,它就会被添加到动画中:
myAnim.setKeys(keyFrames);
-
开始动画
为了运行动画,它被推送到网格的动画数组属性上:
mesh.animations.push(myAnim)
开始动画:第四个参数为true表示循环动画
scene.beginAnimation(target, from, to, [loop]);
target:object,要动画化的 Babylon.js 对象;
from:number,开始动画的帧;
to:number,结束动画的帧。
使用以下方法将多个动画应用于目标:
scene.beginDirectAnimation(target, animations, from, to, loop)
-
可动画化
两种启动动画的方法都返回一个 Animatable
对象
const myAnimatable = myscene.beginAnimation(target, from, to, true)
支持以下方法:pause()、restart()、stop()、reset()
3. 排序动画
组合多个剪辑以形成动画的一种直接方法是为每个动画剪辑指定开始时间。
(1)设计:对于相机
移动相机会改变相机的位置(vector3
)。相机环顾四周是围绕 y
轴(float
)的旋转。
由于动画只能更改一个属性,因此相机需要两个动画。
相机的关键点的值将是它在 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);
(2)对于门
门以 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);
(3)对于灯光
灯的关键点是保持关闭到 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);
4. 卡通片
同时运行所有剪辑:
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
);
5. 分组动画:AnimationGroup
AnimationGroup
允许您将动画和网格链接在一起并作为一个动画组播放、暂停和停止它们。
(1)组成一个动画组:addTargetedAnimation
使用 addTargetedAnimation
方法将动画与网格链接起来
var animationGroup1 = new BABYLON.AnimationGroup("Group1");
var animationGroup2 = new BABYLON.AnimationGroup("Group2");
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
规范这些动画。
(2)规范化动画组:normalize
可以使用 normalize
方法使所有动画的帧数相同。
animationGroup2.normalize(0, 100);
一般来说,规范化的参数是数字 beginFrame
和 endFrame
。
beginFrame:开始帧编号,必须小于或等于所有动画的最小开始帧
endFrame:结束帧编号,必须大于或等于所有动画的最大结束帧
// 创建缩放动画
var animation1 = new BABYLON.Animation("tutoAnimation", "scaling.z", 30, BABYLON.Animation
.ANIMATIONTYPE_FLOAT,
BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
// 动画帧数组
var keys = [];
// 在动画帧数组下标 0 处,缩放的值为 1
keys.push({
frame: 0,
value: 1
});
// 在动画帧数组下标 20 处,缩放的值为 0.2
keys.push({
frame: 20,
value: 0.2
});
// 在动画帧数组下标 100 处,缩放的值为 1
keys.push({
frame: 100,
value: 1
});
// 向动画对象添加帧
animation1.setKeys(keys);
// 创建具有不同时间轴的第二个旋转动画
var animation2 = new BABYLON.Animation("tutoAnimation", "rotation.y", 30, BABYLON.Animation
.ANIMATIONTYPE_FLOAT,
BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
// 动画帧数组
keys = [];
keys.push({
frame: 0,
value: 0
});
keys.push({
frame: 40,
value: Math.PI
});
keys.push({
frame: 80,
value: 0
});
// 向动画对象添加帧
animation2.setKeys(keys);
// 创建动画组
var animationGroup = new BABYLON.AnimationGroup("my group");
//只有box1在动
animationGroup.addTargetedAnimation(animation1, box1);
animationGroup.addTargetedAnimation(animation2, box1);
//两个box都在动
animationGroup.addTargetedAnimation(animation1, box1);
animationGroup.addTargetedAnimation(animation2, box2);
//两个box同频
animationGroup.addTargetedAnimation(animation1, box1);
animationGroup.addTargetedAnimation(animation2, box1);
animationGroup.addTargetedAnimation(animation2, box2);
// 确保将动画标准化为相同的时间线
animationGroup.normalize(0, 100);
(3)动画组的速率比:speedRatio
可以设置组中所有动画的 speedRatio
来加快或减慢动画播放
animationGroup1.speedRatio = 0.25;
animationGroup2.speedRatio = 3;
(4)从现有动画创建动画组
通过枚举动画表中包含的动画来从动画表中创建新的 AnimationGroup
var animationGroup = new BABYLON.AnimationGroup("my-animation-group");
for (anim of idleAnim.getAnimations()) {
animationGroup.addTargetedAnimation(anim.animation, anim.target);
}
(5)在动画结束时:onAnimationEndObservable
onAnimationEndObservable
可以用来在动画组结束时触发一个函数:
animationGroup1.onAnimationEndObservable.add(function () {
mesh2.material = redMaterial;
});
(6)在动画组循环时:onAnimationLoopObservable、onAnimationGroupLoopObservable
onAnimationLoopObservable
可用于在动画组循环时触发一个函数:
animationGroup1.onAnimationLoopObservable.add(function (targetAnimation) {
console.log(targetAnimation.animation.name);
});
onAnimationGroupLoopObservable
可用于在动画组的所有动画循环播放时触发一个函数:
animationGroup1.onAnimationGroupLoopObservable.add(function (group) {
console.log("Group looped!");
});
6. 合并动画
(1)如何合并动画
- 动画并行
只需设置多个动画并添加到 Babylon.js
对象的动画数组中即可实现多个动画并行执行。
1. 滑动和旋转并行
// 位置动画
var xSlide = new BABYLON.Animation("xSlide", "position.x", frameRate, BABYLON.Animation
.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
var keyFramesP = [];
keyFramesP.push({
frame: 0,
value: 2
});
keyFramesP.push({
frame: frameRate,
value: -2
});
keyFramesP.push({
frame: 2 * frameRate,
value: 2
});
xSlide.setKeys(keyFramesP);
// 旋转动画
var yRot = new BABYLON.Animation("yRot", "rotation.y", frameRate, BABYLON.Animation.ANIMATIONTYPE_FLOAT,
BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
var keyFramesR = [];
keyFramesR.push({
frame: 0,
value: 0
});
keyFramesR.push({
frame: frameRate,
value: Math.PI
});
keyFramesR.push({
frame: 2 * frameRate,
value: 2 * Math.PI
});
yRot.setKeys(keyFramesR);
scene.beginDirectAnimation(box, [xSlide, yRot], 0, 2 * frameRate, true);
2. 滑动和更快速旋转并行
将旋转值更改为更大的数字会增加旋转速率
3. 滑动和变化速率的旋转并行
(2)连续动画:beginDirectAnimation
为了让一个动画跟随另一个动画,需要向 beginDirectAnimation
函数添加另一个参数。
beginDirectAnimation 及其参数
scene.beginDirectAnimation(target, animations, start frame, end frame, loop, speed, on animation end)
target:object,动画目标
animations:array,应用于目标的所有动画
start frame:number,开始动画的帧
end frame:number,结束动画的帧
loop:boolean,可选,true表示循环动画
speed:number,可选,默认1匹配动画帧率,数字越大,动画越快,数字越小,动画越慢
on animation end:function,可选,动画结束时调用的函数,要求 loop
为 false
旋转然后滑动的动画:
scene.beginDirectAnimation(
box,
[yRot],
0,
2 * frameRate,
false,
1,
nextAnimation
);
var nextAnimation = function () {
scene.beginDirectAnimation(box, [xSlide], 0, 2 * frameRate, true);
};
盒子进入滑动循环后,旋转继续进行:
var nextAnimation = function () {
scene.beginDirectAnimation(box, [yRot, xSlide], 0, 2 * frameRate, true);
};
7. 动画角色
(1)用键盘控制动画角色的移动:animationGroups、ActionManager
导入模型并播放动画
确保添加 animationGroups
参数,因为它用于获取和播放动画。
BABYLON.SceneLoader.ImportMesh("", "https://assets.babylonjs.com/meshes/", "HVGirl.glb", scene, function (newMeshes, particleSystems, skeletons, animationGroups) {
var hero = newMeshes[0];
// 缩小模型
hero.scaling.scaleInPlace(0.1);
// 将相机锁定在角色上
camera1.target = hero;
// 获取 Samba 动画组
const sambaAnim = scene.getAnimationGroupByName("Samba");
// 播放 Samba 动画
sambaAnim.start(true, 1.0, sambaAnim.from, sambaAnim.to, false);
});
默认情况下,加载带有动画组的模型时,会播放 animationGroups[0] ,停止动画
animationGroups[0].stop();
通过键盘控制动画
Action Manager 可用于捕获键盘上按下的键
var inputMap = {};
scene.actionManager = new BABYLON.ActionManager(scene);
scene.a