【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. 内存管理:频繁创建的对象建议配合对象池使用

相关文档

相关推荐
June bug4 小时前
【配环境】unity项目开发环境
unity·游戏引擎
李尚朋20216 小时前
搜嗖工具箱|小众有个性的趣味网站合集
深度学习·搜索引擎·游戏引擎
We་ct6 小时前
LeetCode 380. O(1) 时间插入、删除和获取随机元素 题解
前端·算法·leetcode·typescript
垂葛酒肝汤7 小时前
unity的背包滑动组件中道具的提示框被裁剪的问题
unity·游戏引擎
孟无岐8 小时前
【Laya】Ease 缓动函数
typescript·游戏引擎·游戏程序·laya
We་ct8 小时前
LeetCode 238. 除了自身以外数组的乘积|最优解详解(O(n)时间+O(1)空间)
前端·算法·leetcode·typescript
teunyu9 小时前
在Unity中使用LineRenderer实现A点到B点的贝塞尔曲线。并且曲线为虚线。方向为A点流向B点。效果图如下
unity·游戏引擎
速冻鱼Kiel9 小时前
GASP笔记03
笔记·ue5·游戏引擎·虚幻
踢球的打工仔9 小时前
typescript-类的静态属性和静态方法
前端·javascript·typescript