Laya.ClassUtils 类反射工具
简介
Laya.ClassUtils 是一个类反射和运行时管理的工具类。它提供了类映射注册、类查找、运行时类(Runtime)注册等功能,主要用于动态创建对象和管理 Prefab 的运行时逻辑类。
适用场景:
- 动态加载和创建 2D 节点类(Node系统:Sprite、Button、Label 等)
- Prefab 的 Runtime 类管理
- 类名到类对象的映射注册
- 通过字符串类名实例化对象
注意:LayaAir 有两套系统:
- Node 系统 :2D UI/场景节点,如 Sprite、Button、Label 等,继承自
Laya.Node - Component 系统 :3D 组件和脚本组件,继承自
Laya.Script
工作原理:
注册类映射 → 根据类名查找 → 获取类对象 → 动态实例化
核心优势:
| 优势 | 说明 |
|---|---|
| 动态创建 | 通过类名字符串动态创建对象 |
| 解耦管理 | 类名与类定义分离,便于管理 |
| Runtime 支持 | 为 Prefab 绑定运行时逻辑类 |
| 别名支持 | 支持短别名快速获取常用类 |
目录
API 参考
类映射方法
| 方法 | 参数 | 返回值 | 描述 |
|---|---|---|---|
regClass(className, classDef) |
string, any |
void |
注册类映射,建立类名与类定义的关联 |
getClass(className) |
string |
any |
根据类名获取类对象 |
Runtime 方法
| 方法 | 参数 | 返回值 | 描述 |
|---|---|---|---|
regRuntime(url, cls) |
string, Function |
void |
注册 Prefab 的 Runtime 运行时类 |
getRuntime(url) |
string |
Function |
根据 Prefab URL 获取 Runtime 类 |
基础用法
1. 注册和获取类映射
typescript
// 注册类映射
Laya.ClassUtils.regClass("MySprite", Laya.Sprite);
Laya.ClassUtils.regClass("laya.display.Sprite", Laya.Sprite);
// 获取类对象
let SpriteClass = Laya.ClassUtils.getClass("MySprite");
let sprite = new SpriteClass();
2. 使用短别名
typescript
// 为常用类注册简短别名
Laya.ClassUtils.regClass("Box", Laya.Box);
Laya.ClassUtils.regClass("Button", Laya.Button);
Laya.ClassUtils.regClass("Label", Laya.Label);
// 通过别名获取
let BoxClass = Laya.ClassUtils.getClass("Box");
let box = new BoxClass();
3. Runtime 类注册
typescript
// 为 Prefab 注册 Runtime 类
Laya.ClassUtils.regRuntime("scenes/GameScene.ls", GameSceneScript);
// 获取 Runtime 类
let runtimeClass = Laya.ClassUtils.getRuntime("scenes/GameScene.ls");
实用示例
示例1: 动态组件创建器
typescript
export class ComponentFactory {
// 初始化时注册所有组件类型
public static init(): void {
Laya.ClassUtils.regClass("Button", Laya.Button);
Laya.ClassUtils.regClass("CheckBox", Laya.CheckBox);
Laya.ClassUtils.regClass("Image", Laya.Image);
Laya.ClassUtils.regClass("Label", Laya.Label);
Laya.ClassUtils.regClass("Box", Laya.Box);
}
// 根据配置动态创建组件
public static createComponent(config: any): Laya.Node | null {
let className: string = config.type;
let properties: any = config.props || {};
// 获取类对象
let ComponentClass = Laya.ClassUtils.getClass(className);
if (!ComponentClass) {
console.error("未找到组件类:", className);
return null;
}
// 创建实例
let component = new ComponentClass() as Laya.Node;
// 设置属性
for (let key in properties) {
(component as any)[key] = properties[key];
}
return component;
}
}
// 使用示例
ComponentFactory.init();
let buttonConfig = {
type: "Button",
props: {
skin: "atlas/comp/button.png",
label: "点击我",
x: 100,
y: 100,
width: 120,
height: 40
}
};
let button = ComponentFactory.createComponent(buttonConfig);
Laya.stage.addChild(button);
示例2: UI 配置文件解析
typescript
export class UILoader {
// 从配置加载 UI
public static loadFromConfig(config: any): Laya.Box {
let container = new Laya.Box();
// 遍历配置中的子元素
if (config.children && Array.isArray(config.children)) {
for (let childConfig of config.children) {
let child = this.createComponent(childConfig);
if (child) {
container.addChild(child);
}
}
}
return container;
}
private static createComponent(config: any): Laya.Node | null {
let className = config.className;
if (!className) {
console.warn("组件缺少 className 属性");
return null;
}
// 获取类并创建
let ClassDef = Laya.ClassUtils.getClass(className);
if (!ClassDef) {
console.warn("未找到类:", className);
return null;
}
let component = new ClassDef() as Laya.Node;
// 设置基本属性
this.setProperties(component, config);
return component;
}
private static setProperties(target: any, config: any): void {
let props = ["x", "y", "width", "height", "pivotX", "pivotY", "scaleX", "scaleY", "rotation"];
for (let prop of props) {
if (config[prop] !== undefined) {
target[prop] = config[prop];
}
}
// 特殊属性处理
if (config.text !== undefined && target instanceof Laya.Label) {
target.text = config.text;
}
if (config.skin !== undefined) {
target.skin = config.skin;
}
}
}
// 配置示例
let uiConfig = {
children: [
{
className: "Label",
x: 50,
y: 50,
text: "玩家信息"
},
{
className: "Button",
x: 50,
y: 100,
width: 100,
height: 40,
skin: "atlas/comp/button.png",
label: "开始游戏"
}
]
};
let ui = UILoader.loadFromConfig(uiConfig);
Laya.stage.addChild(ui);
示例3: Prefab Runtime 管理
typescript
// 定义场景运行时脚本
@regClass()
export class GameSceneScript extends Laya.Script {
@property(Number)
public levelId: number = 1;
onStart(): void {
console.log("游戏场景启动,关卡ID:", this.levelId);
this.initGame();
}
private initGame(): void {
// 初始化游戏逻辑
}
}
// 注册 Runtime 类
export class RuntimeManager {
private static inited: boolean = false;
// 初始化所有 Runtime 映射
public static init(): void {
if (this.inited) return;
// 注册场景 Runtime
Laya.ClassUtils.regRuntime("scenes/MainScene.ls", MainSceneScript);
Laya.ClassUtils.regRuntime("scenes/GameScene.ls", GameSceneScript);
Laya.ClassUtils.regRuntime("scenes/ResultScene.ls", ResultSceneScript);
// 注册 Prefab Runtime
Laya.ClassUtils.regRuntime("prefabs/Player.lh", PlayerScript);
Laya.ClassUtils.regRuntime("prefabs/Enemy.lh", EnemyScript);
Laya.ClassUtils.regRuntime("prefabs/Bullet.lh", BulletScript);
this.inited = true;
console.log("Runtime 类注册完成");
}
// 检查 Runtime 是否注册
public static hasRuntime(url: string): boolean {
return Laya.ClassUtils.getRuntime(url) !== undefined;
}
}
// 游戏初始化时调用
RuntimeManager.init();
示例4: 类名验证工具
typescript
export class ClassValidator {
// 检查类是否已注册
public static isRegistered(className: string): boolean {
return Laya.ClassUtils.getClass(className) !== undefined;
}
// 安全获取类
public static safeGetClass(className: string): any | null {
try {
let cls = Laya.ClassUtils.getClass(className);
if (!cls) {
console.error("类未注册:", className);
return null;
}
return cls;
} catch (e) {
console.error("获取类失败:", className, e);
return null;
}
}
// 安全创建实例
public static safeCreate(className: string): any | null {
let ClassDef = this.safeGetClass(className);
if (!ClassDef) return null;
try {
return new ClassDef();
} catch (e) {
console.error("创建实例失败:", className, e);
return null;
}
}
// 批量注册
public static registerBatch(classMap: Record<string, any>): void {
for (let name in classMap) {
Laya.ClassUtils.regClass(name, classMap[name]);
}
console.log("批量注册完成,共", Object.keys(classMap).length, "个类");
}
}
// 批量注册使用
ClassValidator.registerBatch({
"Sprite": Laya.Sprite,
"Box": Laya.Box,
"Button": Laya.Button,
"Label": Laya.Label,
"Image": Laya.Image
});
// 安全创建
let button = ClassValidator.safeCreate("Button");
高级技巧
1. 使用常量管理类名
typescript
export class ClassNames {
// UI 组件
public static readonly BUTTON = "Button";
public static readonly LABEL = "Label";
public static readonly IMAGE = "Image";
public static readonly BOX = "Box";
public static readonly CHECK_BOX = "CheckBox";
// 游戏对象
public static readonly PLAYER = "Player";
public static readonly ENEMY = "Enemy";
public static readonly BULLET = "Bullet";
}
// 使用常量避免拼写错误
let cls = Laya.ClassUtils.getClass(ClassNames.BUTTON);
2. 自动注册系统
typescript
// 自动扫描并注册所有带 @regClass() 的类
export class AutoClassRegistry {
private static classList: Array<{ name: string, cls: any }> = [];
// 添加到注册列表
public static add(name: string, cls: any): void {
this.classList.push({ name: name, cls: cls });
}
// 执行批量注册
public static registerAll(): void {
for (let item of this.classList) {
Laya.ClassUtils.regClass(item.name, item.cls);
}
console.log("自动注册完成,共", this.classList.length, "个类");
}
}
// 在各组件类中使用
AutoClassRegistry.add("MyButton", MyButton);
AutoClassRegistry.add("MyLabel", MyLabel);
// 游戏启动时统一注册
AutoClassRegistry.registerAll();
3. Runtime 类自动绑定
typescript
export class RuntimeBinder {
// 自动扫描并绑定所有场景和 Prefab 的 Runtime
public static bindAll(): void {
// 场景绑定
this.bindScene("MainScene", MainSceneScript);
this.bindScene("GameScene", GameSceneScript);
this.bindScene("ResultScene", ResultSceneScript);
// Prefab 绑定
this.bindPrefab("Player", PlayerScript);
this.bindPrefab("Enemy", EnemyScript);
this.bindPrefab("Bullet", BulletScript);
}
private static bindScene(name: string, scriptClass: Function): void {
let url = "scenes/" + name + ".ls";
Laya.ClassUtils.regRuntime(url, scriptClass);
}
private static bindPrefab(name: string, scriptClass: Function): void {
let url = "prefabs/" + name + ".lh";
Laya.ClassUtils.regRuntime(url, scriptClass);
}
}
4. 类工厂模式
typescript
export class ClassFactory {
private static creators: Map<string, () => any> = new Map();
// 注册工厂函数
public static register(className: string, creator: () => any): void {
this.creators.set(className, creator);
}
// 创建实例
public static create(className: string): any | null {
let creator = this.creators.get(className);
if (creator) {
return creator();
}
// 回退到 ClassUtils
let ClassDef = Laya.ClassUtils.getClass(className);
if (ClassDef) {
return new ClassDef();
}
return null;
}
}
// 使用工厂函数创建复杂对象
ClassFactory.register("ComplexEnemy", () => {
let enemy = new Enemy();
enemy.hp = 200;
enemy.attack = 30;
enemy.speed = 2;
return enemy;
});
let enemy = ClassFactory.create("ComplexEnemy");
最佳实践
1. 统一初始化
typescript
// ✅ 正确:统一位置注册所有类
export class GameInit {
public static init(): void {
// 注册类映射
this.registerClasses();
// 注册 Runtime
this.registerRuntimes();
}
private static registerClasses(): void {
Laya.ClassUtils.regClass("Box", Laya.Box);
// ... 更多类
}
private static registerRuntimes(): void {
Laya.ClassUtils.regRuntime("scenes/Game.ls", GameScript);
// ... 更多 Runtime
}
}
2. 错误处理
typescript
// ✅ 正确:添加错误处理
let ClassDef = Laya.ClassUtils.getClass(className);
if (!ClassDef) {
console.error("类不存在:", className);
return null;
}
try {
let instance = new ClassDef();
return instance;
} catch (e) {
console.error("实例化失败:", e);
return null;
}
3. 命名规范
typescript
// ✅ 推荐:使用统一的命名规范
Laya.ClassUtils.regClass("UI.Button", Laya.Button);
Laya.ClassUtils.regClass("UI.Label", Laya.Label);
Laya.ClassUtils.regClass("Game.Player", Player);
Laya.ClassUtils.regClass("Game.Enemy", Enemy);
4. Runtime 继承注意
typescript
// ✅ 正确:Runtime 类继承对应组件类型
@regClass()
export class MyButton extends Laya.Button {
// 可以正常使用 Button 的所有属性
@property(String)
public customText: string = "";
}
// ❌ 错误:Runtime 不继承对应组件会导致属性失效
@regClass()
export class MyBox extends Laya.Script {
// 如果 Runtime 指向 Box,但这里继承 Script,
// Box 的属性在编辑器中设置会失效
}
5. 性能考虑
typescript
// ✅ 推荐:缓存获取的类对象
export class ClassCache {
private static cache: Map<string, any> = new Map();
public static getClass(className: string): any {
if (!this.cache.has(className)) {
let cls = Laya.ClassUtils.getClass(className);
this.cache.set(className, cls);
}
return this.cache.get(className);
}
}
注意事项
- 必须使用
Laya.ClassUtils前缀 :访问该类时需加上Laya.命名空间 - Runtime 继承问题:Runtime 类应该继承对应的组件类型,否则属性会失效
- 注册时机:类映射应在使用前完成注册,建议在游戏初始化时统一注册
- 类名区分:注意区分完整类名和别名,避免命名冲突
- 空值检查:获取类对象后应检查是否为 null
- 内存管理:频繁创建的对象建议配合对象池使用