每天都和编辑器作斗争之 Color.WHITE

用Cocos做游戏一个月左右,原来是做Unity,为了搞微信小游戏开始用Cocos。
初打开Cocos感觉到很熟悉的样子,颇有几分Unity的感觉,加上TS和C#也有那么几分相似,所以使用的时候也是继承了搞Unity的经验,想当然地把套路用在Cocos上。
比如说,你想有一个 Color 属性,把他初值设为白色, Color.WHITE,在Unity 里你这么来:

public Color color = Color.white;

Cocos中你想当然这样用:

@property(Color)
public color:Color = Color.WHITE;

是不是看起来挺正常的?把这两行加到你的component 中,你会看到如同预想中的:
image
不错不错,就像Unity一样
诶?下面怎么有个报错?
image
这是啥?什么 “不能给只读属性赋值” ,我代码里没定义什么只读属性,一定是像有的 prefab 一样经常乱报错,我reload一下会好了。
Reload之后你会发现,更糟了:
image

编译失败?我哪里写错了?
于是你上下求索,百思不得解,

终于,你点进 Color.WHITE,

static WHITE: Readonly<Color>;

你明白了,这是个只读值,不能改。
你是幸运的,因为我是在一个非 component 里定义的 color,而我也没有脚本编译失败的提示,只是反复不断报错,而且还引发了其他连锁问题。
正确的做法是这样定义一个初始值:

color:Color = new Color(Color.WHITE);

由于JS 或者 TS 没有类似 struct 的东西,所以不是primtive的值都是对象引用,而

color = Color.white;

这种写法是把一个只读对象给了 color,所以后续对他的写都是禁止的。如果你在代码中对它进行赋值vscode就会帮你高亮出来,但是我在这里的用法是在编辑器中对它的dump值进行操作,所以发现不了问题,但是在加载(deserialize)的时候就会报错。

是我的不好,但是我想问:如果 Color.WHITE 就这么一个用法,还要它做什么?能起到简化代码的目的吗?难道大家的项目里都是这样繁琐地使用?

看看Unity 的 Color.White 的实现:

//     Solid white. RGBA is (1, 1, 1, 1).
public static Color white
{
	[MethodImpl(MethodImplOptions.AggressiveInlining)]
	get
	{
		return new Color(1f, 1f, 1f, 1f);
	}
}

Cocos 在这里给一个 只读的 value,只是给引擎内部用的吗?

同样的问题还是 Vec3.ONE,也是一个只读值,这么让人混淆的使用,我实在是想不明白,于是在Github上找有几个项目使用了 Vec3.ONE,又是怎么使用的。可是我只看到 cocos engine内部对它的使用。

但是我却发现,在早期 3.0.0 以前的版本里,是有合理的版本的,从2.2.2 到 2.4.14(3.0.0之前最后一个版本),都有合理的常量定义,如:

tag : 2.2.2 vec3.js

/**
 * !#en return a Vec3 object with x = 1, y = 1, z = 1.
 * !#zh 新 Vec3 对象。
 * @property ONE
 * @type Vec3
 * @static
 */
js.get(Vec3, 'ONE', function () {
	return new Vec3(1.0, 1.0, 1.0);
});

tag: 2.4.14 vec3.ts

 /**
 * !#en return a Vec3 object with x = 1, y = 1, z = 1.
 * !#zh 新 Vec3 对象。
 * @property ONE
 * @type Vec3
 * @static
 */
static get ONE () { return new Vec3(1, 1, 1); }
static readonly ONE_R = Vec3.ONE;

在 2.4.14 这个版本里,合理给出了 ONE() get 属性 以及 ONE_R 只读变量。

可是到了 3.0.0 这么好的设计却没有了!
谁要用那个 只读变量 ONE! 要的是 get ONE()!

求求大大了,这个不是说 TS 的机制做不到,你哪怕要写成 ONE(),也比给个只读的量好吧!像 = Vec3.ONE, =Color.WHITE 的用法,该不是只有引擎内在用吧!

