【Laya】Script 使用说明
Laya.Script 是 LayaAir 引擎的核心脚本组件,专用于游戏逻辑开发。它继承自 Component 类,但提供了更丰富的生命周期方法和事件响应功能。通过 @regClass 装饰器可将脚本注册到 IDE,@property 装饰器则能暴露属性到编辑器面板。Script 支持完整的生命周期流程(onAwake、onUpdate 等)、鼠标/键盘事件、3D 物理碰撞检测等特性,同时区分
Laya.Script 使用说明
概述
Script 是所有脚本组件的父类,继承自 Component。与 Component 不同,Script 专门用于编写游戏逻辑,提供了丰富的生命周期方法和事件响应方法。
在 LayaAir 的 ECS(实体-组件-系统)架构中,Script 类承担了组件和系统的双重职责:
- 组件部分:通过属性字段(使用
@property装饰器)存储数据 - 系统部分:通过生命周期方法和事件方法处理逻辑
Script 与 Component 的区别
| 特性 | Component | Script |
|---|---|---|
| 用途 | 通用组件基类 | 专门用于编写游戏逻辑脚本 |
| owner 类型 | Node | Sprite | Sprite3D |
| 事件方法 | 无 | 支持鼠标、键盘、物理等事件方法 |
| 装饰器支持 | 基础支持 | 完整的 @regClass、@property 支持 |
装饰器
@regClass()
将脚本类注册到 IDE,使其可以在编辑器中使用:
const { regClass } = Laya;
@regClass()
export class MyScript extends Laya.Script {
// 脚本内容
}
@property()
将属性暴露到 IDE 属性面板:
const { regClass, property } = Laya;
@regClass()
export class MyScript extends Laya.Script {
@property(Number)
public speed: number = 10;
@property(String)
public playerName: string = "Player";
@property(Laya.Sprite3D)
public target: Laya.Sprite3D;
}
API 参考
属性
| 属性 | 类型 | 说明 |
|---|---|---|
owner |
Sprite | Sprite3D |
脚本所属的节点(只读) |
生命周期方法
| 方法 | 调用时机 |
|---|---|
onAdded(): void |
被添加到节点后调用(即使节点未激活也会调用) |
onReset(): void |
重置组件参数到默认值,实现此方法后组件可回收到对象池 |
onAwake(): void |
组件被激活后执行,此时所有节点和组件均已创建完毕,只执行一次 |
onEnable(): void |
组件被启用后执行,比如节点被添加到舞台后 |
onStart(): void |
第一次执行 onUpdate 之前执行,只会执行一次 |
onUpdate(): void |
每帧更新时执行 |
onLateUpdate(): void |
每帧更新时执行,在 onUpdate 之后 |
onPreRender(): void |
渲染之前执行 |
onPostRender(): void |
渲染之后执行 |
onDisable(): void |
组件被禁用时执行,比如节点从舞台移除后 |
onDestroy(): void |
手动调用节点销毁时执行 |
鼠标事件方法
| 方法 | 说明 |
|---|---|
onMouseDown(evt: Event): void |
鼠标按下时执行 |
onMouseUp(evt: Event): void |
鼠标抬起时执行 |
onRightMouseDown(evt: Event): void |
鼠标右键或中键按下时执行 |
onRightMouseUp(evt: Event): void |
鼠标右键或中键抬起时执行 |
onMouseMove(evt: Event): void |
鼠标在节点上移动时执行 |
onMouseOver(evt: Event): void |
鼠标进入节点时执行 |
onMouseOut(evt: Event): void |
鼠标离开节点时执行 |
onMouseDrag(evt: Event): void |
鼠标按住物体拖拽时执行 |
onMouseDragEnd(evt: Event): void |
鼠标拖拽结束后执行 |
onMouseClick(evt: Event): void |
鼠标点击时执行 |
onMouseDoubleClick(evt: Event): void |
鼠标双击时执行 |
onMouseRightClick(evt: Event): void |
鼠标右键点击时执行 |
键盘事件方法
| 方法 | 说明 |
|---|---|
onKeyDown(evt: Event): void |
键盘按下时执行 |
onKeyPress(evt: Event): void |
键盘产生一个字符时执行 |
onKeyUp(evt: Event): void |
键盘抬起时执行 |
3D 物理碰撞器事件
| 方法 | 说明 |
|---|---|
onCollisionEnter(other: Collider): void |
开始碰撞时执行,仅执行一次 |
onCollisionStay(other: Collider): void |
持续碰撞时执行,每帧都执行 |
onCollisionExit(other: Collider): void |
结束碰撞时执行,仅执行一次 |
3D/2D 物理触发器事件
| 方法 | 说明 |
|---|---|
onTriggerEnter(other: Collider): void |
开始触发时执行,仅执行一次 |
onTriggerStay(other: Collider): void |
持续触发时执行,每帧都执行(2D 传感器不支持) |
onTriggerExit(other: Collider): void |
结束触发时执行,仅执行一次 |
脚本生命周期流程
添加组件
│
▼
┌─────────────┐
│ onAdded() │ 添加到节点时(即使未激活)
└──────┬──────┘
│
▼
┌─────────────┐
│ onAwake() │ 只执行一次
└──────┬──────┘
│
▼
┌─────────────┐
│ onStart() │ 只执行一次
└──────┬──────┘
│
▼
◄────────┼────────►
│ │ │
│ ┌────┴────┐ │
│ │onEnable()│◄──┼──► onDisable()
│ └────┬────┘ │
│ │ │
└───────►│◄───────┘
│
▼
┌─────────┐
│onUpdate()│◄──── 每帧循环
└────┬────┘
│
▼
┌───────────────┐
│onLateUpdate() │
└───────┬───────┘
│
▼
┌───────────────┐
│ onPreRender() │
└───────────────┘
│
▼
[ 渲染 ]
│
▼
┌───────────────┐
│onPostRender() │
└───────────────┘
销毁时: onDestroy()
基本用法
1. 创建基础脚本
const { regClass } = Laya;
@regClass()
export class MyScript extends Laya.Script {
public speed: number = 10;
onAwake(): void {
console.log("脚本已激活");
}
onUpdate(): void {
// 每帧执行
}
}
2. 添加脚本到节点
// 2D 节点
const sprite = new Laya.Sprite();
sprite.addComponent(MyScript);
Laya.stage.addChild(sprite);
// 3D 节点
const sprite3D = new Laya.Sprite3D();
sprite3D.addComponent(MyScript);
scene3D.addChild(sprite3D);
3. 使用属性装饰器
const { regClass, property } = Laya;
@regClass()
export class PlayerScript extends Laya.Script {
@property(Number)
public moveSpeed: number = 5;
@property(String)
public playerName: string = "Hero";
@property({ type: Laya.Sprite3D })
public target: Laya.Sprite3D;
@property({ type: Laya.Prefab })
public bulletPrefab: Laya.Prefab;
onAwake(): void {
console.log("玩家:", this.playerName);
console.log("速度:", this.moveSpeed);
}
}
完整示例
示例1: 旋转控制脚本
const { regClass, property } = Laya;
@regClass()
export class RotationScript extends Laya.Script {
@property(Number)
public rotationSpeed: number = 90;
private _autoRotateSpeed: Laya.Vector3 = new Laya.Vector3(0, 0.25, 0);
private _isDragging: boolean = false;
private _lastMouseX: number = 0;
private _rotateDelta: Laya.Vector3 = new Laya.Vector3();
onAwake(): void {
Laya.stage.on(Laya.Event.MOUSE_DOWN, this, this.onMouseDown);
Laya.stage.on(Laya.Event.MOUSE_UP, this, this.onMouseUp);
}
onMouseDown(): void {
this._isDragging = true;
this._lastMouseX = Laya.stage.mouseX;
}
onMouseUp(): void {
this._isDragging = false;
}
onUpdate(): void {
const sprite3D = this.owner as Laya.Sprite3D;
if (this._isDragging) {
const deltaX = Laya.stage.mouseX - this._lastMouseX;
this._rotateDelta.setValue(0, deltaX * 0.2, 0);
sprite3D.transform.rotate(this._rotateDelta, false, false);
this._lastMouseX = Laya.stage.mouseX;
} else {
sprite3D.transform.rotate(this._autoRotateSpeed, false, false);
}
}
onDestroy(): void {
Laya.stage.off(Laya.Event.MOUSE_DOWN, this, this.onMouseDown);
Laya.stage.off(Laya.Event.MOUSE_UP, this, this.onMouseUp);
}
}
示例2: 鼠标拖拽脚本
const { regClass } = Laya;
@regClass()
export class DragScript extends Laya.Script {
private _offsetX: number = 0;
private _offsetY: number = 0;
onEnable(): void {
console.log("拖拽脚本已启用");
}
onMouseDown(evt: Laya.Event): void {
const sprite = this.owner as Laya.Sprite;
this._offsetX = sprite.x - evt.stageX;
this._offsetY = sprite.y - evt.stageY;
sprite.startDrag();
}
onMouseDrag(evt: Laya.Event): void {
const sprite = this.owner as Laya.Sprite;
sprite.pos(evt.stageX + this._offsetX, evt.stageY + this._offsetY);
}
onMouseUp(evt: Laya.Event): void {
const sprite = this.owner as Laya.Sprite;
sprite.stopDrag();
}
onDisable(): void {
console.log("拖拽脚本已禁用");
}
}
示例3: 键盘控制脚本
const { regClass, property } = Laya;
@regClass()
export class KeyboardControlScript extends Laya.Script {
@property(Number)
public moveSpeed: number = 200;
private _moveX: number = 0;
private _moveY: number = 0;
onKeyDown(evt: Laya.Event): void {
const code = evt["keyCode"];
switch (code) {
case 37: // 左
case 65: // A
this._moveX = -1;
break;
case 39: // 右
case 68: // D
this._moveX = 1;
break;
case 38: // 上
case 87: // W
this._moveY = -1;
break;
case 40: // 下
case 83: // S
this._moveY = 1;
break;
}
}
onKeyUp(evt: Laya.Event): void {
const code = evt["keyCode"];
switch (code) {
case 37: case 39: case 65: case 68:
this._moveX = 0;
break;
case 38: case 40: case 87: case 83:
this._moveY = 0;
break;
}
}
onUpdate(): void {
const sprite = this.owner as Laya.Sprite;
const delta = Laya.timer.delta * 0.001;
sprite.x += this._moveX * this.moveSpeed * delta;
sprite.y += this._moveY * this.moveSpeed * delta;
}
}
示例4: 生命周期演示脚本
const { regClass } = Laya;
@regClass()
export class LifeCycleDemoScript extends Laya.Script {
private _log(tag: string, message: string): void {
const time = new Date().toLocaleTimeString();
console.log(`[${time}] [${tag}] ${message}`);
}
onAdded(): void {
this._log("onAdded", "脚本已添加到节点");
}
onAwake(): void {
this._log("onAwake", "脚本已激活,只执行一次");
}
onEnable(): void {
this._log("onEnable", "脚本已启用");
}
onStart(): void {
this._log("onStart", "第一次更新前执行,只执行一次");
}
onUpdate(): void {
// 每帧执行,为避免刷屏不打印日志
}
onLateUpdate(): void {
// 每帧在 onUpdate 之后执行
}
onPreRender(): void {
// 渲染前执行
}
onPostRender(): void {
// 渲染后执行
}
onDisable(): void {
this._log("onDisable", "脚本已禁用");
}
onDestroy(): void {
this._log("onDestroy", "脚本已销毁");
}
}
示例5: 3D 物理碰撞器脚本
注意:
onCollisionEnter/Stay/Exit仅适用于 3D 物理碰撞器,且碰撞器的"是否为触发器"选项未勾选时才会触发。此类碰撞会产生实际的物理阻挡效果。
const { regClass } = Laya;
@regClass()
export class CollisionScript extends Laya.Script {
/**
* 3D 物理碰撞开始时调用
* @param collision 碰撞信息
*/
onCollisionEnter(collision: Laya.Collision): void {
const otherOwner = collision.other.owner;
console.log("碰撞开始,对方物体:", otherOwner.name);
}
/** 持续碰撞时每帧调用 */
onCollisionStay(collision: Laya.Collision): void {
// 持续碰撞中,可以在这里处理持续接触的逻辑
}
/** 碰撞结束时调用 */
onCollisionExit(collision: Laya.Collision): void {
const otherOwner = collision.other.owner;
console.log("碰撞结束,对方物体:", otherOwner.name);
}
}
使用方法:
- 在 3D 场景中选择一个 Sprite3D 节点
- 添加
PhysicsCollider组件 - 确保"是否为触发器"未勾选
- 添加上述
CollisionScript脚本 - 确保项目已启用物理引擎模块(项目设置 → 物理模块)
示例6: 触发器脚本(2D/3D通用)
注意:
onTriggerEnter/Stay/Exit用于触发器事件。勾选碰撞器的"是否为触发器"选项后,只会触发事件但不会产生实际的物理阻挡效果。
const { regClass } = Laya;
@regClass()
export class TriggerScript extends Laya.Script {
/**
* 进入触发区域时调用
* @param other 对方的碰撞器组件
* @param self 自身的碰撞器组件(可选)
* @param contact 接触点信息(可选)
*/
onTriggerEnter(other: any, self?: any, contact?: any): void {
// other 类型:3D 是 PhysicsColliderComponent,2D 是 ColliderBase
const otherOwner = other.owner;
console.log("进入触发区域:", otherOwner.name);
}
/** 持续在触发区域内时每帧调用(注意:2D 传感器模式下不触发此方法) */
onTriggerStay(other: any, self?: any, contact?: any): void {
// 持续在触发区域内
}
/** 离开触发区域时调用 */
onTriggerExit(other: any, self?: any, contact?: any): void {
const otherOwner = other.owner;
console.log("离开触发区域:", otherOwner.name);
}
}
重要提示:
- 2D 物理启用传感器(isSensor = true)后,
onTriggerStay不会被触发 - 3D 触发器和 2D 传感器的区别:触发器不产生物理阻挡,只检测碰撞并触发事件
示例7: 组件属性引用
const { regClass, property } = Laya;
@regClass()
export class ReferenceScript extends Laya.Script {
// 引用 2D 节点
@property({ type: Laya.Sprite })
public targetSprite: Laya.Sprite;
// 引用 3D 节点
@property({ type: Laya.Sprite3D })
public targetSprite3D: Laya.Sprite3D;
// 引用组件
@property({ type: Laya.Animator })
public animator: Laya.Animator;
// 引用 Prefab
@property({ type: Laya.Prefab })
public enemyPrefab: Laya.Prefab;
onAwake(): void {
// 使用引用的节点
if (this.targetSprite) {
this.targetSprite.alpha = 0.5;
}
// 使用引用的组件
if (this.animator) {
this.animator.play("run");
}
// 实例化 Prefab
if (this.enemyPrefab) {
const enemy = this.enemyPrefab.create() as Laya.Sprite3D;
(this.owner as Laya.Sprite3D).addChild(enemy);
}
}
}
示例8: 使用对象池的脚本
const { regClass } = Laya;
@regClass()
export class BulletScript extends Laya.Script {
public speed: number = 500;
public direction: Laya.Vector2 = new Laya.Vector2(0, -1);
public damage: number = 10;
private _lifeTime: number = 0;
private _maxLifeTime: number = 3; // 3秒后销毁
onEnable(): void {
this._lifeTime = 0;
console.log("子弹已启用");
}
onUpdate(): void {
const sprite = this.owner as Laya.Sprite;
const delta = Laya.timer.delta * 0.001;
// 移动
sprite.x += this.direction.x * this.speed * delta;
sprite.y += this.direction.y * this.speed * delta;
// 更新生存时间
this._lifeTime += delta;
if (this._lifeTime >= this._maxLifeTime) {
this.recoverToPool();
}
// 检查是否超出屏幕
if (sprite.y < -50 || sprite.y > Laya.stage.height + 50) {
this.recoverToPool();
}
}
onDisable(): void {
console.log("子弹已禁用,回收到对象池");
}
// 重置方法(对象池复用时调用)
onReset(): void {
this._lifeTime = 0;
this.damage = 10;
const sprite = this.owner as Laya.Sprite;
sprite.pos(0, 0);
}
private recoverToPool(): void {
// 回收到对象池
Laya.Pool.recover("Bullet", this.owner);
}
}
注意事项
1. owner 类型
Script 的 owner 属性类型为 Sprite | Sprite3D,使用时需要类型判断:
const sprite = this.owner as Laya.Sprite; // 2D
const sprite3D = this.owner as Laya.Sprite3D; // 3D
2. onUpdate 性能
避免在 onUpdate 中进行大循环或频繁调用 getComponent:
// ❌ 不推荐
onUpdate(): void {
const comp = this.owner.getComponent(SomeComponent); // 每帧查询
}
// ✅ 推荐
private _comp: SomeComponent;
onAwake(): void {
this._comp = this.owner.getComponent(SomeComponent); // 只查询一次
}
onUpdate(): void {
// 使用缓存的组件
}
3. 事件方法注册
脚本中的事件方法(如 onMouseDown)无需手动注册,引擎会自动调用:
// ✅ 自动调用,无需注册
onMouseDown(evt: Laya.Event): void {
console.log("鼠标按下");
}
// ❌ 不要手动注册这类方法
// Laya.stage.on(Laya.Event.MOUSE_DOWN, this, this.onMouseDown);
4. 2D 物理传感器注意事项
当 2D 物理碰撞体启用传感器时,onTriggerStay 事件不会被触发。
5. onKeyPress 限制
onKeyPress 只在产生字符时触发(如字母、数字),功能键(如 F1-F12、方向键)不会触发此方法。
6. 装饰器必须成对使用
// ✅ 正确
const { regClass, property } = Laya;
@regClass()
export class MyScript extends Laya.Script {
@property(Number)
public speed: number = 10;
}
// ❌ 错误 - 缺少解构
@regClass()
export class MyScript extends Laya.Script {
@property(Number) // ReferenceError: property is not defined
}
更多推荐



所有评论(0)