【Laya】Component 使用说明

Laya.Component 使用说明

概述

Component 是所有组件的基类。组件是一种可以附加到节点(Node)上的功能模块,用于扩展节点的行为和能力。

在 LayaAir 引擎中,许多功能通过组件系统实现,例如:

  • 3D 渲染组件(MeshRendererSkinnedMeshRenderer
  • 物理组件(Rigidbody3DCollider
  • 脚本组件(继承自 Laya.Script
  • 动画组件(Animator

组件是 LayaAir 3.0 中实现游戏逻辑的核心机制,通过组合不同的组件可以构建复杂的游戏对象。

API 参考

属性

属性 类型 说明
owner Laya.Node 获取组件所属的节点(只读)
enabled boolean 是否启用组件
destroyed boolean 是否已经销毁(只读)
awaked boolean 是否已执行过 onAwake(只读)
id number 唯一标识 ID(只读)
hideFlags number 组件隐藏标志位

方法

方法 说明
destroy(): void 销毁组件
hasHideFlag(flag: number): boolean 检查是否有指定的隐藏标志

生命周期方法

方法 调用时机
onAdded(): void 被添加到节点后调用(即使节点未激活也会调用)
onAwake(): void 组件被激活后执行,此时所有节点和组件均已创建完毕,只执行一次
onEnable(): void 组件被启用后执行,比如节点被添加到舞台后
onDisable(): void 组件被禁用时执行,比如节点从舞台移除后
onDestroy(): void 被销毁时调用
onStart(): void 第一次执行 update 之前执行,只会执行一次
onUpdate(): void 每帧更新时执行
onLateUpdate(): void 每帧更新后执行
onPreRender(): void 渲染前执行

组件生命周期

复制代码
                    ┌─────────────┐
                    │  onAdded()  │  添加到节点时
                    └──────┬──────┘
                           │
                           ▼
                    ┌─────────────┐
                    │  onAwake()  │  只执行一次
                    └──────┬──────┘
                           │
                           ▼
                    ┌─────────────┐
                    │  onStart()  │  只执行一次
                    └──────┬──────┘
                           │
                           ▼
            ┌──────────────┴──────────────┐
            │                             │
            ▼                             ▼
     ┌──────────────┐             ┌──────────────┐
     │  onEnable()  │◄────────────►│  onDisable() │
     └──────┬───────┘              └──────────────┘
            │
            ▼
     ┌──────────────┐
     │  onUpdate()  │◄────── 每帧循环
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │onLateUpdate() │
     └──────┬───────┘
            │
            ▼
     ┌──────────────┐
     │ onPreRender() │
     └──────────────┘

     销毁时: onDestroy()

基本用法

1. 创建自定义组件

自定义组件通过继承 Laya.Component(或其子类如 Laya.Script)来实现:

typescript 复制代码
const { regClass, property } = Laya;

@regClass()
export class MyComponent extends Laya.Component {
    // 添加标签后可以在编辑器中设置
    @property(Number)
    private speed: number = 10;

    private _counter: number = 0;

    /**
     * 组件被激活后执行,只执行一次
     */
    onAwake(): void {
        console.log("组件已激活,owner:", this.owner.name);
    }

    /**
     * 被添加到节点后调用
     */
    onAdded(): void {
        console.log("组件已添加到节点");
    }

    /**
     * 组件启用时执行
     */
    onEnable(): void {
        console.log("组件已启用");
    }

    /**
     * 每帧更新
     */
    onUpdate(): void {
        this._counter += Laya.timer.delta * 0.001;
    }

    /**
     * 组件禁用时执行
     */
    onDisable(): void {
        console.log("组件已禁用");
    }

    /**
     * 组件销毁时执行
     */
    onDestroy(): void {
        console.log("组件已销毁");
    }
}

2. 添加组件到节点

typescript 复制代码
export class ComponentExample {
    private sprite: Laya.Sprite;

    constructor() {
        this.createSprite();
    }

    private createSprite(): void {
        this.sprite = new Laya.Sprite();
        this.sprite.graphics.drawRect(0, 0, 100, 100, "#FF5722");
        this.sprite.pos(200, 200);

        // 添加组件
        const component = this.sprite.addComponent(MyComponent);

        // 访问组件属性
        component.speed = 20;

        Laya.stage.addChild(this.sprite);
    }
}

3. 获取组件

typescript 复制代码
// 获取指定类型的组件
const myComponent = sprite.getComponent(MyComponent);
if (myComponent) {
    console.log("找到组件,speed:", myComponent.speed);
}

// 获取所有组件
const components = sprite.getComponents(Laya.Component);
console.log("组件数量:", components.length);

4. 移除组件

typescript 复制代码
// 移除指定类型的组件
const component = sprite.getComponent(MyComponent);
if (component) {
    component.destroy();
}

完整示例

示例1: 旋转组件

typescript 复制代码
const { regClass } = Laya;

@regClass()
export class RotateComponent extends Laya.Component {
    /** 旋转速度(度/秒) */
    public rotationSpeed: number = 90;

    onUpdate(): void {
        if (this.owner instanceof Laya.Sprite) {
            const sprite = this.owner as Laya.Sprite;
            sprite.rotation += this.rotationSpeed * Laya.timer.delta * 0.001;
        }
    }
}

export class RotateExample {
    private box: Laya.Sprite;

    constructor() {
        this.createBox();
    }

    private createBox(): void {
        this.box = new Laya.Sprite();
        this.box.graphics.drawRect(-50, -50, 100, 100, "#4CAF50");
        this.box.pos(Laya.stage.width / 2, Laya.stage.height / 2);
        this.box.pivotX = 50;
        this.box.pivotY = 50;

        // 添加旋转组件
        const rotator = this.box.addComponent(RotateComponent);
        rotator.rotationSpeed = 180;

        Laya.stage.addChild(this.box);
    }
}

示例2: 生命周期演示组件

typescript 复制代码
const { regClass } = Laya;

@regClass()
export class LifeCycleDemo extends Laya.Component {
    public componentName: string = "DemoComponent";

    private _logTime(): void {
        const time = new Date().toLocaleTimeString();
        console.log(`[${time}] ${this.componentName}`);
    }

    onAdded(): void {
        this._logTime();
        console.log("  → onAdded(): 组件已添加到节点");
    }

    onAwake(): void {
        this._logTime();
        console.log("  → onAwake(): 组件已激活");
    }

    onStart(): void {
        this._logTime();
        console.log("  → onStart(): 第一次更新前执行");
    }

    onEnable(): void {
        this._logTime();
        console.log("  → onEnable(): 组件已启用");
    }

    onUpdate(): void {
        // 仅演示,避免频繁输出
    }

    onDisable(): void {
        this._logTime();
        console.log("  → onDisable(): 组件已禁用");
    }

    onDestroy(): void {
        this._logTime();
        console.log("  → onDestroy(): 组件已销毁");
    }
}

export class LifeCycleExample {
    private sprite: Laya.Sprite;
    private component: LifeCycleDemo;

    constructor() {
        Laya.init(800, 600).then(() => {
            this.setupScene();
        });
    }

    private setupScene(): void {
        this.sprite = new Laya.Sprite();
        this.sprite.graphics.drawRect(0, 0, 100, 100, "#2196F3");
        this.sprite.pos(350, 250);

        // 添加生命周期演示组件
        this.component = this.sprite.addComponent(LifeCycleDemo);
        this.component.componentName = "蓝色方块组件";

        Laya.stage.addChild(this.sprite);

        // 3秒后禁用组件
        Laya.timer.once(3000, this, () => {
            this.component.enabled = false;
            console.log("--- 组件已禁用 ---");
        });

        // 5秒后重新启用
        Laya.timer.once(5000, this, () => {
            this.component.enabled = true;
            console.log("--- 组件已重新启用 ---");
        });

        // 7秒后销毁组件
        Laya.timer.once(7000, this, () => {
            this.component.destroy();
            console.log("--- 组件已销毁 ---");
        });
    }
}

示例3: 移动控制组件

typescript 复制代码
const { regClass } = Laya;

@regClass()
export class MoveControlComponent extends Laya.Component {
    /** 移动速度 */
    public moveSpeed: number = 200;

    private _direction: Laya.Vector3 = new Laya.Vector3();

    onAwake(): void {
        // 注册键盘事件
        Laya.stage.on(Laya.Event.KEY_DOWN, this, this.onKeyDown);
        Laya.stage.on(Laya.Event.KEY_UP, this, this.onKeyUp);
    }

    onKeyDown(e: Laya.Event): void {
        const code = e["keyCode"];
        switch (code) {
            case 37: // 左箭头
            case 65: // A
                this._direction.x = -1;
                break;
            case 39: // 右箭头
            case 68: // D
                this._direction.x = 1;
                break;
            case 38: // 上箭头
            case 87: // W
                this._direction.y = -1;
                break;
            case 40: // 下箭头
            case 83: // S
                this._direction.y = 1;
                break;
        }
    }

    onKeyUp(e: Laya.Event): void {
        const code = e["keyCode"];
        switch (code) {
            case 37: case 39: case 65: case 68:
                this._direction.x = 0;
                break;
            case 38: case 40: case 87: case 83:
                this._direction.y = 0;
                break;
        }
    }

    onUpdate(): void {
        if (this.owner instanceof Laya.Sprite) {
            const sprite = this.owner as Laya.Sprite;
            const delta = Laya.timer.delta * 0.001;

            sprite.x += this._direction.x * this.moveSpeed * delta;
            sprite.y += this._direction.y * this.moveSpeed * delta;
        }
    }

    onDestroy(): void {
        Laya.stage.off(Laya.Event.KEY_DOWN, this, this.onKeyDown);
        Laya.stage.off(Laya.Event.KEY_UP, this, this.onKeyUp);
    }
}

export class MoveControlExample {
    private player: Laya.Sprite;

    constructor() {
        this.createPlayer();
    }

    private createPlayer(): void {
        this.player = new Laya.Sprite();
        this.player.graphics.drawCircle(0, 0, 30, "#FF5722");
        this.player.pos(Laya.stage.width / 2, Laya.stage.height / 2);

        // 添加移动控制组件
        this.player.addComponent(MoveControlComponent);

        Laya.stage.addChild(this.player);
    }
}

示例4: 脉动动画组件

typescript 复制代码
const { regClass } = Laya;

@regClass()
export class PulseComponent extends Laya.Component {
    /** 脉动幅度 */
    public scaleAmount: number = 0.2;
    /** 脉动速度 */
    public pulseSpeed: number = 3;

    private _time: number = 0;
    private _baseScaleX: number = 1;
    private _baseScaleY: number = 1;

    onAwake(): void {
        if (this.owner instanceof Laya.Sprite) {
            const sprite = this.owner as Laya.Sprite;
            this._baseScaleX = sprite.scaleX;
            this._baseScaleY = sprite.scaleY;
        }
    }

    onUpdate(): void {
        if (this.owner instanceof Laya.Sprite) {
            this._time += Laya.timer.delta * 0.001;
            const sprite = this.owner as Laya.Sprite;

            const scaleOffset = Math.sin(this._time * this.pulseSpeed) * this.scaleAmount;
            sprite.scaleX = this._baseScaleX + scaleOffset;
            sprite.scaleY = this._baseScaleY + scaleOffset;
        }
    }
}

export class PulseExample {
    constructor() {
        this.createPulsingBox();
    }

    private createPulsingBox(): void {
        const box = new Laya.Sprite();
        box.graphics.drawRect(-50, -50, 100, 100, "#9C27B0");
        box.pos(Laya.stage.width / 2, Laya.stage.height / 2);

        const pulse = box.addComponent(PulseComponent);
        pulse.scaleAmount = 0.3;
        pulse.pulseSpeed = 4;

        Laya.stage.addChild(box);
    }
}

示例5: 组件间通信

typescript 复制代码
const { regClass } = Laya;

/**
 * 生命值组件
 */
@regClass()
export class HealthComponent extends Laya.Component {
    public maxHealth: number = 100;
    public currentHealth: number = 100;

    onAwake(): void {
        this.currentHealth = this.maxHealth;
    }

    public takeDamage(damage: number): void {
        this.currentHealth -= damage;
        console.log("受到伤害:", damage, "剩余血量:", this.currentHealth);

        if (this.currentHealth <= 0) {
            this.die();
        }
    }

    public heal(amount: number): void {
        this.currentHealth = Math.min(this.currentHealth + amount, this.maxHealth);
        console.log("恢复血量:", amount, "当前血量:", this.currentHealth);
    }

    private die(): void {
        console.log("角色死亡");
        // 可以派发事件或调用其他组件
    }
}

/**
 * 血条显示组件
 */
@regClass()
export class HealthBarComponent extends Laya.Component {
    private _barWidth: number = 100;
    private _barHeight: number = 10;
    private _background: Laya.Sprite;
    private _foreground: Laya.Sprite;
    private _healthComponent: HealthComponent;

    onAwake(): void {
        // 创建血条
        this._background = new Laya.Sprite();
        this._background.size(this._barWidth, this._barHeight);
        this._background.graphics.drawRect(0, 0, 1, 1, "#333333").percent = true;

        this._foreground = new Laya.Sprite();
        this._foreground.size(this._barWidth, this._barHeight);
        this._foreground.graphics.drawRect(0, 0, 1, 1, "#FF0000").percent = true;

        this.owner.addChild(this._background);
        this.owner.addChild(this._foreground);

        // 获取同一节点上的 HealthComponent
        this._healthComponent = this.owner.getComponent(HealthComponent);
    }

    onUpdate(): void {
        if (this._healthComponent) {
            const healthPercent = this._healthComponent.currentHealth / this._healthComponent.maxHealth;
            this._foreground.width = this._barWidth * healthPercent;
        }
    }
}

export class ComponentCommunicationExample {
    constructor() {
        this.createPlayer();
    }

    private createPlayer(): void {
        const player = new Laya.Sprite();
        player.graphics.drawRect(25, 40, 50, 50, "#4CAF50");
        player.pos(Laya.stage.width / 2, Laya.stage.height / 2);

        // 添加生命值组件
        const health = player.addComponent(HealthComponent);
        health.maxHealth = 100;

        // 添加血条组件
        player.addComponent(HealthBarComponent);

        Laya.stage.addChild(player);

        // 模拟受伤
        Laya.timer.once(2000, this, () => {
            health.takeDamage(30);
        });

        Laya.timer.once(4000, this, () => {
            health.takeDamage(50);
        });
    }
}

注意事项

1. 组件启用/禁用

typescript 复制代码
// 禁用组件(onDisable 会被调用)
component.enabled = false;

// 启用组件(onEnable 会被调用)
component.enabled = true;

// 实测没什么实际作用,可以当作标识符自行判断
if (component.enabled) {}

2. 获取 owner 节点

typescript 复制代码
// owner 是组件所属的节点
const nodeName = this.owner.name;
const parent = this.owner.parent;

3. 组件销毁

typescript 复制代码
// 组件销毁后,其引用可能变为无效
component.destroy();

// 检查组件是否已销毁
if (!component.destroyed) {
    // 安全使用
}

4. 访问其他组件

typescript 复制代码
// 获取同一节点上的其他组件
const health = this.owner.getComponent(HealthComponent);

// 获取节点及其子节点上的组件
const rigidbody = this.owner.getComponentInParent(Rigidbody3D);
const collider = this.owner.getComponentInChildren(Collider);

5. 组件与节点的关系

typescript 复制代码
// 组件必须添加到节点后才能正常工作
const component = new MyComponent();
// ❌ 错误:此时 owner 为 null

sprite.addComponent(MyComponent);
// ✅ 正确:现在 owner 有值

6. Script 类与 Component 类

Laya.ScriptLaya.Component 的子类,专门用于编写游戏逻辑脚本:

typescript 复制代码
// Script 继承自 Component,拥有相同的生命周期
@regClass()
export class MyScript extends Laya.Script {
    onAwake(): void {
        console.log("脚本已激活");
    }

    onUpdate(): void {
        // 每帧更新
    }
}
相关推荐
阿蒙Amon1 小时前
TypeScript学习-第10章:模块与命名空间
学习·ubuntu·typescript
小李也疯狂3 小时前
Unity 中的立方体贴图(Cubemaps)
unity·游戏引擎·贴图·cubemap
呆呆敲代码的小Y3 小时前
【Unity工具篇】| 超实用工具LuBan,快速上手使用
游戏·unity·游戏引擎·unity插件·luban·免费游戏·游戏配置表
EQ-雪梨蛋花汤3 小时前
【Unity优化】Unity多场景加载优化与资源释放完整指南:解决Additive加载卡顿、预热、卸载与内存释放问题
unity·游戏引擎
我的offer在哪里4 小时前
用 Unity 从 0 做一个「可以玩的」游戏,需要哪些步骤和流程
游戏·unity·游戏引擎
泡泡茶壶ᐇ4 小时前
Unity游戏开发入门指南:从零开始理解游戏引擎核心概念
unity·游戏引擎
Var_al6 小时前
抖小Unity WebGL分包命令行工具实践指南
unity·游戏引擎·webgl
VT.馒头6 小时前
【力扣】2695. 包装数组
前端·javascript·算法·leetcode·职场和发展·typescript
天人合一peng8 小时前
unity 通过代码修改button及其名字字体的属性
unity·游戏引擎
GLDbalala12 小时前
Unity基于自定义管线实现经典经验光照模型
unity·游戏引擎