关于智能注册cc节点的懒人方法,适合所有开发者

每个开发者在调用UI节点的时候都需要提前定义:
@property({type:Node})
private target: Node = null;

如果一个场景或预制体内有很多节点都要调用,就要定义N次
我觉得应该有更好的办法实现智能解析绑定这些节点。

目前我自己实现的懒人方式,如下:
1、如果你要调用某个节点,那么在创建这个UI节点时前面加个@符号,如:
@loginBtn这个按钮节点位于Login这个根节点

2、然后我在绑定的ts脚本写了一段遍历注册所有@开头节点的代码:
/**

     * 注册预制体的所有以@开头的子节点。

     * @param rootNode 预制体的根节点。

     */

    public registerNodes(rootNode: Node): void {

        if (!rootNode) {

            logMgr.err("根节点不存在。");

            return;

        }

        // 从根节点开始递归遍历并注册节点,传递根节点名称作为初始路径

        this.traverseAndRegister(rootNode, rootNode.name);

    }

    /**

     * 递归遍历所有子节点并注册以@开头的节点。

     * 使用节点的完整路径作为键。

     * @param node 当前遍历的节点。

     * @param path 当前节点的完整路径。

     */

    private traverseAndRegister(node: Node, path: string): void {

        // 如果节点名称以@开头,则使用当前的完整路径作为键

        if (node.name.startsWith('@')) {

            this._nodeMap.set(path, node);

            logMgr.debug(`注册节点 ${path}`);

        }

        // 遍历并递归注册子节点,更新路径以包含当前节点的名称

        node.children.forEach(child => {

            const childPath = `${path}/${child.name}`;

            this.traverseAndRegister(child, childPath);

        });

    }

/**

     * 根据节点的完整路径获取节点。

     * @param path 节点的完整路径。

     * @returns 对应的节点或未找到时返回undefined。

     */

    public getNode(path: string): Node | undefined {

        return this._nodeMap.get(path);

    }

这样我就实现了把Login预制体下所有@开头的节点都注册了
以后在任何地方只要使用:
xxx.getNode("Login/@loginBtn");

--------------------------------------------------------具体的代码,如下:
import { Node } from ‘cc’;

import { logMgr } from './LogMgr';

import { audioMgr } from './AudioMgr';

import { eventMgr } from './EventMgr';

/**

 * 组件管理器

 * 提供遍历预制体的所有子节点功能,并允许通过定义的键映射操作这些节点。

 */

class CompMgr {

    private static _instance: CompMgr; // 单例实例

    private _nodeMap: Map<string, Node> = new Map(); // 存储节点名称到节点的映射

    // 私有构造函数,确保外部无法直接通过new创建实例

    private constructor() {}

    // 获取单例实例

    public static get instance(): CompMgr {

        if (!this._instance) {

            this._instance = new CompMgr();

        }

        return this._instance;

    }

    /**

     * 注册预制体的所有以@开头的子节点。

     * @param rootNode 预制体的根节点。

     */

    public registerNodes(rootNode: Node): void {

        if (!rootNode) {

            logMgr.err("根节点不存在。");

            return;

        }

        // 从根节点开始递归遍历并注册节点,传递根节点名称作为初始路径

        this.traverseAndRegister(rootNode, rootNode.name);

    }

    /**

     * 递归遍历所有子节点并注册以@开头的节点。

     * 使用节点的完整路径作为键。

     * @param node 当前遍历的节点。

     * @param path 当前节点的完整路径。

     */

    private traverseAndRegister(node: Node, path: string): void {

        // 如果节点名称以@开头,则使用当前的完整路径作为键

        if (node.name.startsWith('@')) {

            this._nodeMap.set(path, node);

            logMgr.debug(`注册节点 ${path}`);

        }

        // 遍历并递归注册子节点,更新路径以包含当前节点的名称

        node.children.forEach(child => {

            const childPath = `${path}/${child.name}`;

            this.traverseAndRegister(child, childPath);

        });

    }

    /**

     * 根据节点的完整路径获取节点。

     * @param path 节点的完整路径。

     * @returns 对应的节点或未找到时返回undefined。

     */

    public getNode(path: string): Node | undefined {

        return this._nodeMap.get(path);

    }

    /**

     * 清除所有以指定根节点名称为前缀的子节点。

     * @param rootNode 根节点。

     */

    public removeNode(rootNode: Node): void {

        const keysToRemove: string[] = [];

        // 查找所有以rootNode.name为前缀的键

        this._nodeMap.forEach((value, key) => {

            if (key.startsWith(`${rootNode.name}/`)) {

                keysToRemove.push(key);

            }

        });

        // 移除找到的键

        keysToRemove.forEach(key => {

            this._nodeMap.delete(key);

            logMgr.debug(`移除节点 ${key}`);

        });

        if (keysToRemove.length > 0) {

            logMgr.debug(`已清除所有以 ${rootNode.name} 为前缀的节点`);

        } else {

            logMgr.warn(`未找到以 ${rootNode.name} 为前缀的节点`);

        }

    }

    /**

     * 清除所有注册的节点,释放内存。

     */

    public clearAllNodes(): void {

        this._nodeMap.clear();

        logMgr.debug("清除所有节点");

    }

    /**

     * 为指定的按钮设置点击播放音效并派发事件。

     * @param tag 事件标签,用于把事件交给事件管理器统一处理。

     * @param btnNode 按钮节点。

     * @param bundleName 资源包名称,默认为 'resources'。

     * @param name 音效资源名称,默认为 "audio/btn_click"。

     */

    public setBtnSound(

        tag: string,

        btnNode: Node,

        bundleName: string = 'resources',

        name: string = "audio/btn_click"

    ): void {

        btnNode.on(Node.EventType.TOUCH_START, () => {

            // 播放点击音效

            audioMgr.playEffect(bundleName, name);

            // 派发点击事件

            eventMgr.emit(tag);

        }, this);

    }

}

// 导出CompMgr的单例实例

export const compMgr = CompMgr.instance;

嗯…其实如果时最方便的话,this.loginBtn 直接获取是不是更方便一点呢

1赞

确实,,,

我目的是想偷懒,自己注册所有@开头节点,不用手动一个个写
给你看个例子

1
2

我使用的时候直接
3

这个还是得写路径啊

https://forum.cocos.org/t/topic/137317?u=812289303
这个比较方便