diff --git a/main.js b/main.js index 6b1c5aa5988483eeb0adb0e2d4c5bbac16ccb483..405a7927b7cad8d8e89cb87c5d41eae827087abf 100644 --- a/main.js +++ b/main.js @@ -3,6 +3,7 @@ const Path = require('path'); const ConfigManager = require('./config-manager'); const FileUtil = require('./utils/file-util'); const ObjectUtil = require('./utils/object-util'); +const FindNodeRef = require('./utils/find-node-ref'); /** * i18n @@ -66,11 +67,10 @@ module.exports = { this.findCurrentSelection(); }, - /** - * 通过 uuid 查找 - * @param {*} event - * @param {string} uuid - */ + 'find-current-node'() { + this.findCurrentNode(); + }, + 'find-via-uuid'(event, uuid) { this.findViaUuid(uuid); event.reply(null, null); @@ -151,6 +151,25 @@ module.exports = { } }, + + /** + * 查找当前选中节点引用 + */ + findCurrentNode() { + const curUuids = Editor.Selection.curSelection('node'); + if (curUuids.length === 0) { + Editor.log('[🔎]', '请先在资源管理器中选择需要查找引用的节点!'); + return; + } + Editor.Scene.callSceneScript('ccc-references-finder', 'getSceneOrPrefabUuid', function (err, uuid) { + let filePath = Editor.assetdb.uuidToFspath(uuid); + let results = FindNodeRef(filePath,curUuids[0]); + for(let i=0;i { + if (!curNode) + return; + if (curNode && curNode._id == targetNode._id) { + return; + } + if (curNode.__id__ == targetNode.__id__) { + //过滤掉自己本身引用 + if (curNode.___parent.node.__id__ == targetNode.__id__) { + return false; + } + return true; + } + let comps = targetNode.components || []; + for (let i = 0; i < comps.length; i++) { + if (curNode.__id__ == comps[i].originalIndex) { + return true; + } + } + }); + return results; +}; + +function parseRefs(scene, refs) { + let infos = []; + for (let c in refs) { + let ref = refs[c]; + let node = ref.node; + let keys = []; + while (node) { + if (node.___parent.___Key == "components")//找到第一个组件 + break; + keys.push(node.___Key); + node = node.___parent; + } + keys.reverse(); + let realType = node.__type__; + if (Editor.Utils.UuidUtils.isUuid(node.__type__)) { + const scriptUuid = Editor.Utils.UuidUtils.decompressUuid(node.__type__); + const assetInfo = Editor.assetdb.assetInfoByUuid(scriptUuid); + realType = Path.basename(assetInfo.url); + } + infos.push({ + type: node.__type__, + keys: keys, + path: node.___parent.___parent.path, + str: `       💾[节点]->${node.___parent.___parent.path}  →  💿 ${realType} →  🎲 [属性] ${keys.join("/")}` + }); + } + return infos; +} + +let parseJsonFile = function (filepath) { + if (!filepath) { + console.trace("filepath is empty"); + return; + } + try { + let str = fs.readFileSync(filepath, 'utf8'); + let result = JSON.parse(str); + return result; + } catch (e) { + console.trace(e, filepath); + } +}; +let findNodeRef = function (sceneFilePath, uuid) { + let sceneNode = ObjectUtilsEx.convertToNodeTree(parseJsonFile(sceneFilePath)); + let findNode = findInJson(sceneNode, (curNode) => { + return curNode && curNode._id == uuid; + }); + Editor.log(`[🔎] 查找节点引用 ${findNode[0].node.path}`); + Editor.log(`[🔎] 查找节点引用结果 >>> `); + + let tempScene = deepCopy(sceneNode); + addParentsForJson(tempScene); + let elements = findInJson(tempScene, (element) => { + if (element && element._id == uuid) + return true; + }); + let refs = getRefInfos(tempScene, elements[0].node); + let infos = parseRefs(tempScene, refs); + Editor.log(`   📙 节点引用 x ${infos.length}`); + return infos.map(v => { + return v.str; + }); +}; + +module.exports = findNodeRef; \ No newline at end of file diff --git a/utils/object-util-ex.js b/utils/object-util-ex.js new file mode 100644 index 0000000000000000000000000000000000000000..77b20545ff5299f5e4366e8d8593935d6c0f5f54 --- /dev/null +++ b/utils/object-util-ex.js @@ -0,0 +1,144 @@ +let ObjectUtilEx = { + isObject(arg) { + return Object.prototype.toString.call(arg) === '[object Object]'; + }, + /** + * 对象中是否包含指定的属性 + * @param {object} object 对象 + * @param {any} name 属性名 + */ + containsProperty(object, name) { + let result = false; + const search = (_object) => { + if (this.isObject(_object)) { + for (const key in _object) { + if (key == name) { + result = true; + return; + } + search(_object[key]); + } + } else if (Array.isArray(_object)) { + for (let i = 0; i < _object.length; i++) { + search(_object[i]); + } + } + }; + search(object); + return result; + }, + + + /** + * 提取有效信息(含有 uuid) + * @param {object} data 元数据 + * @returns {object} + */ + extractValidInfo(data, originalIndex, componentIndex) { + const info = Object.create(null); + // 记录有用的属性 + const keys = ['__type__', '_name', 'fileId', 'target', '__id__']; + for (let i = 0; i < keys.length; i++) { + if (data[keys[i]]) { + info[keys[i]] = data[keys[i]]; + } + } + if (originalIndex !== undefined) + info.originalIndex = originalIndex; + if (componentIndex !== undefined) + info.componentIndex = componentIndex; + // 记录包含 uuid 的属性 + for (const key in data) { + if (this.containsProperty(data[key], '__uuid__') || this.containsProperty(data[key], '__id__')) { + info[key] = data[key]; + } + } + return info; + }, + /** + * 将资源数据转为节点树 + * @param {object} data 元数据 + * @returns {object} + */ + convertToNodeTree(data) { + /** + * 读取节点 + * @param {object} node 节点 + * @param {number} id ID + */ + const read = (node, id, childIndex) => { + const nodeData = Object.create(null); + const actualNodeData = data[id]; + + // 基本信息 + nodeData['_name'] = actualNodeData['_name']; + if (childIndex !== undefined) { + nodeData.childIndex = childIndex; + } + nodeData['__id__'] = id; + if (actualNodeData['__type__']) + nodeData['__type__'] = actualNodeData['__type__']; + if (actualNodeData['_id']) + nodeData['_id'] = actualNodeData['_id']; + + // 记录路径 + const parentPath = node['path'] ? node['path'] : (node['_name'] ? node['_name'] : null); + nodeData['path'] = (parentPath ? parentPath + '/' : '') + nodeData['_name']; + + // 记录组件 + const components = actualNodeData['_components']; + if (components && components.length > 0) { + nodeData['components'] = []; + for (let i = 0; i < components.length; i++) { + const actualComponent = data[components[i]['__id__']]; + nodeData['components'].push(this.extractValidInfo(actualComponent, components[i]['__id__'], i)); + } + } + + // 记录预制体引用 + const prefab = actualNodeData['_prefab']; + if (prefab) { + const realPrefab = data[prefab['__id__']]; + nodeData['prefab'] = this.extractValidInfo(realPrefab); + } + + // 记录子节点 + const children = actualNodeData['_children']; + if (children && children.length > 0) { + nodeData['children'] = []; + for (let i = 0; i < children.length; i++) { + const nodeId = children[i]['__id__']; + read(nodeData, nodeId, i); + } + } + + // 推入引用容器 + node['children'].push(nodeData); + }; + + // 读取 + const tree = Object.create(null); + const type = data[0]['__type__']; + if (type === 'cc.SceneAsset') { + // 场景资源 + tree['__type__'] = 'cc.Scene'; + tree['children'] = []; + const sceneId = data[0]['scene']['__id__']; + tree['__id__'] = sceneId; + const nodes = data[sceneId]['_children']; + for (let i = 0; i < nodes.length; i++) { + const nodeId = nodes[i]['__id__']; + read(tree, nodeId); + } + } else if (type === 'cc.Prefab') { + // 预制体资源 + tree['__type__'] = 'cc.Prefab'; + tree['__uuid__'] = data[data.length - 1]['asset']['__uuid__']; + tree['children'] = []; + const rootId = data[0]['data']['__id__']; + read(tree, rootId); + } + return tree; + }, +}; +module.exports = ObjectUtilEx; \ No newline at end of file