就是最近经常在说的要new对象来赋值吧哈哈哈哈哈

可以用c:color = Color.WHITE.clone()

自己定义一个全局变量COLOR_WHITE安逸

你居然拿来和unity比 差的不是一星半点

就是一个再简单不过的变量 或许提高了一丢丢可读性(简化了注释哈哈) :joy:

我只能说每家公司的思维是不同的,相对雅基,夸张点讲,如果你发现他的代码里有个1=2(举例表示一个显而易见的逻辑或代码语法错误)的代码,如果你提bug,他的第一反应大概率就是,请告诉我在哪个平台出现了什么bug,而不是去看代码可能造成的问题。
当然,我并不对此有任何反对,因为能跑就不要动,谁知道就因为这个错才没造成更大的错呢?但是吧,相对引擎,个人认为这种方式很容易造成屎山,越到后面越难维护,顺便可以联想到的就是cocos的各种不兼容。
针对你这个问题,我觉得不如像楼上的所说,自己定义一个方便的多。

提 bug 时把问题描述清楚这是很基本的要求啊。省去了沟通的成本。有些情况是只有在特定情况特定平台才出现的问题。

回到这个问题。我觉得 Color.WHITE 这样做也没什么问题。比如我代码就是想判断颜色是否是 Color.WHITE,每次 new 个对象出来也不合理啊。目前的这个设计可以根据自己的需求决定是要要克隆。

3赞

所以我说文化区别啊

  1. 明显的bug,一定要开发者提供完善信息才愿意去改bug,说实话,这种开发环境真的好安逸
  2. WHITE那个当然合理,所以呢?开发者的需求是合理呢?还是好用呢?

比js的undefined强就可以了 :rofl:

可以麻烦举个例子吗?我相信之所以要完善信息肯定是还有双方理解不一致或者不清楚的地方。

我觉得目前的设计是让开发者有选择而不是一味克隆一个出来。可能 C# 的垃圾回收做得很好,但是对于 javascript,是能不克隆尽量不克隆。

  1. 比如一个promise没有返回,对就是没有resolve也没有reject,我提交pr后,追问我复现条件以及为什么会影响
  2. 问题就是,大部分用户要修改啊,哪怕如楼主所说,哪怕来个可变和不可变的给用户选择也好啊

有感而发~还在用雅基的软件混饭吃,希望creator更好~

我猜测他这样问不是说这个 PR 有问题,而是想测试一下是否解决了问题。这样可以加单元测试或者测试例子。在 release note 也容易把影响说得清楚些。

那你的建议是什么?

Cocos研发确实很喜欢跟人要demo,基本上每个反馈帖子下面第一句话都是要demo
有些问题,别人都把报错日志、必现方式、改进方法、后续问题都贴出来了,他们还是直接要demo,有点像机器人回复

成本转移,人少就是这样。他们没有时间去一个一个的复现,你能让他直接复现,他成本就低,就更快处理。面对那么多开发者,看了帖子,一眼觉得不可能,就不会管你了,你要么给demo要么觉得你误报。正常。

这个帖子走向渐渐变成了《jara走后的每一天》哈哈哈

感觉相比判断颜色是否为白色的对比,个人更多的是直接赋值用得多,我这没用过判断一个颜色是否为白色。另外能帮忙看看这个问题吗,demo都给出了 https://forum.cocos.org/t/topic/158577

开发效率高、符合直觉的 API 设计永远优先于性能,我觉得性能只需要做到 “有办法” 就行。

之前的设计算符合,人们普遍使用 Color.WHITE,当出现性能问题、或者高级用户就会懂得改为使用 Color.WHITE_R

大家都觉得 new 的方式好的话,我们是可以改。不过有个问题就是,一旦改了就又破坏兼容性了。对兼容性的保证也导致了引擎的修改比较困难。

虽然需要的同学会说只要说明了就没问题,但实际遇到的情况是,只要这个修改影响到了你自己的项目,你就不会这么想了。

能理解,只希望这种技术、设计的债务会被记录好,在 4.x 的时候全部实现。