cocos / cocos-engine

Cocos simplifies game creation and distribution with Cocos Creator, a free, open-source, cross-platform game engine. Empowering millions of developers to create high-performance, engaging 2D/3D games and instant web entertainment.
https://www.cocos.com/en/creator
Other
8.87k stars 2.05k forks source link

Unreasonable Inspector and Serialization Behavior #16569

Open smallmain opened 11 months ago

smallmain commented 11 months ago

Cocos Creator version

3.8.2-beta

System information

MacOS

Issue description

https://github.com/cocos/cocos-engine/issues/14550#issuecomment-1709543165 In version 3.8.2, the above issues are "solved", but it's still a confusing design, and some of the design doesn't even help anything. Code:

 @ccclass("B") 
 export class B { 
 @property 
 a = 11; 
 } 
 @ccclass("A") 
 export class A extends B { 
 @property 
 b = 1222; 
 } 
 @ccclass("NewComponent") 
 export class NewComponent extends Component { 
 @property(B) 
 test: B = new B(); 
 start() { } 
 update(deltaTime: number) { } 
 } 
  1. Now the inspector will show the declared type name of the property, but this does not solve the problem of the actual stored data being inconsistent with the declaration and completely uninformed: image How does this help solve the problem in the original post? The inspector shows the declared type, which I already knew when I wrote the code:
    @property(B) 
    test: B = new B(); 

    I already know in the code that it is "B", yes, I don't have to repeat it to me in the inspector, but what I need to know is what type it actually stores, look at the actual file:

    { 
    "__type__": "A", 
    "a": 22, 
    "b": 1222 
    }, 

    After actually deserializing, it is an instance of "A" at runtime, but this is not consistent with what I wrote in the code, but the point is that I have no way of knowing this unless I open the original file of the scene/prefab to view , which causes this code to fail:

    if(getClass(this.test) === B){ 
    } 

    The point is: I have no way of knowing this unless I open the raw file of the scene/prefab and view it. I have no way of knowing this unless I open the raw file of the scene/prefab and view it. I have no way of knowing this unless I open the raw file of the scene/prefab and view it. Of course, subclasses should be supported for serialization. In this case, you can clearly know that although cc.Asset is declared, the actual type is cc.Prefab:

    @property(Asset) 
    asset = null!; 

    image So the checker should show the actual stored type if the types are not exactly equal, rather than the declared type, and show it when equal, which is really of no use!

  2. There is still no method provided to help users migrate data. For example, when the types are inconsistent, you can manually pop up the menu and click the "Migrate to Type B" button to convert the data type to B: from:
    { 
    "__type__": "A", 
    "a": 22, 
    "b": 1222 
    }, 

    to:

    { 
    "__type__": "B", 
    "a": 22, 
    }, 

    Now if you know that the actual data is A, you can only reset the data of the entire component to convert the actual data type to B. The user will definitely lose the data that has been set. If you think this is useless, think about what if I renamed or simply deleted the A data type? All data will be lost.

  3. A single property can only be reset in the case of a type error, and it should be possible to right-click and reset back to the default value at any time.

Relevant error log output

No response

Steps to reproduce

原文:

https://github.com/cocos/cocos-engine/issues/14550#issuecomment-1709543165

在 3.8.2 版本中,“解决”了上面的问题,但是依然是令人困惑的设计,甚至一些设计对任何事都没有任何帮助。

代码:

@ccclass("B")
export class B {
    @property
    a = 11;
}

@ccclass("A")
export class A extends B {
    @property
    b = 1222;
}

@ccclass("NewComponent")
export class NewComponent extends Component {
    @property(B)
    test: B = new B();

    start() { }

    update(deltaTime: number) { }
}

1.现在检查器会显示该属性的声明类型名,但这并没解决实际存储数据与声明不一致并且完全不知情的问题:

image

这对解决原帖的问题有什么帮助?

检查器显示声明类型,我在写代码时就已经知道了:

    @property(B)
    test: B = new B();

在代码中就已经知道了它是 “B”,是的,不用再在检查器重复一遍给我看了,但我需要知道的是它实际存储了什么类型,请看实际文件:

  {
    "__type__": "A",
    "a": 22,
    "b": 1222
  },

真正反序列化后,它在运行时是一个 “A” 的实例,但这和我在代码中写的并不一致,但重点是我无法知道这个情况,除非我打开场景/预制体的原始文件查看,这导致这段代码将失效:

if(getClass(this.test) === B){
}

重点是:

我无法知道这个情况,除非我打开场景/预制体的原始文件查看。 我无法知道这个情况,除非我打开场景/预制体的原始文件查看。 我无法知道这个情况,除非我打开场景/预制体的原始文件查看。

当然应该要支持子类进行序列化,像在这种情况下可以非常清楚的知道虽然声明的是 cc.Asset,但实际类型是 cc.Prefab

    @property(Asset)
    asset = null!;

image

所以检查器应该在类型不完全相等的情况下显示实际存储的类型,而不是声明的类型,当相等时显示它,真的可以说没有任何用处!

2.还是没有提供一个可以帮助用户迁移数据的方法,比如当类型不一致时,可以手动弹出菜单点击 “迁移至 B 类型” 按钮将数据类型转为 B:

from:

  {
    "__type__": "A",
    "a": 22,
    "b": 1222
  },

to:

  {
    "__type__": "B",
    "a": 22,
  },

现在如果得知实际数据是 A 时,只能够重置整个组件的数据才能将实际数据类型转为 B,用户一定会丢失已经设置好的数据。

如果你觉得这没有用,想一想如果我是重命名或者直接删除了 A 数据类型呢?一样会丢失所有数据。

3.只能在类型错误的情况下重置单个属性,应该能够随时右键重置回默认值。

Minimal reproduction project

No response

jareguo commented 11 months ago

在代码中就已经知道了它是 “B”,是的,不用再在检查器重复一遍给我看了,但我需要知道的是它实际存储了什么类型,请看实际文件:

抱歉,这是我们实现的 bug,原先的 issue 希望达到的效果是

image

已在原 issue 中补充确认项

还是没有提供一个可以帮助用户迁移数据的方法,比如当类型不一致时,可以手动弹出菜单点击 “迁移至 B 类型” 按钮将数据类型转为 B。。。现在如果得知实际数据是 A 时,只能够重置整个组件的数据才能将实际数据类型转为 B,用户一定会丢失已经设置好的数据。

根据原贴的讨论,我没太 get 到这里会出问题的点。多态不是很正常的需求吗?子类转成父类应该是违反了编码上的意图才对。如果真的要强制转换,或许可以等我们之后在属性检查器上设计的“单属性 Reset 功能”做好。这样就不用整个组件重置了。(显而易见,对引擎的类型来说,多态是很常见的赋值手段,不适合全部提供菜单项,会干扰控件本身的 UI,本来 UI 就比较挤了)

3.只能在类型错误的情况下重置单个属性,应该能够随时右键重置回默认值。

同上,之后会提供此功能