属性动画(PropertyAnimation)
属性动画组件 PropertyAnimation 通过持续更改对象的属性值来变更目标的状态,实现动画效果。
应用举例
Object3D
的属性:position
,scale
,rotation
材质球
中的贴图:uv
,offset
,tiling
后处理效果
参数:color
,strength
以上属性都可以提供为属性动画更改的内容。
基本使用
指定场景中的某个节点 Object3D
,为其添加组件 PropertyAnimation
;然后给组件追加影片剪辑后即能使用。
TIP
目前引擎只支持通过 Unity
工具导入经过 Curve
曲线或者 Animation
数据导出 Clip
素材,暂不支持代码中自定义属性动画,后续版本会加入
cs
// Unity中导出clip的及脚本
public class AnimationInfo : InfoBase
{
[SerializeField] public Animation animation;
[SerializeField] public List<AnimationClip> clipList;
public string WriteJson()
{
string ret = JsonUtility.ToJson(this);
return ret;
}
}
ts
// load test model
let node = new Object3D();
scene.addChild(node);
// 添加组件
let animation = node.addComponent(PropertyAnimation);
// 加载clip素材
let res = await fetch('path/to/clip.json')
let json = await res.json()
// 初始化clip
let animClip = new PropertyAnimClip();
// 解析clip
animClip.parse(json);
animClip.wrapMode = WrapMode.Once;
animation.defaultClip = animClip.name;
animation.autoPlay = false;
// 将clip追加至组件
animation.appendClip(animClip);
播放动画
可以使用 play 方法来播放对应名称 (name)
的 PropertyAnimClip
。
ts
animation.play('anim_0', true); // 默认 true 从头播放
暂停动画
可以使用 stop 方法来播放指定的 PropertyAnimClip
。
ts
animation.stop();
切换动画
可以使用 toggle 方法来播放已暂停的动画,或暂停正在播放的动画。
ts
animation.toggle();
指定动画时间
可以使用 seek 方法来指定动画播放起始时间。
ts
animation.seek(1.2);//指定到1.2s
获取影片剪辑
使用 getClip 方法,获取组件已绑定过的影片剪辑 PropertyAnimClip
,参数为 PropertyAnimClip
的 name
。
ts
let clip: PropertyAnimClip = animation.getClip('anim_0');
获取当前剪辑
可以使用 currentClip 方法来获取当前正在播放的 PropertyAnimClip
。
ts
const currentClip = animation.currentClip;
获取动画时间
可以使用 time 方法来获取当前时间。
ts
const currentClip = animation.time;
示例
ts
import { DirectLight, Engine3D, AtmosphericComponent, View3D, HoverCameraController, KelvinUtil, Object3D, Scene3D, CameraUtil, webGPUContext, PropertyAnimation, PropertyAnimClip, WrapMode } from '@orillusion/core';
import * as dat from 'dat.gui';
class Sample_PropertyAnim {
lightObj: Object3D;
scene: Scene3D;
private animation: PropertyAnimation;
constructor() {}
async run() {
await Engine3D.init();
this.scene = new Scene3D();
let camera = CameraUtil.createCamera3DObject(this.scene, 'camera');
camera.perspective(60, webGPUContext.aspect, 1, 2000.0);
let ctrl = camera.object3D.addComponent(HoverCameraController);
ctrl.setCamera(180, -20, 15);
await this.initScene(this.scene);
this.scene.addComponent(AtmosphericComponent).sunY = 0.6;
// create a view with target scene and camera
let view = new View3D();
view.scene = this.scene;
view.camera = camera;
// start render
Engine3D.startRenderView(view);
let guiData = {
click: () => this.animation.play('anim_0', true),
Seek: 0,
Speed: 1
};
const GUIHelp = new dat.GUI();
GUIHelp.add(guiData, 'click').name('Restart');
GUIHelp.add(guiData, 'Seek', 0, 4, 0.01).onChange((v) => {
this.animation.stop();
this.animation.seek(v);
});
GUIHelp.add(guiData, 'Speed', 0, 1, 0.01).onChange((v) => {
this.animation.speed = v;
});
this.animation.onLateUpdate = () => {
guiData.Seek = this.animation.time;
GUIHelp.updateDisplay();
};
}
private async makePropertyAnim(node: Object3D) {
// add PropertyAnimation
let animation = node.addComponent(PropertyAnimation);
// load clip source
let res = await fetch('https://cdn.orillusion.com/json/anim_0.json');
let json = await res.json();
// init clip
let animClip = new PropertyAnimClip();
// parse clip
animClip.parse(json);
animClip.wrapMode = WrapMode.Loop;
animation.defaultClip = animClip.name;
animation.autoPlay = true;
// add clip to animation
animation.appendClip(animClip);
return animation;
}
async initScene(scene: Scene3D) {
/******** light *******/
{
this.lightObj = new Object3D();
this.lightObj.x = 0;
this.lightObj.y = 30;
this.lightObj.z = -40;
this.lightObj.rotationX = 45;
this.lightObj.rotationY = 0;
this.lightObj.rotationZ = 45;
let lc = this.lightObj.addComponent(DirectLight);
lc.lightColor = KelvinUtil.color_temperature_to_rgb(5355);
lc.intensity = 2;
scene.addChild(this.lightObj);
}
let duck = await Engine3D.res.loadGltf('https://cdn.orillusion.com/PBR/Duck/Duck.gltf');
this.scene.addChild(duck);
duck.scaleX = duck.scaleY = duck.scaleZ = 0.02;
this.animation = await this.makePropertyAnim(duck);
this.animation.play(this.animation.defaultClip);
return true;
}
}
new Sample_PropertyAnim().run();