【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 {
        // 每帧更新
    }
}
相关推荐
weixin_409383122 小时前
cocos shader三角流光
游戏引擎·cocos2d
EndingCoder2 小时前
类的继承和多态
linux·运维·前端·javascript·ubuntu·typescript
码界奇点4 小时前
基于Vue3与TypeScript的后台管理系统设计与实现
前端·javascript·typescript·vue·毕业设计·源代码管理
Mars-xq4 小时前
godot 毛玻璃效果着色器shader
游戏引擎·godot·着色器
Wect7 小时前
LeetCode 274. H 指数:两种高效解法全解析
算法·typescript
绀目澄清7 小时前
Unity 的AI Navigation 系统详细总结
人工智能·unity·游戏引擎
咖啡の猫7 小时前
TypeScript-Babel
前端·javascript·typescript
绀目澄清8 小时前
Unity3D AI Navigation 详解:从基础概念到实战应用
unity·游戏引擎
weixin_409383129 小时前
cocos shader流光文字 不显示透明部分
游戏引擎·cocos2d