cocos-engine
cocos-engine copied to clipboard
[Proposal] Field unit editing experience
Problem Statement
It's nice if cc could provide the ablitity to freely change class field's unit for editing. For example, say we have a class:
class C {
@property angle = 0.0;
}
The class field angle
may denote an angle attribute of class C
. In most time, for performance reason, the class's implementer expects the angle to be in radians.
But as you know, the radians is not a good unit for editing(imagine what should you type if you want exactly 45° in radians). In such case, it would be friendly if user may edit that field in degrees and when editing completed, the value is converted into radians and then be stored into objects.
Proposal
I'd like to propose a solution for this problem.
The @property
decorator could futher accept an object type of decorator attribute unit
, which has the following signature:
interface PropertyDecoratorOptions {
unit: string | UnitDescription;
}
interface UnitDescription {
units: string[];
convert: (currentValue: number, currentUnit: string) => number;
}
in which:
-
If
.unit
is type of string, it behaves like before. Otherwise, a new machanism is introduced. As described by following. -
The
UnitDescription.units
lists all units' text that can be selectively shown and switched in editor inspector.For example:
['m, cm, km']
means that inspector may show text "m", "cm" or "km" in editor inspector. User may freely switch between them.The first one is shown by default.
-
The
UnitDescription.convert
is a function. Which is called, by editor, to ask users for how to convert the editing value into the value which it should emplace into the object. Where:-
Parameter
currentValue
is the editing value. -
Parameter
currentUnit
is the current unit selected by user.
-
Engine itself may provide convinient functions to represent some widely used units. For example:
export namespace unit {
export function length(desiredUnit: string, ...allowedUnits: string[]): UnitDescription;
}
In which:
-
Parameter
desiredUnit
is the unit of the value you want to be stored into objects. -
Paramter
allowedUnits
is the units having same semantic asUnitDescription.units
.
Users may use the functionality like this:
import { unit } from 'cc';
class A {
@property({
unit: unit.angle('rad', 'deg', 'rad'),
})
angle = 0.0;
@property({
unit: unit.length('m', 'cm', 'dm', 'km'),
})
length = 0.0;
}
Or differentiating UI components, where you can choose what UI to use. Example:
import { unit } from 'cc';
class A {
@property({
type: Number,
ui: 'silder',
min: 0,
max: 100,
})
num = 0.0;
@property({
type: Number,
ui: 'angle',
min: 0,
max: 100,
})
angle = 0.0;
@property({
type: Number,
ui: 'length',
min: 0,
max: 100,
})
length = 0.0;
}
The solution you proposed increases the project package size (runtime solution rather than editor solution) and introduces additional runtime overhead. So I'm a little concerned about the potential for abuse.
Why we dont just use standard JS way fits everyone's intuition.
class A {
@property
get rad () { return this.angle * RAD_TO_DEG };
set rad (value) { this.angle = value * DEG_TO_RAD };
@visible(false)
angle = 0.0;
}
I think this meets the needs of 99% of developers, because most people don't need to provide a so-called "variable unit" ui control, they just need to hard code a most commonly used unit.
需求汇总
进制转换:国标单位下的十进制、千进制、千分之一进制转换,便于输入(km/m/cm/mm...)
想想用户在调一个魔性手指的位置,你喜欢在输入框输入0.0079m呢,还是 0.79cm呢
这里要解决的是好不好输入的问题,而不是能不能输入的问题。但是容易导致滥用,会带来两个潜在风险
- 精度问题
小数点后面的精确有可能不够。一个 desiredUnit 是 km 的值,如果接收到一个 cm 的输入,精度在 32 位上很可能丢失。 例如 speed = 0.79(cm); this.x = 1(km); this.x += this.speed(此时精度出现丢失)。 当不支持进制转换时,用户只能输入 speed: 0.0000079,此时会很容易发现精度问题。而使用 cm 输入时输入 0.79 则不会意识到精度问题。
- 统一问题
允许自定义每个属性设置 desiredUnit,并且此设置在装饰器而不是变量名上体现的话,容易导致项目里的单位或者进制不统一。不看代码的话单看 API 搞不明白用的是什么单位。编辑器用户是方便了,但是写代码的人负担就大了。
- 兼容性问题
desiredUnit 发生改变时,场景中的值是否应该重刷一遍,进行自动转换?比如从 hour -> second 时,所有值要自动 * 60....
- 体验一致性问题
场景中设置的单位要保存在哪里?如果不保存,用户每次重开场景,之前输入时使用的单位都会变成默认的,还得自己切换一次。 比如设好了 hour,结果每次打开都变成 s 甚至 ms,还得再切一次否则看不清楚时间到底多久....
单位转换:非国标单位之间的转换,便于人类理解(光年、英寸、尺、盎司、欧元、弧度、四元数、lux、HSV、bit...)
这里要解决的其实是能不能输入的问题,而不是好不好输入的问题。很多单位用户是无法直接用编辑器输入的,不符合人类的思考习惯。
这种需求其实我们现在已经能很好的满足了,做法无非就是
- 对控件进行扩展,扩展出 Color 输入框,由输入框解决各种 HSV/HEX 之间的转换
- 对 API 进行扩展,添加 node.eulerAngle
如果要支持此提案,除了上面 4 个,还会带来其他风险:
5、运行时包体变大
unit 这个机制这么好用,运行时是否也要支持?就算运行时不用支持,用户声明的装饰器、UnitDescription 信息、自定义转换函数也无法剔除。
6、需求蔓延
用户定义 convert 时,得支持各类单位,但是这里的转换其实绝大多数情况下是 1 对多就够了。可是由于 UnitDescription 并不知道这里面的 desiredUnit 是谁,因此需要写很多无用的转换公式。 例如 HSB <-> HSV,尺 <-> 英寸,四元数 <-> 角度,这些往往都不需要。真正用户需要用的只有 RGB、米、弧度这些 desiredUnit 到其他单位之间的转换就够了。