You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
361 lines
14 KiB
361 lines
14 KiB
(function (global) {
|
|
'use strict';
|
|
|
|
var WorkbenchAgent = global.PWAgent;
|
|
var U = global.PWAgentUtil;
|
|
var nodeId = U && U.nodeId;
|
|
if (!WorkbenchAgent) {
|
|
console.error('[调试工作台] agent-serialize 需先加载 agent-core');
|
|
return;
|
|
}
|
|
if (!nodeId) {
|
|
console.error('[调试工作台] agent-serialize 需先加载 agent-util');
|
|
return;
|
|
}
|
|
|
|
var COMP_PROP_SKIP = {
|
|
_name: 1, _objFlags: 1, __scriptAsset: 1, node: 1, _enabled: 1,
|
|
__prefab: 1, __editorExtras__: 1, uuid: 1, name: 1, enabled: 1,
|
|
};
|
|
|
|
/**
|
|
* 读取 CCClass 属性元数据(displayName / type / enumList / visible)。
|
|
* @param {Function} ctor
|
|
* @returns {object}
|
|
*/
|
|
function getCtorAttrs(ctor) {
|
|
if (!ctor) return {};
|
|
if (ctor.__attrs__ && typeof ctor.__attrs__ === 'object') return ctor.__attrs__;
|
|
return {};
|
|
}
|
|
|
|
/**
|
|
* @param {object} attrs
|
|
* @param {string} key
|
|
* @param {string} name
|
|
* @returns {*}
|
|
*/
|
|
function attrOf(attrs, key, name) {
|
|
return attrs[key + '$_$' + name];
|
|
}
|
|
|
|
/**
|
|
* 列出组件应展示的属性名(对齐编辑器:走 __props__,隐藏 _ 开头与 visible:false)。
|
|
* @param {Function} ctor
|
|
* @returns {string[]}
|
|
*/
|
|
function listCompPropKeys(ctor) {
|
|
var attrs = getCtorAttrs(ctor);
|
|
var raw = (ctor && (ctor.__props__ || ctor.__values__)) || [];
|
|
var out = [];
|
|
for (var i = 0; i < raw.length; i++) {
|
|
var key = raw[i];
|
|
if (!key || COMP_PROP_SKIP[key]) continue;
|
|
if (key.charAt(0) === '_' || key.indexOf('__') === 0) continue;
|
|
if (attrOf(attrs, key, 'visible') === false) continue;
|
|
out.push(key);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
/**
|
|
* 将运行时值序列化为 Inspector DTO。
|
|
* @param {*} val
|
|
* @param {object} attrs
|
|
* @param {string} key
|
|
* @param {object} cc
|
|
* @returns {{type:string, value:*, enumOptions?:Array, readOnly?:boolean}}
|
|
*/
|
|
function serializeFieldValue(val, attrs, key, cc) {
|
|
var enumList = attrOf(attrs, key, 'enumList');
|
|
var typeTag = attrOf(attrs, key, 'type');
|
|
if (enumList && enumList.length) {
|
|
return {
|
|
type: 'enum',
|
|
value: val,
|
|
enumOptions: enumList.map(function (it) {
|
|
return { name: String(it.name), value: it.value };
|
|
}),
|
|
};
|
|
}
|
|
if (typeTag === 'Enum') {
|
|
return { type: 'number', value: typeof val === 'number' ? val : Number(val) || 0 };
|
|
}
|
|
var t = typeof val;
|
|
if (t === 'boolean' || t === 'string') return { type: t, value: val };
|
|
if (t === 'number') return { type: 'number', value: isFinite(val) ? val : 0 };
|
|
if (val == null) {
|
|
var ctorHint = attrOf(attrs, key, 'ctor');
|
|
var hintName = ctorHint && (ctorHint.name || ctorHint.prototype && ctorHint.prototype.__classname__);
|
|
if (hintName || typeTag === 'Object') {
|
|
return {
|
|
type: 'ref',
|
|
readOnly: true,
|
|
value: { kind: hintName || 'Object', name: '(空)', uuid: '' },
|
|
};
|
|
}
|
|
return { type: 'string', value: '' };
|
|
}
|
|
if (cc && cc.Color && val instanceof cc.Color) {
|
|
return {
|
|
type: 'color',
|
|
value: { r: val.r | 0, g: val.g | 0, b: val.b | 0, a: val.a != null ? (val.a | 0) : 255 },
|
|
};
|
|
}
|
|
// Size
|
|
if (typeof val.width === 'number' && typeof val.height === 'number' && val.x === undefined) {
|
|
return { type: 'size', value: { width: val.width, height: val.height } };
|
|
}
|
|
// Vec2 / Vec3
|
|
if (typeof val.x === 'number' && typeof val.y === 'number') {
|
|
if (typeof val.z === 'number') {
|
|
return { type: 'vec3', value: { x: val.x, y: val.y, z: val.z } };
|
|
}
|
|
return { type: 'vec2', value: { x: val.x, y: val.y } };
|
|
}
|
|
// Asset / Node / Component 引用
|
|
if (typeof val === 'object') {
|
|
var kind = (val.constructor && val.constructor.name) || 'Object';
|
|
var name = '';
|
|
if (val.name != null) name = String(val.name);
|
|
else if (val.node && val.node.name) name = String(val.node.name);
|
|
var uuid = val._uuid || val.uuid || '';
|
|
return {
|
|
type: 'ref',
|
|
readOnly: true,
|
|
value: { kind: kind, name: name || kind, uuid: uuid ? String(uuid) : '' },
|
|
};
|
|
}
|
|
return { type: 'string', value: String(val) };
|
|
}
|
|
|
|
/**
|
|
* 提取单个组件的 Inspector 字段列表。
|
|
* @param {object} comp
|
|
* @param {number} index
|
|
* @param {object} cc
|
|
* @returns {{name:string, index:number, fields:Array}}
|
|
*/
|
|
function extractCompFields(comp, index, cc) {
|
|
var ctor = comp.constructor;
|
|
var cname = (cc && cc.js && typeof cc.js.getClassName === 'function')
|
|
? (cc.js.getClassName(ctor) || ctor.name)
|
|
: (ctor && ctor.name) || 'Component';
|
|
var attrs = getCtorAttrs(ctor);
|
|
var keys = listCompPropKeys(ctor);
|
|
// 无元数据时有限回退
|
|
if (!keys.length) {
|
|
var own = Object.keys(comp);
|
|
for (var o = 0; o < own.length && keys.length < 24; o++) {
|
|
var ok = own[o];
|
|
if (!ok || ok.charAt(0) === '_' || COMP_PROP_SKIP[ok]) continue;
|
|
var ot = typeof comp[ok];
|
|
if (ot === 'number' || ot === 'boolean' || ot === 'string') keys.push(ok);
|
|
}
|
|
if (!keys.length) {
|
|
console.warn('[Agent] 组件无 CCClass 属性元数据', cname);
|
|
}
|
|
}
|
|
var fields = [];
|
|
var max = 80;
|
|
for (var i = 0; i < keys.length && fields.length < max; i++) {
|
|
var key = keys[i];
|
|
var label = attrOf(attrs, key, 'displayName') || key;
|
|
var raw;
|
|
try { raw = comp[key]; } catch (e) { continue; }
|
|
var ser = serializeFieldValue(raw, attrs, key, cc);
|
|
fields.push({
|
|
key: key,
|
|
label: label,
|
|
type: ser.type,
|
|
value: ser.value,
|
|
enumOptions: ser.enumOptions || null,
|
|
readOnly: !!ser.readOnly,
|
|
});
|
|
}
|
|
return { name: cname, index: index, fields: fields };
|
|
}
|
|
|
|
WorkbenchAgent.prototype._getNodeProps = function (uuid) {
|
|
var node = this._findNode(uuid);
|
|
if (!node) return null;
|
|
var cc = this.cc;
|
|
var p = node.getPosition ? node.getPosition() : node.position;
|
|
var e = node.eulerAngles || { x: 0, y: 0, z: 0 };
|
|
var s = node.getScale ? node.getScale() : node.scale;
|
|
var comps = [];
|
|
if (node.components) {
|
|
for (var i = 0; i < node.components.length; i++) {
|
|
var comp = node.components[i];
|
|
if (!comp || !comp.constructor) continue;
|
|
comps.push(extractCompFields(comp, i, cc));
|
|
}
|
|
}
|
|
return {
|
|
uuid: nodeId(node),
|
|
name: node.name || '',
|
|
active: !!node.active,
|
|
layer: node.layer,
|
|
pos: { x: p.x, y: p.y, z: p.z },
|
|
euler: { x: e.x, y: e.y, z: e.z },
|
|
scale: { x: s.x, y: s.y, z: s.z },
|
|
comps: comps,
|
|
};
|
|
};
|
|
|
|
WorkbenchAgent.prototype._setNodeProp = function (uuid, fid, value) {
|
|
var node = this._findNode(uuid);
|
|
if (!node || !fid) return false;
|
|
var cc = this.cc;
|
|
try {
|
|
if (fid === 'name') node.name = String(value);
|
|
else if (fid === 'active') node.active = !!value;
|
|
else if (fid === 'layer') node.layer = Number(value) || 0;
|
|
else if (fid === 'px' || fid === 'py' || fid === 'pz') {
|
|
var p = node.getPosition ? node.getPosition() : node.position;
|
|
var x = fid === 'px' ? Number(value) : p.x;
|
|
var y = fid === 'py' ? Number(value) : p.y;
|
|
var z = fid === 'pz' ? Number(value) : p.z;
|
|
if (typeof node.setPosition === 'function') node.setPosition(x, y, z);
|
|
else if (cc && cc.Vec3) node.position = new cc.Vec3(x, y, z);
|
|
} else if (fid === 'rx' || fid === 'ry' || fid === 'rz') {
|
|
var e = node.eulerAngles || { x: 0, y: 0, z: 0 };
|
|
var ex = fid === 'rx' ? Number(value) : e.x;
|
|
var ey = fid === 'ry' ? Number(value) : e.y;
|
|
var ez = fid === 'rz' ? Number(value) : e.z;
|
|
if (typeof node.setRotationFromEuler === 'function') node.setRotationFromEuler(ex, ey, ez);
|
|
else if (cc && cc.Vec3) node.eulerAngles = new cc.Vec3(ex, ey, ez);
|
|
} else if (fid === 'sx' || fid === 'sy' || fid === 'sz') {
|
|
var sc = node.getScale ? node.getScale() : node.scale;
|
|
var sx = fid === 'sx' ? Number(value) : sc.x;
|
|
var sy = fid === 'sy' ? Number(value) : sc.y;
|
|
var sz = fid === 'sz' ? Number(value) : sc.z;
|
|
if (typeof node.setScale === 'function') node.setScale(sx, sy, sz);
|
|
else if (cc && cc.Vec3) node.scale = new cc.Vec3(sx, sy, sz);
|
|
} else if (fid.indexOf('c') === 0 && fid.indexOf('_') > 0) {
|
|
return this._setCompProp(node, fid, value);
|
|
} else {
|
|
return false;
|
|
}
|
|
return true;
|
|
} catch (err) {
|
|
console.warn('[Agent] set prop', fid, err);
|
|
return false;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 写回组件字段。fid: c{index}_{key}[_axis]
|
|
* @param {object} node
|
|
* @param {string} fid
|
|
* @param {*} value
|
|
* @returns {boolean}
|
|
*/
|
|
WorkbenchAgent.prototype._setCompProp = function (node, fid, value) {
|
|
var m = /^c(\d+)_(.+)$/.exec(fid);
|
|
if (!m) return false;
|
|
var ci = parseInt(m[1], 10);
|
|
var rest = m[2];
|
|
var comp = node.components && node.components[ci];
|
|
if (!comp) return false;
|
|
var cc = this.cc;
|
|
var axis = '';
|
|
var key = rest;
|
|
var axisMatch = /^(.*)_(x|y|z|w|h|r|g|b|a|hex)$/.exec(rest);
|
|
if (axisMatch) {
|
|
// 优先整键命中(字段名本身含下划线时仍以整键为准)
|
|
if (!(rest in comp) && (axisMatch[1] in comp || hasCompProp(comp, axisMatch[1]))) {
|
|
key = axisMatch[1];
|
|
axis = axisMatch[2];
|
|
}
|
|
}
|
|
if (axis) {
|
|
return setCompAxisValue(comp, key, axis, value, cc);
|
|
}
|
|
var attrs = getCtorAttrs(comp.constructor);
|
|
if (attrOf(attrs, key, 'type') === 'Object' || attrOf(attrs, key, 'ctor')) {
|
|
// 引用只读
|
|
return false;
|
|
}
|
|
var cur;
|
|
try { cur = comp[key]; } catch (e1) { return false; }
|
|
if (cur && typeof cur === 'object' && !(typeof cur === 'boolean')) {
|
|
// 未带轴的对象写入拒绝(避免覆盖引用)
|
|
if (typeof cur.x === 'number' || typeof cur.width === 'number' || (cc && cc.Color && cur instanceof cc.Color)) {
|
|
return false;
|
|
}
|
|
}
|
|
try {
|
|
if (typeof cur === 'boolean') comp[key] = !!value;
|
|
else if (typeof cur === 'number') comp[key] = Number(value);
|
|
else if (typeof cur === 'string') comp[key] = String(value);
|
|
else if (attrOf(attrs, key, 'enumList')) comp[key] = Number(value);
|
|
else comp[key] = value;
|
|
return true;
|
|
} catch (e2) {
|
|
console.warn('[Agent] set comp prop', key, e2);
|
|
return false;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {object} comp
|
|
* @param {string} key
|
|
* @returns {boolean}
|
|
*/
|
|
function hasCompProp(comp, key) {
|
|
if (!comp || !key) return false;
|
|
if (key in comp) return true;
|
|
var ctor = comp.constructor;
|
|
var props = (ctor && (ctor.__props__ || ctor.__values__)) || [];
|
|
return props.indexOf(key) >= 0;
|
|
}
|
|
|
|
/**
|
|
* @param {object} comp
|
|
* @param {string} key
|
|
* @param {string} axis
|
|
* @param {*} value
|
|
* @param {object} cc
|
|
* @returns {boolean}
|
|
*/
|
|
function setCompAxisValue(comp, key, axis, value, cc) {
|
|
var cur;
|
|
try { cur = comp[key]; } catch (e) { return false; }
|
|
if (axis === 'hex') {
|
|
var hex = String(value).replace(/^#/, '');
|
|
if (hex.length < 6) return false;
|
|
var r = parseInt(hex.slice(0, 2), 16);
|
|
var g = parseInt(hex.slice(2, 4), 16);
|
|
var b = parseInt(hex.slice(4, 6), 16);
|
|
var a = hex.length >= 8 ? parseInt(hex.slice(6, 8), 16) : 255;
|
|
if (cc && cc.Color) {
|
|
comp[key] = new cc.Color(r, g, b, a);
|
|
return true;
|
|
}
|
|
if (cur && typeof cur.r === 'number') {
|
|
cur.r = r; cur.g = g; cur.b = b; cur.a = a;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
var num = Number(value);
|
|
if (!isFinite(num)) return false;
|
|
if (!cur || typeof cur !== 'object') return false;
|
|
if (axis === 'w' && typeof cur.width === 'number') { cur.width = num; return true; }
|
|
if (axis === 'h' && typeof cur.height === 'number') { cur.height = num; return true; }
|
|
if ((axis === 'x' || axis === 'y' || axis === 'z') && typeof cur[axis] === 'number') {
|
|
cur[axis] = num;
|
|
// 触发 setter(部分引擎属性需整体赋值)
|
|
try { comp[key] = cur; } catch (e2) { /* 就地改写已足够 */ }
|
|
return true;
|
|
}
|
|
if ((axis === 'r' || axis === 'g' || axis === 'b' || axis === 'a') && typeof cur[axis] === 'number') {
|
|
cur[axis] = num;
|
|
try { comp[key] = cur; } catch (e3) { /* */ }
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
})(window);
|
|
|