脚本组件
我们已经在 组件 中介绍了组件的 生命周期,用户可以继承 ComponentBase 基类进行自定义组件的开发工作。用户可以通过覆写 (overwrite)
组件基类的生命周期函数来自定义运行逻辑:
初始化/卸载
: 如init
和destroy
状态变化
: 如start
,stop
,onEnable
和onDisable
更新逻辑
: 如onUpdate
,onLateUpdate
和onBeforeUpdate
基础用法
为实体添加自定义脚本
ts
class Script extends ComponentBase {
// 覆写 初始化
public init(){
// 该函数在组件被创建时调用,可以用来初始化内部的变量
// 注意,此时组件还没有被挂载到 Object3D 上,所以无法访问 this.object3D
}
// 覆写 渲染开始
public start(){
// 该函数在组件开始渲染前被调用,
// 此时可以访问 this.object3D, 可以用来获取节点的属性或其他组件
}
// 覆写 onUpdate
public onUpdate() {
// 每帧渲染循环调用,通常定义节点的循环逻辑
// 例如,每一帧更新物体旋转角度
this.object3D.rotationY += 1;
}
}
let ball: Object3D = new Object3D();
ball.addComponent( Script );
在自定义脚本中,可以通过 this.object3D
获取当前组件挂载到的 object3D
对象,从而进行对象状态的更改。
游戏/动画开发的一个关键点是在每一帧渲染前更新物体的行为,状态和方位。这些更新操作通常可以定义在组件自己的 onUpdate
回调中。引擎会自动注册 onUpdate
回调到主循环,实现每帧更新逻辑。
示例
下面我们通过三个不同的脚本动画示例来了解一下更复杂的脚本组件使用方法。
1. 光照动画脚本组件
ts
import { ComponentBase, Time, DirectLight, Color, LitMaterial, MeshRenderer, Scene3D, BoxGeometry, Object3D, Engine3D, Camera3D, HoverCameraController, View3D, AtmosphericComponent } from '@orillusion/core';
class LightAnimation extends ComponentBase {
private light: DirectLight;
private color: Color;
public start() {
this.light = this.object3D.getComponent(DirectLight);
this.color = this.light.lightColor;
}
public onUpdate() {
this.color.r = Math.pow(Math.sin(Time.time * 0.001), 10);
this.light.lightColor = this.color;
}
}
class UserLogic {
private scene: Scene3D;
private cube: Object3D;
private light: DirectLight;
init(scene3D: Scene3D) {
this.scene = scene3D;
this.createCube();
this.createLight();
this.createComponents();
}
private createCube() {
let mat = new LitMaterial();
mat.roughness = 1.0;
let obj: Object3D = new Object3D();
let geometry = new BoxGeometry(3, 3, 3);
let mr = obj.addComponent(MeshRenderer);
mr.material = mat;
mr.geometry = geometry;
this.scene.addChild(obj);
}
private createLight() {
let light: Object3D = new Object3D();
let component = light.addComponent(DirectLight);
light.rotationX = -45;
light.rotationZ = -45;
component.lightColor = new Color(1.0, 0, 0, 1);
component.intensity = 10;
this.scene.addChild(light);
this.light = component;
}
private createComponents() {
this.light.object3D.addComponent(LightAnimation);
}
async run() {
await Engine3D.init();
this.init(new Scene3D());
let cameraObj = new Object3D();
let camera = cameraObj.addComponent(Camera3D);
camera.perspective(60, Engine3D.aspect, 1, 5000.0);
let controller = camera.object3D.addComponent(HoverCameraController);
controller.setCamera(-135, 0, 15);
this.scene.addChild(cameraObj);
// add an Atmospheric sky enviroment
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);
}
}
new UserLogic().run();
ts
class LightAnimation extends ComponentBase {
private light: DirectLight;
private color: Color;
// 覆写 start 初始化变量
protected start() {
this.light = this.object3D.getComponent(DirectLight);
this.color = this.light.lightColor;
}
onUpdate() {
// 更新 lightColor
this.color.r = Math.pow(Math.sin(Time.time * 0.001), 10);
this.light.lightColor = this.color;
}
}
这里我们通过改变每一帧光照颜色的红色分量,使之随时间变化,从而产生最终光照动画的效果。
2. 材质动画脚本组件
ts
import { ComponentBase, Time, DirectLight, Color, LitMaterial, MeshRenderer, Scene3D, BoxGeometry, Object3D, Engine3D, Camera3D, HoverCameraController, BloomPost, View3D, AtmosphericComponent, PostProcessingComponent } from '@orillusion/core';
class MaterialAnimation extends ComponentBase {
private material: LitMaterial;
private time: number = 0;
start() {
let mr = this.object3D.getComponent(MeshRenderer);
this.material = mr.material as LitMaterial;
}
public onUpdate() {
let delta = Time.time * 0.001;
this.material.baseColor = new Color(Math.sin(delta), Math.cos(delta), Math.sin(delta));
}
}
class UserLogic {
private scene: Scene3D;
private cube: Object3D;
private light: DirectLight;
init(scene3D: Scene3D) {
this.scene = scene3D;
this.createCube();
this.createLight();
this.createComponents();
}
private createCube() {
let mat = new LitMaterial();
let obj: Object3D = new Object3D();
let geometry = new BoxGeometry(3, 3, 3);
let mr = obj.addComponent(MeshRenderer);
mr.material = mat;
mr.geometry = geometry;
this.cube = obj;
this.scene.addChild(obj);
}
private createLight() {
let light: Object3D = new Object3D();
let component = light.addComponent(DirectLight);
light.rotationX = 45;
light.rotationY = 30;
component.lightColor = new Color(1, 1, 1, 1);
component.intensity = 2;
this.scene.addChild(light);
this.light = component;
}
private createComponents() {
this.cube.addComponent(MaterialAnimation);
}
async run() {
await Engine3D.init();
this.init(new Scene3D());
let cameraObj = new Object3D();
let camera = cameraObj.addComponent(Camera3D);
camera.perspective(60, Engine3D.aspect, 1, 5000.0);
let controller = camera.object3D.addComponent(HoverCameraController);
controller.setCamera(45, 0, 15);
this.scene.addChild(cameraObj);
// add an Atmospheric sky enviroment
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);
// add a bloom post
let postProcessing = this.scene.addComponent(PostProcessingComponent);
postProcessing.addPost(BloomPost);
}
}
new UserLogic().run();
ts
class MaterialAnimation extends ComponentBase {
private material: LitMaterial;
// 覆写 start 初始化变量
protected start() {
let mr = this.object3D.getComponent(MeshRenderer);
this.material = mr.material;
}
onUpdate() {
// 更新 baseColor
let delta = Time.time * 0.001
this.material.baseColor = new Color(Math.sin(delta), Math.cos(delta), Math.sin(delta));
}
}
类似的,我们通过改变对象材质对象,比如跟随时间改变材质颜色,从而达到相应的动画效果。
3. 路径动画脚本组件
ts
import { ComponentBase, Time, DirectLight, Color, LitMaterial, MeshRenderer, Scene3D, BoxGeometry, Object3D, Engine3D, Camera3D, HoverCameraController, View3D, AtmosphericComponent } from '@orillusion/core';
class PathAnimation extends ComponentBase {
onUpdate() {
this.object3D.x = Math.sin(Time.time * 0.001) * 2;
this.object3D.y = Math.cos(Time.time * 0.001) * 2;
}
}
class UserLogic {
private scene: Scene3D;
private cube: Object3D;
private light: DirectLight;
init(scene3D: Scene3D) {
this.scene = scene3D;
this.createCube();
this.createLight();
this.createComponents();
}
private createCube() {
let mat = new LitMaterial();
let obj: Object3D = new Object3D();
let geometry = new BoxGeometry(3, 3, 3);
let mr = obj.addComponent(MeshRenderer);
mr.material = mat;
mr.geometry = geometry;
this.scene.addChild(obj);
this.cube = obj;
}
private createLight() {
let light: Object3D = new Object3D();
let component = light.addComponent(DirectLight);
light.rotationX = 45;
light.rotationY = 30;
component.lightColor = new Color(1.0, 1.0, 0, 1);
component.intensity = 2;
this.scene.addChild(light);
this.light = component;
}
private createComponents() {
this.cube.addComponent(PathAnimation);
}
async run() {
await Engine3D.init();
this.init(new Scene3D());
let cameraObj = new Object3D();
let camera = cameraObj.addComponent(Camera3D);
camera.perspective(60, Engine3D.aspect, 1, 5000.0);
let controller = camera.object3D.addComponent(HoverCameraController);
controller.setCamera(45, 0, 15);
this.scene.addChild(cameraObj);
// add an Atmospheric sky enviroment
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);
}
}
new UserLogic().run();
ts
class PathAnimation extends ComponentBase {
onUpdate() {
// 更新 Position
this.object3D.x = Math.sin(Time.time * 0.001) * 2;
this.object3D.y = Math.cos(Time.time * 0.001) * 2;
}
}
这次我们通过更改 Object3D
的 Position
属性,使物体随时间变化在 xy
平面内做一个圆周运动。