【Laya】ClassUtils 类反射工具

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

注意事项

  1. 必须使用 Laya.ClassUtils 前缀 :访问该类时需加上 Laya. 命名空间
  2. Runtime 继承问题:Runtime 类应该继承对应的组件类型,否则属性会失效
  3. 注册时机:类映射应在使用前完成注册,建议在游戏初始化时统一注册
  4. 类名区分:注意区分完整类名和别名,避免命名冲突
  5. 空值检查:获取类对象后应检查是否为 null
  6. 内存管理:频繁创建的对象建议配合对象池使用

相关文档

相关推荐
郝学胜-神的一滴13 小时前
游戏设计:原型与开发 18:Unity入门的第一课
人工智能·程序人生·游戏·ui·unity·游戏引擎
猫头_14 小时前
MCP 开发实战:把你自己的后端 API 接入 Agent
人工智能·typescript·mcp
淡海水15 小时前
09-01-内存-Unity资源生命周期全景
unity·c#·游戏引擎·.netcore·gc
长不大的杨瑞杰17 小时前
OpenCode v2 LSP 实战:内置语言服务器、自定义配置与诊断闭环
java·人工智能·typescript·lsp·opencode
Solange__18 小时前
AI世界模型、3D场景生成和游戏引擎怎么选?按5类任务比较交互、物理与导出
开发语言·人工智能·游戏·平面·3d·自然语言处理·游戏引擎
Highcharts.js18 小时前
Highcharts 集成到 React 或 Vue 项目中时,如何高效地使用 TypeScript技巧?
vue.js·人工智能·react.js·typescript·前端框架·highcharts
呆呆敲代码的小Y19 小时前
【实战精选面试题】Unity精选面试题详细解答,按模块整理汇总
人工智能·游戏·unity·游戏引擎
地狱为王1 天前
Unity使用VoxLock实现语音隔离
unity·游戏引擎·音频处理·voxlock
二哈赛车手1 天前
新人笔记---hermes定时功能设计,具体讲解以及可优化点
java·人工智能·笔记·spring·ai·typescript
jktantan2 天前
每个节点 41 字节:把一棵百万节点树拆进 14 个 typed array
前端·javascript·vue.js·算法·typescript