Unity3D Editor ControlID 简单介绍
Posted 暗光之痕
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity3D Editor ControlID 简单介绍相关的知识,希望对你有一定的参考价值。
环境:Unity2021.1.14 Odin3.0.4 语言:C#
面向:Editor开发人员
总起
当我在工具代码中看到这行代码时:
GUIUtility.hotControl = id;
我产生了一个疑问,ControlID是什么?
在第19章 GUI を自作する - エディター拡張入門中的介绍也只是寥寥几笔:
为每个GUI分配一个ControlID,此ID使每个GUI独立,如果没有正确分配这个ID,你最终会遇到多个GUI操作的冲突。比如当鼠标拖动GUI.Window时,范围选择工具起作用了。
生成/获取ControlID:
int id = GUIUtility.GetControlID(FocusType.Passive, rect);
通过ControlID控制样式:
EditorStyles.objectFieldThumb.Draw(rect, content, id);
而使用GUIUtility.hotControl可以找出当前处于焦点的ControlID,或者使用GUIUtility.keyboardControl你可以知道键盘的焦点。
一个例子
在Odin的使用中我发现了以下的bug(或者说设计如此):
我设计了这么个界面,Data中显示Datas的其中一个数据,然后选择显示哪个数据,由ChooseIndex决定。
代码如下:
using System;
using Sirenix.OdinInspector;
using UnityEngine;
public class TestControlID : MonoBehaviour
{
[Serializable]
public class Data
{
[ShowIf("@this.mode == Mode.Show")]
public int testInt;
public Mode mode = Mode.Show;
}
public enum Mode
{
Show,
DontShow,
}
[OnValueChanged("Choose")]
public int chooseIndex;
public void Choose()
{
if (chooseIndex >= 0 && chooseIndex < datas.Length)
{
data = datas[chooseIndex];
}
}
public Data data;
public Data[] datas;
}
先把数据全选成NotShow,之后选择第一个Data将 Mode选择Show,再切到第二个,这时会神奇的发现第二个Data也“粘连”修改成了Show。
这个问题最终原因就是出在ControlID上:
- Odin使用的enum popup有两段操作,鼠标按下和抬起都会使值触发变化;
- 鼠标按下已经使值从DontShow改成了Show,造成了TestInt被隐藏,这导致了当前Mode框的ControlID发生了变化;
- 鼠标抬起也会触发值的变化,但此时的ControlID已经发生了变化,所以OdinSelector<T>.confirmedPopupControlId虽然被记录下来,但并没有触发;
- 等到我们改换显示第二个数据,因为计算出来ControlID是一致的,所以第二段此时才进行触发;
产生“粘连”修改问题,也很好解决:
- 禁用这种二段触发的popup,可以使用Unity原生popup;
- ControlID在计算的时候可以率先分配一个不共用的id段;
- 会被隐藏的属性都放到mode下面,这样ControlID不会发生变化。
这个例子直接放到InspectorWindow上进行切换是不会有问题的,Unity的处理方式实际就是第2种。
不过落到具体的方式上Unity2021和Unity2017还有所不同,2021采用了切换时会一直增长的方式,而2017则会为每个GameObject分配单独的ControlID。
总结
ControlID的具体算法因为是写在C++层的,我还是不是很了解,有大佬知道的话可以指引一下。
大体的作用实际就是标明每个控件的唯一ID,但是这种做法也会有局限,就是缓存下来的这个ID可能会随时变换,这是一个不稳定的ID。
以上是关于Unity3D Editor ControlID 简单介绍的主要内容,如果未能解决你的问题,请参考以下文章