最近在封装一个组件,有一个字符串枚举类型, 发现不支持, 想探探原因,看看能否hook解决这个问题! 因为数字枚举类型编辑器是支持的。
cocos creator支持的所有属性类型分别是:
[
"string",
"number",
"boolean",
"array",
"object",
"enum",
"color",
"vec2",
"vec3",
"String",
"Float",
"Boolean",
"Object",
"Integer",
"Enum",
"asset",
"cc.Asset",
"cc.Node",
"cc.Vec2",
"cc.Vec3",
"cc.Size",
"cc.Color",
"cc.Rect",
"cc.Vec4"
]- 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.
所有类型的显示模版如下:
string
template(t) {
let i;
return (i = t.multiline
? '\n <ui-text-area class="flex-1" resize-v></ui-text-area>\n '
: '\n <ui-input class="flex-1"></ui-input>\n ');
}
===================
number
template(t) {
let i;
return (i = t.slide
? '\n <ui-slider class="flex-1"></ui-slider>\n '
: '\n <ui-num-input class="flex-1"></ui-num-input>\n ');
}
===================
boolean
<ui-checkbox class="flex-1"></ui-checkbox>
===================
array
<ui-num-input class="flex-1"></ui-num-input>
<div slot="child"></div>
===================
object
<div class="child" slot="child"></div>
===================
enum
<ui-select class="flex-1"></ui-select>
===================
color
<ui-color class="flex-1"></ui-color>
===================
vec2
<ui-prop name="X" id="x-comp" slidable class="fixed-label red flex-1">
<ui-num-input class="flex-1"></ui-num-input>
</ui-prop>
<ui-prop name="Y" id="y-comp" slidable class="fixed-label green flex-1">
<ui-num-input class="flex-1"></ui-num-input>
</ui-prop>
===================
vec3
<ui-prop name="X" id="x-comp" slidable class="fixed-label red flex-1">
<ui-num-input class="flex-1"></ui-num-input>
</ui-prop>
<ui-prop name="Y" id="y-comp" slidable class="fixed-label green flex-1">
<ui-num-input class="flex-1"></ui-num-input>
</ui-prop>
<ui-prop name="Z" id="z-comp" slidable class="fixed-label blue flex-1">
<ui-num-input class="flex-1"></ui-num-input>
</ui-prop>
===================
String
template(t) {
let i;
return (i = t.multiline
? '\n <ui-text-area class="flex-1" resize-v></ui-text-area>\n '
: '\n <ui-input class="flex-1"></ui-input>\n ');
}
===================
Float
template(t) {
let i;
return (i = t.slide
? '\n <ui-slider class="flex-1"></ui-slider>\n '
: '\n <ui-num-input class="flex-1"></ui-num-input>\n ');
}
===================
Boolean
<ui-checkbox class="flex-1"></ui-checkbox>
===================
Object
<div class="child" slot="child"></div>
===================
Integer
template(t) {
let i;
return (i = t.slide
? '\n <ui-slider class="flex-1"></ui-slider>\n '
: '\n <ui-num-input class="flex-1" type="int"></ui-num-input>\n ');
}
===================
Enum
<ui-select class="flex-1"></ui-select>
===================
asset
(t) =>
`\n <ui-asset class="flex-1" type="${t.assetType}"></ui-asset>\n `
===================
cc.Asset
(t) =>
`\n <ui-asset class="flex-1" type="${t.assetType}"></ui-asset>\n `
===================
cc.Node
(t) =>
`\n <ui-node class="flex-1"\n type="${t.typeid}"\n typename="${t.typename}"\n ></ui-node>\n `
===================
cc.Vec2
<ui-prop name="X" id="x-comp" subset slidable class="fixed-label flex-1">
<ui-num-input class="flex-1"></ui-num-input>
</ui-prop>
<ui-prop name="Y" id="y-comp" subset slidable class="fixed-label flex-1">
<ui-num-input class="flex-1"></ui-num-input>
</ui-prop>
===================
cc.Vec3
<ui-prop name="X" id="x-comp" subset slidable class="fixed-label flex-1">
<ui-num-input class="flex-1"></ui-num-input>
</ui-prop>
<ui-prop name="Y" id="y-comp" subset slidable class="fixed-label flex-1">
<ui-num-input class="flex-1"></ui-num-input>
</ui-prop>
<ui-prop name="Z" id="z-comp" subset slidable class="fixed-label flex-1">
<ui-num-input class="flex-1"></ui-num-input>
</ui-prop>
===================
cc.Size
<ui-prop name="W" id="w-comp" subset slidable class="fixed-label flex-1">
<ui-num-input class="flex-1"></ui-num-input>
</ui-prop>
<ui-prop name="H" id="h-comp" subset slidable class="fixed-label flex-1">
<ui-num-input class="flex-1"></ui-num-input>
</ui-prop>
===================
cc.Color
<ui-color class="flex-1"></ui-color>
===================
cc.Rect
<div class="vertical flex-1">
<div class="layout horizontal">
<ui-prop subset slidable name="X" class="fixed-label flex-1" style="min-width: 0; margin-right: 10px;">
<ui-num-input id="x-input" class="flex-1"></ui-num-input>
</ui-prop>
<ui-prop subset slidable name="Y" class="fixed-label flex-1" style="min-width: 0;">
<ui-num-input id="y-input" class="flex-1"></ui-num-input>
</ui-prop>
</div>
<div class="layout horizontal">
<ui-prop subset slidable name="W" class="fixed-label flex-1" style="min-width: 0; margin-right: 10px;">
<ui-num-input id="w-input" class="flex-1"></ui-num-input>
</ui-prop>
<ui-prop subset slidable name="H" class="fixed-label flex-1" style="min-width: 0;">
<ui-num-input id="h-input" class="flex-1"></ui-num-input>
</ui-prop>
</div>
</div>
===================
cc.Vec4
<div class="vertical flex-1">
<div class="layout horizontal">
<ui-prop name="X" class="fixed-label flex-1" style="min-width: 0; margin-right: 10px;">
<ui-num-input id="x-input" class="flex-1"></ui-num-input>
</ui-prop>
<ui-prop name="Y" class="fixed-label flex-1" style="min-width: 0;">
<ui-num-input id="y-input" class="flex-1"></ui-num-input>
</ui-prop>
</div>
<div class="layout horizontal">
<ui-prop name="Z" class="fixed-label flex-1" style="min-width: 0; margin-right: 10px;">
<ui-num-input id="z-input" class="flex-1"></ui-num-input>
</ui-prop>
<ui-prop name="W" class="fixed-label flex-1" style="min-width: 0;">
<ui-num-input id="w-input" class="flex-1"></ui-num-input>
</ui-prop>
</div>
</div>- 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.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
- 183.
- 184.
- 185.
- 186.
- 187.
- 188.
- 189.
- 190.
- 191.
- 192.
- 193.
- 194.
- 195.
- 196.
- 197.
- 198.
- 199.
有一个关键算法, 它是获取所有属性源头:
function getClassAttrs(ctor) {
return ctor.hasOwnProperty('__attrs__') && ctor.__attrs__ || createAttrs(ctor);
}
function attr(ctor, propName, newAttrs) {
var attrs = getClassAttrs(ctor);
if (!CC_DEV || typeof newAttrs === 'undefined') {
// get
var prefix = propName + DELIMETER;
var ret = {};
for (var key in attrs) {
if (key.startsWith(prefix)) {
ret[key.slice(prefix.length)] = attrs[key];
}
}
return ret;
} else if (CC_DEV && typeof newAttrs === 'object') {
// set
cc.warn("`cc.Class.attr(obj, prop, { key: value });` is deprecated, use `cc.Class.Attr.setClassAttr(obj, prop, 'key', value);` instead please.");
for (var _key in newAttrs) {
attrs[propName + DELIMETER + _key] = newAttrs[_key];
}
}
} // returns a readonly meta object- 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.
creator编辑器自己有一个运行时, 所有的对象存放cc.engine.attachedObjsForEditor:

