Laya.Component 使用说明

简介

Laya.Component 是所有组件的基类,是 LayaAir 引擎中实现游戏逻辑的核心机制。通过将功能封装为组件并附加到节点上,可以灵活地构建复杂的游戏对象。

组件系统的优势:

  • 模块化:每个组件负责单一功能
  • 可复用:同一组件可添加到多个节点
  • 可组合:通过组合不同组件实现复杂行为
  • 易维护:组件之间相对独立,便于调试

常见组件类型:

  • 脚本组件(Laya.Script)
  • 渲染组件(MeshRenderer、Sprite2D)
  • 物理组件(Rigidbody3D、Collider)
  • 动画组件(Animator)

目录


API 参考

属性

属性类型只读说明
ownerLaya.Node✅组件所属的节点
enabledboolean❌是否启用组件
destroyedboolean✅是否已销毁
awakedboolean✅是否已执行过 onAwake
idnumber✅唯一标识 ID

方法

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

生命周期方法

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

组件生命周期

生命周期流程图

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

     销毁时: onDestroy()

生命周期详解

阶段方法说明
初始化onAdded()组件被添加到节点时调用,此时可能无法访问其他组件
onAwake()所有节点和组件创建完毕后调用,只执行一次,适合初始化
onStart()第一次更新前执行,适合获取其他组件的引用
运行时onEnable()组件启用时调用
onUpdate()每帧调用,用于游戏逻辑更新
onLateUpdate()每帧更新后调用,适合处理相机跟随等
onPreRender()渲染前调用
清理onDisable()组件禁用时调用
onDestroy()组件销毁时调用,释放资源

创建自定义组件

基本结构

const { regClass, property } = Laya;

@regClass()
export class MyComponent extends Laya.Component {
    // @property 标记的属性可在编辑器中设置
    @property(Number)
    public speed: number = 10;

    private _counter: number = 0;

    onAwake(): void {
        console.log("组件已激活,owner:", this.owner.name);
    }

    onUpdate(): void {
        this._counter += Laya.timer.delta * 0.001;
    }

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

继承 Script 类

Laya.Script 是 Laya.Component 的子类,专门用于编写游戏逻辑:

@regClass()
export class MyScript extends Laya.Script {
    declare owner: Laya.Sprite;  // 声明 owner 类型以便类型提示

    onAwake(): void {
        // this.owner 自动有类型提示
        this.owner.x = 100;
    }
}

组件管理

添加组件

// 方式一:添加组件并获取引用
const component = sprite.addComponent(MyComponent);

// 方式二:添加后获取
sprite.addComponent(MyComponent);
const component = sprite.getComponent(MyComponent);

// 设置组件属性
component.speed = 20;

获取组件

// 获取指定类型的组件(只获取第一个)
const myComponent = sprite.getComponent(MyComponent);

// 获取指定类型的所有组件
const components = sprite.getComponents(MyComponent);

// 获取节点及其父节点的组件
const parentComponent = sprite.getComponentInParent(MyComponent);

// 获取节点及其子节点的组件
const childComponent = sprite.getComponentInChildren(MyComponent);

移除组件

// 方式一:通过组件引用销毁
const component = sprite.getComponent(MyComponent);
if (component) {
    component.destroy();
}

// 方式二:通过节点移除
sprite.removeComponent(MyComponent);

// 移除所有指定类型的组件
sprite.removeComponents(MyComponent);

启用/禁用组件

const component = sprite.getComponent(MyComponent);

// 禁用组件(会触发 onDisable)
component.enabled = false;

// 启用组件(会触发 onEnable)
component.enabled = true;

// 检查组件是否启用
if (component.enabled) {
    // 组件处于启用状态
}

实用示例

示例1: 旋转组件

@regClass()
export class RotateComponent extends Laya.Component {
    @property(Number)
    public rotationSpeed: number = 90;

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

// 使用
const sprite = new Laya.Sprite();
sprite.graphics.drawRect(-50, -50, 100, 100, "#4CAF50");
sprite.pos(400, 300);
sprite.pivotX = 50;
sprite.pivotY = 50;

const rotator = sprite.addComponent(RotateComponent);
rotator.rotationSpeed = 180;

Laya.stage.addChild(sprite);

示例2: 键盘移动控制

@regClass()
export class KeyboardMoveComponent extends Laya.Component {
    @property(Number)
    public moveSpeed: number = 200;

    private _direction = { x: 0, y: 0 };

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

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

    private 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.offAllCaller(this);
    }
}

示例3: 脉动缩放效果

@regClass()
export class PulseComponent extends Laya.Component {
    @property(Number)
    public scaleAmount: number = 0.2;

    @property(Number)
    public pulseSpeed: number = 3;

    private _time: number = 0;
    private _baseScale: number = 1;

    onAwake(): void {
        if (this.owner instanceof Laya.Sprite) {
            this._baseScale = this.owner.scaleX;
        }
    }

    onUpdate(): void {
        this._time += Laya.timer.delta * 0.001;
        const offset = Math.sin(this._time * this.pulseSpeed) * this.scaleAmount;
        const scale = this._baseScale + offset;

        if (this.owner instanceof Laya.Sprite) {
            const sprite = this.owner as Laya.Sprite;
            sprite.scaleX = scale;
            sprite.scaleY = scale;
        }
    }
}

示例4: 生命值系统

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

    public currentHealth: number = 100;

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

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

        if (this.currentHealth <= 0) {
            this.owner.event("DEATH");
        }
    }

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

/**
 * 血条显示组件
 */
