1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
const { print } = require("../eazax/editor-main-util");
const FileUtil = require("../eazax/file-util");
const { containsProperty } = require("./object-util");
/**
* 解析器
*/
const Parser = {
/**
* 节点树缓存
* @type {{ [key: string]: object }}
*/
caches: Object.create(null),
/**
* 获取节点树
* @param {string} path 路径
* @returns {Promise<object>}
*/
async getNodeTree(path) {
if (!Parser.caches[path]) {
const file = await FileUtil.readFile(path);
let data = null;
try {
data = JSON.parse(file);
} catch (error) {
print('warn', '文件解析失败', path);
print('warn', error);
}
if (!data) {
return null;
}
Parser.caches[path] = Parser.convert(data);
}
return Parser.caches[path];
},
/**
* 更新缓存
* @param {string} path 路径
*/
async updateCache(path) {
Parser.caches[path] = null;
await Parser.getNodeTree(path);
},
/**
* 将资源解析为节点树
* @param {object} source 源数据
* @returns {object}
*/
convert(source) {
const tree = Object.create(null),
type = source[0]['__type__'];
if (type === 'cc.SceneAsset') {
// 场景资源
const sceneId = source[0]['scene']['__id__'],
children = source[sceneId]['_children'];
tree.type = 'cc.Scene'; // 类型
tree.id = sceneId; // ID
// 场景下可以有多个一级节点
tree.children = [];
for (let i = 0, l = children.length; i < l; i++) {
const nodeId = children[i]['__id__'];
Parser.convertNode(source, nodeId, tree);
}
} else if (type === 'cc.Prefab') {
// 预制体资源
const uuid = source[source.length - 1]['asset']['__uuid__'];
tree.type = 'cc.Prefab'; // 类型
tree.uuid = uuid; // uuid
// 预制体本身就是一个节点
tree.children = [];
const nodeId = source[0]['data']['__id__'];
Parser.convertNode(source, nodeId, tree);
}
return tree;
},
/**
* 解析节点
* @param {object} source 源数据
* @param {number} nodeId 节点 ID
* @param {object} parent 父节点
*/
convertNode(source, nodeId, parent) {
const srcNode = source[nodeId],
node = Object.create(null);
// 基本信息
node.name = srcNode['_name'];
node.id = nodeId;
node.type = srcNode['__type__'];
// 路径
const parentPath = parent.path || null;
node.path = parentPath ? `${parentPath}/${node.name}` : node.name;
// 预制体引用
const srcPrefab = srcNode['_prefab'];
if (srcPrefab) {
const id = srcPrefab['__id__'];
node.prefab = Parser.extractValidInfo(source[id]);
}
// 组件
node.components = [];
const srcComponents = srcNode['_components'];
if (srcComponents && srcComponents.length > 0) {
for (let i = 0, l = srcComponents.length; i < l; i++) {
const compId = srcComponents[i]['__id__'],
component = Parser.extractValidInfo(source[compId]);
node.components.push(component);
}
}
// 子节点
node.children = [];
const srcChildren = srcNode['_children'];
if (srcChildren && srcChildren.length > 0) {
for (let i = 0, l = srcChildren.length; i < l; i++) {
const nodeId = srcChildren[i]['__id__'];
Parser.convertNode(source, nodeId, node);
}
}
// 保存到父节点
parent.children.push(node);
},
/**
* 提取有效信息(含有 uuid)
* @param {object} source 源数据
* @returns {{ __type__: string, _name: string, fileId?: string }}
*/
extractValidInfo(source) {
const result = Object.create(null);
// 记录有用的属性
const keys = ['__type__', '_name', 'fileId'];
for (let i = 0, l = keys.length; i < l; i++) {
const key = keys[i];
if (source[key] !== undefined) {
result[key] = source[key];
}
}
// 记录包含 uuid 的属性
for (const key in source) {
const contains = containsProperty(source[key], '__uuid__');
if (contains) {
result[key] = source[key];
}
}
return result;
},
};
module.exports = Parser;