如果是数字枚举, 结果如下:

如果是字符串枚举, 结果如下:

这个就是问题所在了,类型是Enum,默认值string类型,enumList 为空。
enumList 元素对象类型是{name:xxx, value: v}
enumList 的数据来源算法是:
cc.Enum.getList = function (enumDef) {
if (enumDef.__enums__) return enumDef.__enums__;
var enums = enumDef.__enums__ = [];
for (var name in enumDef) {
var value = enumDef[name];
if (Number.isInteger(value)) {
enums.push({
name: name,
value: value
});
}
}
enums.sort(function (a, b) {
return a.value - b.value;
});
return enums;
};- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
从这个代码就可以知道enumList为啥空了, 因为只处理了interger.
写一个插件看看, 强行让它也可以处理string类型看看

现在插件运行正确,确实能赋值给enumList了, 但是还是显示类型错误

那么就需要从显示逻辑着手了,为啥显示Type Error:
这是错误组件cc-type-error-prop:

看看什么时候会用cc-type-error-prop组件:

通过断点找到了异常的地方, 大概的意思值类型String和Enum不是一个类型

这个数据本身没问题, 因为字符串枚举, 底层本身就是string。
enum CmpIconGroup {
ABC = 'abc',
DEF = 'def',
}
从初步分析结果来看,数字枚举应该也会显示Type Error才对, 因为number类似不等于Enum, 再次分析正确的情况:

