参考
每一个绑定,什么会触发它,以及它不会做什么。
所有内容都在 Udonite.Binding 中。
每个绑定的形状都一样:
| 成员 | 作用 |
|---|---|
Watch(control) |
开始盯住;只触发事件 |
Bind(control, observable) |
同上,并让一个 Observable 保持同步 |
Poll() |
读取控件;在 Update 里调用 |
Unbind() |
停止盯住,并放开 Observable |
Value |
不管有没有绑 Observable,控件当前的值 |
规则也一样:
- 绑定的那一刻就写入控件,所以它一开始显示的是值,而不是设计师随手留下的内容。
- 再次绑定会替换上一次,所以跑两遍的
Start只会留下一个。 - null 的控件或 Observable 会被忽略,而不是让 behaviour 停摆。
- 已经加上的处理器会留着,无论你再
Watch还是Unbind。它们属于添加的人,而不属于控件;要丢掉就把事件字段清空。 - 只有玩家的改动会触发事件。 通过给 Observable 赋值来移动控件不会。
被盯住的控件
这些需要在 Update 里 Poll()。
SliderBinding
| 成员 | |
|---|---|
Action<float> OnChanged |
玩家移动手柄时触发 |
Bind(Slider, Observable<float>) |
双向 |
float Value |
手柄在哪 |
ToggleBinding
| 成员 | |
|---|---|
Action<bool> OnChanged |
玩家点击时触发 |
Bind(Toggle, Observable<bool>) |
双向 |
bool Value |
是否勾选 |
InputBinding
| 成员 | |
|---|---|
Action<string> OnChanged |
每一次按键 |
Action<string> OnEndEdit |
玩家改动后离开字段 |
Action OnFocused |
玩家开始编辑 |
Bind(TMP_InputField, Observable<string>) |
双向 |
string Value |
字段里的内容 |
OnChanged 每按一个键就报告一次,因为绑在实时标签上的字段本该如此。如果你不要这个,就在按下按钮时读一次 Value。
玩家正在打字时写入 Observable 会把光标弹到末尾。绑定自己不会这么做(轮询先把玩家的文字取进来,写回去的是同一个字符串),但在编辑过程中赋值的代码会。
被驱动的控件
没有事件,也不需要 Poll()。Observable 就是喂给它们的方式。
TextBinding<T>
| 成员 | |
|---|---|
Bind(TextMeshProUGUI, Observable<T>) |
显示这个值 |
Bind(TextMeshProUGUI, Observable<T>, prefix, suffix) |
前后加字 |
例如 Bind(label, score, "Score: ", "") 和 Bind(label, health, "", " HP")。null 值显示为空字符串,而不是出错。
ActiveBinding
| 成员 | |
|---|---|
Bind(GameObject, Observable<bool>) |
为 true 时显示 |
BindInverted(GameObject, Observable<bool>) |
为 false 时显示 |
在同一个 Observable 上绑两个,就是一对配套的牌子。
被隐藏物体上的 behaviour 会停止运行。 隐藏绑定所在的物体会让它的 Update 停下,上面的 Poll() 也跟着停。把需要轮询的绑定放在一直显示的物体上。
FillBinding
| 成员 | |
|---|---|
Bind(Image, Observable<float>) |
已经在 0 到 1 之间的值 |
Bind(Image, Observable<float>, min, max) |
你指定范围的值 |
范围是绑定的一部分,因为 fillAmount 是 0 到 1,而世界要数的东西几乎都不是。超出范围的会被夹住,空的或反过来的范围只会得到空的图,而不是除以零。
要让 fillAmount 起作用,Image 的 Type 必须设为 Filled。
它不做什么,以及为什么
按钮。 点击是事件而不是值。Slider.value、Toggle.isOn 和 TMP_InputField.text 随时可读,"上一帧到现在有没有被点过"读不到,也就没有可轮询的东西。按钮仍然需要在 Inspector 里连到一个公开方法。
URL 输入框。 VRCUrlInputField 接受选中却从不获得焦点——直接调用 ActivateInputField() 后 isFocused 仍为 false,这是实测的。VRChat 通过自己的键盘把文字送进去,所以在编辑器里根本无法验证。按 VRChat 的文档,在 Inspector 里用它的 OnEndEdit。
成排的列表。 用 ObservableList<T> 驱动一组行意味着创建和销毁物体,而在世界里这意味着 VRCObjectPool 和一堆关于所有权的决定。那是另一个包。
如果处理器抛出异常
在 Udon 里,一个异常会让 behaviour 在本次会话余下的时间里停摆,也没有办法只接住某一个再继续下一个。抛出异常的处理器会把剩下的通知、以及那个 behaviour 之后本该做的一切一并带走,所以处理器最好只做读值和更新这两件事。