Laya.Component 使用说明

简介

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

组件系统的优势

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

常见组件类型

  • 脚本组件(Laya.Script
  • 渲染组件(MeshRendererSprite2D
  • 物理组件(Rigidbody3DCollider
  • 动画组件(Animator

目录


API 参考

属性

属性 类型 只读 说明
owner Laya.Node 组件所属的节点
enabled boolean 是否启用组件
destroyed boolean 是否已销毁
awaked boolean 是否已执行过 onAwake
id number 唯一标识 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.ScriptLaya.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. 生命周期顺序onAwakeonStartonEnable,其他组件的 onAwake 可能尚未执行
  3. 启用状态enabled = false 会停止 onUpdate 等方法的调用
  4. 销毁检查:销毁后 destroyedtrue,不应再访问组件属性
  5. 事件清理:在 onDestroy 中移除所有事件监听
  6. 性能考虑:避免在 onUpdate 中进行复杂计算或创建对象

相关文档

Logo

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

更多推荐