非常好,这就是为啥不显示的Type Error的原因了, value是2,但是o却是Enum. 并没有识别出是Number
为啥value=2能识别出是Enum, 而value='def'确是string?
继续往前跟踪, 发现之前忽略一点,只关注了prop属性值, 没有关心value值:

继续往前,看看为啥数字2为啥可以识别出Enum:

这个数字2,源头就是Enum, 再看看字符串枚举, 这个源头是啥:

数据源头也是Enum, 但是经过Editor.getNodeDump(node) 之后变成了String:

继续挖掘, 找到了根源:

如果原始数据是Enum, 值是Number类型时,强制用Enum, 而忽略了string类型。 这也是为啥Type Error的真正原因。
知道原因就好处理了, 直接hook Editor.getNodeDump方法, 解决编辑器不支持字符串枚举的方法是写一个插件,代码如下:
if(CC_EDITOR){
cc.Enum.getList = function (enumDef) {
if (enumDef.__enums__) return enumDef.__enums__;
var enums = enumDef.__enums__ = [];
let isStr = false;
for (var name in enumDef) {
var value = enumDef[name];
isStr = typeof value === 'string';
enums.push({name: name,value: value});
}
!isStr && enums.sort(function (a, b) {
return a.value - b.value;
});
return enums;
};
const oldDump = Editor.getNodeDump;
Editor.getNodeDump = function(node){
const ret = oldDump(node);
let {types, value} = ret;
if(value.__comps__){
value.__comps__.forEach((comp)=>{
const cmpType = types[comp.type];
const properties = cmpType.properties;
for(let propKey in properties){
const prop = properties[propKey];
if(prop.type === 'Enum'){// 真实数据是Enum类型
comp.value[propKey].type = 'Enum';
}
}
});
}
return ret;
}
Editor.UI.getProperty('Enum').inputValue = function(){
const ty = this.get_real_type();
if(ty == 'string'){
return this.$input.value;
}
return Number(this.$input.value);
}
Editor.UI.getProperty('Enum').get_real_type = function(){
if(this.__ty){
return this.__ty;
}
let ty = 'string';
try{
ty = typeof this._attrs.enumList[0].value;
}catch(e){
// pass
}
this.__ty = ty;
return ty;
}
const oldEidtorWarn = Editor.warn;
Editor.warn = function(s){// hook 枚举类型保存错误的警告!因为已经支持string类型了
if(typeof s == 'string' && s.startsWith('Expecting number type of value for')){
return;
}
return oldEidtorWarn.apply(Editor, arguments);
}
}- 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.
附上最终的效果图:

碰到字符串枚举类型时编辑器再也不显示Type Error了, 哈哈,完美收工!!!
经过实际测试已经支持 string float, int 的枚举类型了。
比如:
后续属性设置可以直接
8万+

被折叠的 条评论
为什么被折叠?



