// 导入 Cocos Creator 核心模块 const { _decorator, Component, EventTouch, find, instantiate, Node, Animation, Prefab, Size, UITransform, v3, Vec3, AnimationClip } = cc; /** * 视图工具类 - 提供节点查找、组件操作、坐标转换、预制创建等常用功能 */ const ViewUtil = { /** * 查找节点 * @param {Node} curNode 当前节点 * @param {string} path 从当前节点开始的路径 * @returns {Node|null} 找到的节点(未找到返回null) **/ findNode(curNode, path) { if (!curNode) { console.error(`当前节点为空:${path}`); return null; } let node = find(path, curNode); if (node) { return node; } console.error(`未找到节点:${path},当前节点:${curNode.name}`); return null; }, /** * 查找组件 * @param {Node} curNode 当前节点 * @param {string} path 从当前节点开始的路径 * @param {Function} type 组件构造函数(如 cc.Button) * @returns {Component|null} 找到的组件(未找到返回null) */ findComponent(curNode, path, type) { let node = this.findNode(curNode, path); if (node) { return node.getComponent(type); } console.error(`未找到组件:${path}`); return null; }, /** * 添加组件(若节点已存在该组件则直接返回,否则新增组件) * @param {Node} curNode 当前节点 * @param {string} path 从当前节点开始的路径 * @param {Function} type 组件构造函数(如 cc.Button) * @returns {Component|null} 添加/获取的组件(失败返回null) */ addComponent(curNode, path, type) { let node = this.findNode(curNode, path); if (node) { let compt = node.getComponent(type) || node.addComponent(type); return compt; } console.error(`添加组件失败:${path}`); return null; }, /** * 把Node当前的节点树结构根据Node命名转成一个Map对象,重名的组件会覆盖 * Node的name不应该包含空格键,否则将跳过 * @param {Node} parent 被遍历的Node组件 * @param {Map} obj 绑定的Map对象 (可选) * @returns {Map|null} 存储节点名与节点对应关系的Map */ nodeTreeInfoLite(parent, obj) { let map = obj || new Map(); let items = parent.children; for (let i = 0; i < items.length; i++) { let _node = items[i]; if (_node.name.indexOf(" ") < 0) { map.set(_node.name, _node); } ViewUtil.nodeTreeInfoLite(_node, map); } return map; }, /** * 正则搜索节点名字,符合条件的节点将会返回 * @param {RegExp} reg 正则表达式 * @param {Node} parent 要搜索的父节点 * @param {Array} nodes 返回的数组(可选) * @returns {Array} 符合正则条件的节点数组 */ findNodes(reg, parent, nodes) { let ns = nodes || []; let items = parent.children; for (let i = 0; i < items.length; i++) { let _name = items[i].name; if (reg.test(_name)) { ns.push(items[i]); } ViewUtil.findNodes(reg, items[i], ns); } return ns; }, /** * 节点之间坐标互转 * @param {Node} a A节点 * @param {Node} b B节点 * @param {Vec3} aPos A节点空间中的相对位置 * @returns {Vec3} 转换到B节点空间的位置 */ calculateASpaceToBSpacePos(a, b, aPos) { const world = a.getComponent(UITransform).convertToWorldSpaceAR(aPos); return b.getComponent(UITransform).convertToNodeSpaceAR(world); }, /** * 屏幕转空间坐标 * @param {EventTouch} event 触摸事件 * @param {Node} space 转到此节点的坐标空间 * @returns {Vec3} 转换后的节点空间坐标 */ calculateScreenPosToSpacePos(event, space) { const uil = event.getUILocation(); const worldPos = v3(uil.x, uil.y); return space.getComponent(UITransform).convertToNodeSpaceAR(worldPos); }, /** * 显示对象等比缩放 * @param {number} targetWidth 目标宽 * @param {number} targetHeight 目标高 * @param {number} defaultWidth 默认宽 * @param {number} defaultHeight 默认高 * @returns {Size} 等比缩放后的尺寸 */ uniformScale(targetWidth, targetHeight, defaultWidth, defaultHeight) { const widthRatio = defaultWidth / targetWidth; const heightRatio = defaultHeight / targetHeight; let ratio; widthRatio < heightRatio ? ratio = widthRatio : ratio = heightRatio; return new Size(Math.floor(targetWidth * ratio), Math.floor(targetHeight * ratio)); }, // /** // * 从资源缓存中找到预制资源名并创建一个显示对象 // * (建议使用GameComponent里的同名方法,能自动管理内存释放) // * @param {string} path 资源路径 // * @param {string} bundleName 资源包名(默认使用resLoader的默认包名) // * @returns {Node|null} 创建的预制节点(未找到资源返回null) // */ // createPrefabNode(path, bundleName = resLoader.defaultBundleName) { // // 通过路径与类型获取已缓存资源 // const p = resLoader.get(path, Prefab, bundleName); // if (p) { // return instantiate(p); // } // console.warn(`未找到预制资源:${path},是否在预加载common文件夹之前调用?`); // return null; // }, // /** // * 异步加载预制并创建预制节点 // * (建议使用GameComponent里的同名方法,能自动管理内存释放) // * @param {string} path 资源路径 // * @param {string} bundleName 资源包名(默认使用resLoader的默认包名) // * @returns {Promise} 创建的预制节点Promise // */ // createPrefabNodeAsync(path, bundleName = resLoader.defaultBundleName) { // return new Promise(async (resolve, reject) => { // try { // const p = await resLoader.loadAsync(bundleName, path, Prefab); // if (p) { // resolve(instantiate(p)); // } else { // console.error(`名为【${path}】的资源加载失败`); // resolve(null); // } // } catch (error) { // console.error(`预制资源异步加载异常:${path}`, error); // resolve(null); // } // }); // }, // /** // * 添加节点动画 // * @param {string} path 资源路径 // * @param {Node} node 目标节点 // * @param {boolean} onlyOne 是否唯一(默认true,避免重复播放) // * @param {boolean} isDefaultClip 是否播放默认动画剪辑(默认false) // */ // addNodeAnimation(path, node, onlyOne = true, isDefaultClip = false) { // if (!node || !node.isValid) { // return; // } // let anim = node.getComponent(Animation); // if (anim == null) { // anim = node.addComponent(Animation); // } // const clip = resLoader.get(path, AnimationClip); // if (!clip) { // return; // } // if (onlyOne && anim.getState(clip.name) && anim.getState(clip.name).isPlaying) { // return; // } // if (isDefaultClip) { // anim.defaultClip = clip; // anim.play(); // return; // } // // 播放完成后恢复播放默认动画 // anim.once(Animation.EventType.FINISHED, () => { // if (anim.defaultClip) { // anim.play(); // } // }, this); // if (anim.getState(clip.name)) { // anim.play(clip.name); // return; // } // anim.createState(clip, clip.name); // anim.play(clip.name); // } }; // 导出工具类,供其他文件导入 module.exports = { ViewUtil };//使用 CommonJS 模块导出方式