Laya.Component 使用说明
概述
Component 是所有组件的基类。组件是一种可以附加到节点(Node)上的功能模块,用于扩展节点的行为和能力。
在 LayaAir 引擎中,许多功能通过组件系统实现,例如:
- 3D 渲染组件(
MeshRenderer、SkinnedMeshRenderer) - 物理组件(
Rigidbody3D、Collider) - 脚本组件(继承自
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.Script 是 Laya.Component 的子类,专门用于编写游戏逻辑脚本:
typescript
// Script 继承自 Component,拥有相同的生命周期
@regClass()
export class MyScript extends Laya.Script {
onAwake(): void {
console.log("脚本已激活");
}
onUpdate(): void {
// 每帧更新
}
}