@regClass()
export class HealthBarComponent extends Laya.Component {
    private _background: Laya.Sprite;
    private _foreground: Laya.Sprite;
    private _health: HealthComponent;
    private _offsetY: number = -60;

    onAwake(): void {
        // 创建血条背景
        this._background = new Laya.Sprite();
        this._background.size(100, 10);
        this._background.graphics.drawRect(0, 0, 1, 1, "#333333").percent = true;
        this._background.pos(0, this._offsetY);
        this._background.pivotX = 50;

        // 创建血条前景
        this._foreground = new Laya.Sprite();
        this._foreground.size(100, 10);
        this._foreground.graphics.drawRect(0, 0, 1, 1, "#FF0000").percent = true;
        this._foreground.pos(0, this._offsetY);
        this._foreground.pivotX = 50;

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

        // 获取生命值组件
        this._health = this.owner.getComponent(HealthComponent);
    }

    onUpdate(): void {
        if (this._health) {
            const percent = this._health.currentHealth / this._health.maxHealth;
            this._foreground.width = 100 * percent;
        }
    }

    onDestroy(): void {
        this._background.destroy();
        this._foreground.destroy();
    }
}

// 使用示例
const player = new Laya.Sprite();
player.graphics.drawCircle(0, 0, 30, "#4CAF50");
player.pos(400, 300);

const health = player.addComponent(HealthComponent);
health.maxHealth = 100;

player.addComponent(HealthBarComponent);

player.on("DEATH", this, () => {
    console.log("玩家死亡");
    Laya.stage.removeChild(player);
});

Laya.stage.addChild(player);

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

示例5: 定时销毁

@regClass()
export class AutoDestroyComponent extends Laya.Component {
    @property(Number)
    public lifetime: number = 3;  // 存活时间(秒)

    private _elapsed: number = 0;

    onUpdate(): void {
        this._elapsed += Laya.timer.delta * 0.001;
        if (this._elapsed >= this.lifetime) {
            // 可以先播放销毁动画
            this.owner.destroy();
        }
    }
}

// 使用:创建 3 秒后自动销毁的特效
const effect = new Laya.Sprite();
effect.graphics.drawCircle(0, 0, 20, "#FFD700");
effect.pos(400, 300);

const autoDestroy = effect.addComponent(AutoDestroyComponent);
autoDestroy.lifetime = 3;

Laya.stage.addChild(effect);

最佳实践

1. 使用 declare 声明 owner 类型

@regClass()
export class MyScript extends Laya.Script {
    declare owner: Laya.Sprite;  // 提供类型提示

    onAwake(): void {
        this.owner.x = 100;  // 自动补全和类型检查
    }
}

2. 在 onStart 中获取组件引用

@regClass()
export class MyComponent extends Laya.Component {
    private _sprite: Laya.Sprite;
    private _renderer: MeshRenderer;

    onAwake(): void {
        // 此时其他组件可能还未初始化
    }

    onStart(): void {
        // 在 onStart 中获取其他组件更安全
        this._sprite = this.owner as Laya.Sprite;
        this._renderer = this.owner.getComponent(MeshRenderer);
    }
}

3. 及时清理事件监听

@regClass()
export class MyComponent extends Laya.Component {
    onAwake(): void {
        Laya.stage.on(Laya.Event.CLICK, this, this.onClick);
    }

    onDestroy(): void {
        // 销毁时移除监听,避免内存泄漏
        Laya.stage.off(Laya.Event.CLICK, this, this.onClick);
    }

    private onClick(): void {
        console.log("Clicked");
    }
}

4. 使用 @property 暴露可配置属性

@regClass()
export class EnemyComponent extends Laya.Component {
    @property(Number)
    public maxHealth: number = 100;

    @property(Number)
    public moveSpeed: number = 100;

    @property(Number)
    public attackDamage: number = 10;

    // 这些属性可在编辑器中设置,方便设计师调整
}

5. 组件间通信优先使用事件

// 好的做法:使用事件解耦
class HealthComponent extends Laya.Component {
    takeDamage(damage: number): void {
        this.currentHealth -= damage;
        this.owner.event("HEALTH_CHANGED", [this.currentHealth]);
    }
}

// 避免:直接调用其他组件
class HealthComponent extends Laya.Component {
    takeDamage(damage: number): void {
        this.currentHealth -= damage;
        const bar = this.owner.getComponent(HealthBarComponent);
        if (bar) bar.update();  // 耦合度高
    }
}

6. 避免在 onUpdate 中创建对象

// ❌ 错误:每帧创建对象
onUpdate(): void {
    const pos = new Laya.Vector2();
    pos.x = this.owner.x;
    pos.y = this.owner.y;
}

// ✅ 正确:复用对象
private _tempPos = new Laya.Vector2();

onUpdate(): void {
    this._tempPos.x = this.owner.x;
    this._tempPos.y = this.owner.y;
}

7. 检查组件是否已销毁

// 使用组件前检查
if (component && !component.destroyed) {
    component.doSomething();
}

// 在异步操作中检查
onDestroy(): void {
    Laya.timer.clear(this, this._timerHandle);
    Laya.stage.offAllCaller(this);
}

注意事项

  1. 组件必须添加到节点:owner 属性只有在组件添加到节点后才有效
  2. 生命周期顺序:onAwake → onStart → onEnable,其他组件的 onAwake 可能尚未执行
  3. 启用状态:enabled = false 会停止 onUpdate 等方法的调用
  4. 销毁检查:销毁后 destroyed 为 true,不应再访问组件属性
  5. 事件清理:在 onDestroy 中移除所有事件监听
  6. 性能考虑:避免在 onUpdate 中进行复杂计算或创建对象

相关文档

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