Skip to content

脚本组件

我们已经在 组件 中介绍了组件的 生命周期,用户可以继承 ComponentBase 基类进行自定义组件的开发工作。用户可以通过覆写 (overwrite) 组件基类的生命周期函数来自定义运行逻辑:

  • 初始化/卸载: 如 initdestroy
  • 状态变化: 如 start,stop, onEnableonDisable
  • 更新逻辑: 如 onUpdate, onLateUpdateonBeforeUpdate

基础用法

为实体添加自定义脚本

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. 光照动画脚本组件


WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
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. 材质动画脚本组件


WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
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. 路径动画脚本组件


WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

<
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;
  }
}

这次我们通过更改 Object3DPosition 属性,使物体随时间变化在 xy 平面内做一个圆周运动。