Unity uGUI原理解析
Unity uGUI 是Unity官方推出的UI解决方案。 以Package的方法在Unity中提供。官方参考手册 API参考
Unity uGUI的使用可以参考超详细的Unity UGUI教学
准备工作
由于UGUI是Untiy提供的核心包,为了能够看到看到及调试源码。 需要在 Preferences -> External Tools -> Generate .csproj file for: 中勾选 Built-in packages. 重新生成后就可以在Visual Studio 中看到 UnityEngine.UI
项目。到这里就可以正常使用附加到Unity
功能来进行UGUI代码的调试工作。
使用 Visual Studio 的查看类图功能我们可以看到UnityEngine.UI
的类图。生成类图可参考VS 代码结构自动生成类图.
具体类图大概如下(Unity版本2019.4.20):
类图分析
从类图上可以看出UGUI中UI组件的基类为 UIBehaviour。 而UI组件大致可以分成:
- 事件系统
- EventSystem
- PointerInputModule
- 组件
- 遮罩相关(Mask、 RectMask2D)
- UI效果,网格修改相关(实现接口IMeshModifier, Shadow、OutLine)
- Selectable(Toggle、Scrollbar、Dropdown、Button、InputField、Slider)
- Graphic图像相关(RawImage、Image、Text)
- 布局
- ILayoutController,ILayoutElement 等等
UGUI原理
在UGUI中,UI的组成大致就是界面和事件。
在创建UI时会自动生成 Canvas
以及 EventSystem
, 这两个就对应界面以及事件。这里的 EventSystem
通常还会和 Input Module
一起出现。
EventSystem
继承自 UIBehaviour
, 我们可以看到在OnEnable的时候将自身添加到m_EventSystems
这个静态变量中, 而在OnDisable的时候将自身从m_EventSystems
移除。 这个步骤的目的是为了在 Update 中判断 current 是否为自身。 就是说如果存在多个EventSystem
组件则只会有一个触发Update中的内容。
在Update的最末尾,经过一系列的判断后会调用InputModule的Process方法。
public class EventSystem : UIBehaviour
{
//...
protected override void OnEnable()
{
base.OnEnable();
m_EventSystems.Add(this);
}
protected override void OnDisable()
{
if (m_CurrentInputModule != null)
{
m_CurrentInputModule.DeactivateModule();
m_CurrentInputModule = null;
}
m_EventSystems.Remove(this);
base.OnDisable();
}
protected virtual void Update()
{
if (current != this)
return;
TickModules(); // <<-- 在这里触发 InputModule 的 UpdateModule 方法
//...
if (!changedModule && m_CurrentInputModule != null)
m_CurrentInputModule.Process(); // <<-- 在这里进行输入的处理
}
//...
}
InputModule 有 PointerInputModule, StandaloneInputModule, TouchInputModule, 这些类都继承自BaseInputModule。
这里以StandaloneInputModule
为例, 其中的 Process 函数就是在EventSystem中Update里面调用的方法。在这当中还会调用 ProcessMouseEvent
来处理鼠标事件。而在处理事件之前我们还需要知道点击了那个组件。 所以GetMousePointerEventData中还会调用 EventSystem.RaycastAll
来对继承自BaseRaycaster
的组件进行射线检测。
public class StandaloneInputModule : PointerInputModule
{
//...
public override void Process()
{
if (!eventSystem.isFocused && ShouldIgnoreEventsOnNoFocus())
return;
// ...
// touch needs to take precedence because of the mouse emulation layer
if (!ProcessTouchEvents() && input.mousePresent)
ProcessMouseEvent(); // 处理鼠标事件
// ...
}
/// <summary>
/// Process all mouse events.
/// </summary>
protected void ProcessMouseEvent(int id)
{
var mouseData = GetMousePointerEventData(id); // << 在这里面会调用 EventSystem.RaycastAll 来执行射线检测
//...
}
}
总结:EventSystem主要就是通过输入模块(InputModule)对所有的需要被检测的对象(BaseRaycaster)进行检测。 通常我们看到的是 GraphicRaycaster (和Canvas挂载在统一游戏对象上)来进行射线检测。 然后再通过GraphicRaycaster调用每一个实现了ICanvasRaycastFilter接口的Graphic(也就是RawImage、Image、Text)。