Unity 开发高级/资深 04:UI、交互与输入

Unity 开发高级/资深 UI

返回总览

相关图示:工具速查总览

UI 框架

  • UI 分层:HUD、普通面板、全屏面板、弹窗、Toast、引导、Loading、系统提示。
  • 生命周期:打开、关闭、隐藏、恢复、缓存、销毁、重复打开、快速关闭。
  • 面板栈:返回键、遮罩层、互斥面板、弹窗优先级、二次确认。
  • 异步加载:面板资源加载中取消、加载失败重试、加载完成时状态是否仍有效。
  • 数据绑定:手动刷新、事件刷新、响应式绑定的成本和适用范围。
  • 红点系统:数据源、依赖树、聚合规则、缓存、刷新频率、调试视图。
  • 通用组件:奖励格子、货币栏、页签、列表、筛选、排序、确认框、Loading。
  • 适配:安全区、刘海屏、平板、PC 窗口、超宽屏、横竖屏。
  • 性能:Canvas 拆分、避免频繁 Rebuild、图集、字体、ScrollView 虚拟列表。

UI 分层

HUD 全称 Heads-Up Display(抬头显示),指常驻屏幕、不打断操作的信息层(血条、小地图、货币等)。

分层主要靠 Canvas / sortingOrder,不是靠 CanvasGroup。CanvasGroup 管显隐、透明度和能否点击。

逻辑层 典型 sortingOrder 区间 作用
HUD 100 常驻信息,不挡操作
普通面板 200 背包、商店等
全屏面板 300 主界面、结算
弹窗 400 确认框、二次确认
Toast 500 飘字
引导 600 遮罩、高亮
Loading / 系统提示 700+ 最高,必须盖住一切

落地习惯:

  • 每个逻辑层一个根节点(空节点或独立 Canvas);打开面板时挂到对应层并设 sortingOrder。
  • Canvas 按重建成本拆:常驻少变(HUD)、动态频繁、Toast/Loading/引导常单独 Canvas。
  • 不同 Canvas 不能合批;合批发生在同一 Canvas 内。拆层是用 Draw Call 换 Rebuild 隔离。

UIManager 用层枚举映射层根与 sortingOrder,业务不要手拖层级。

生命周期

收成 状态机 + UIManager 统一调度,业务不直接 Instantiate / Destroy。

典型状态:None → Loading → Opening → Opened → Hiding → Hidden,以及 Closing → Closed(Cached 或 Destroyed)。

操作 含义
Open 加载 → 挂层 → 开启动画 → 绑数据/事件
Close 解绑 → 关动画 → 出栈;再决定缓存或销毁
Hide 不销毁,常不卸资源;被全屏盖住时用
Resume/Show 从 Hidden 回来,刷新数据,不一定重新加载
Cache 关了但实例留下,下次秒开
Destroy 卸事件、卸资源、销毁实例
重复打开 聚焦置顶 / 刷新 / 忽略,看配置
快速关闭 取消加载、打断动画、防回调落到已关面板

配置驱动:层级、是否互斥/进栈、关闭策略(Destroy / Cache / HideOnly)、缓存上限、是否允许重复打开。

缓存淘汰常用 LRU(Least Recently Used,最近最少使用):满了或内存紧时先丢最久未打开的面板。

关闭必做:取消异步与网络回调、解绑事件/红点、停动画、归还列表池、Release 资源句柄。

面板栈

可入栈的界面(普通面板、全屏、弹窗)推进栈;HUD / Toast / Loading 通常 不入栈。

  • 返回键 / Esc:全局 UIManager.Back(),只关栈顶;BlockBack(二次确认、引导、Loading)时吞掉或走面板 OnBack。
  • 遮罩:弹窗/全屏自动加 Mask;CloseOnClickMask 可配;sortingOrder 夹在下层与当前弹窗之间,防点穿。
  • 互斥:配置互斥组,Open 前由 Manager 关同组或 Hide 被盖住的面板,并同步改栈。
  • 弹窗优先级:系统断线 > 付费/重要奖励 > 普通确认 > 弱提示/运营窗;高优先级可压低优先级,同级排队。
  • 二次确认:独立高优先级入栈;返回键等于取消;回调前检查原面板是否仍在。

面板配置常见字段:Layer / 是否入栈、MutexGroup、Priority、ShowMask、ClickMaskClose、HandleBack。

异步加载

把「打开面板」当成带生命周期的异步请求,贯穿 openId + CancellationToken。

流水线:Open(requestId) → LoadAsync → 成功则校验仍有效再实例化进栈 → 无效则 Destroy/Release → 失败则有限重试或失败 UI。

取消后 Addressables 句柄要 Release。 句柄(Handle)是一次异步加载的凭证(如 AsyncOperationHandle);取消是业务语义,Release 是资源语义,所有出口(成功/失败/取消/openId 失效)走同一 Cleanup(),且只 Release 一次。

资深常盯四点:

  1. 取消后句柄有没有 Release。
  2. 失败重试是否也看 token(Delay 也传 token;用户已取消不要再弹失败框)。
  3. 连点:按钮防抖 + Manager 对同面板 Opening 做 Ignore / Join / Replace;已 Opened 则 Refresh 置顶。
  4. 加载完成回调先回主线程再碰 Unity 对象;再验 openId,无效则绝不 Instantiate。

加载中算 Opening;可先不正式 push,或 push Loading 占位。快速关闭 = Cancel + 清理同面板排队请求。

数据绑定

大厂很少全项目一种绑定,按变更频率与成本混用:

模式 做法 适用
手动刷新 OnOpen / Refresh() 赋值 打开时定死、列表 cell 的 SetData
事件刷新 领域事件订阅后改 UI 货币、邮件、多界面要听的推送
响应式绑定 Observable / MVVM 声明式 表单、设置、局部双向;少全量上

注意:关面板退订;打开先 Snapshot 再 Subscribe;事件风暴合并刷新;列表不对每行做深度响应式。成本不仅在 GC,更在生命周期与可调试性。

红点系统

独立子系统(树 + 叶子算、向上聚),UI 只绑节点 key。

  • 数据源(叶子):未读数、可领奖励、新解锁未点等;业务变更时 Notify,禁止 UI Update 轮询。
  • 依赖树:配置/注册 父 ← 子;动态活动可增删子节点。
  • 聚合:OR(入口最常见)、SUM(数量)、MAX(优先级)、少数 CUSTOM。
  • 缓存:节点缓存 isShow / count / dirty;脏了向上传。
  • 刷新频率:同帧多叶子变 → 标脏 → 帧末/节流再算,只对真变化的节点通知 UI。
  • 调试视图:整树可视化、叶子条件、已读标记、强制 Set;能解释「为什么亮/不亮」。

适配

原则:规则吃掉设备差异,禁止每个面板手调一版。

  • 安全区 / 刘海:Screen.safeArea(必要时厂商校正)→ 全局 SafeRoot padding;方向/分屏变化要重算。
  • CanvasScaler:固定设计稿契约(如 1080×1920)+ Match;美术出一套锚点规范。
  • 平板:IsTablet 判断;限制内容最大宽、居中,避免巨空白。
  • PC 窗口:监听 resize;最小窗口;Mobile/PC profile。
  • 超宽:中间安全内容区(如仍按 16:9 逻辑宽);左右贴边仍受 SafeArea 约束。
  • 横竖屏:锁方向最省事;双方向由 OrientationService 广播,面板切布局,不各自听 orientation。

性能

UGUI 瓶颈主要在 Rebuild(CPU) 与 Draw Call / Overdraw(GPU)。

Canvas 拆分: 静态 vs 动态、按层、高频小组件;嵌套 Canvas 把狂刷子树隔离。跨 Canvas 不合批。overrideSorting 默认关,只为排序不够用再开,并测 Mask。

少 Rebuild: 脏标记合并写 UI;赋值前比较;少 Layout 套娃;动画避开 Layout 子节点;整页淡入优先 CanvasGroup。

图集: 按模块/生命周期打包;一界面少图集;动态头像勿期待与静态合批;Mask/多材质仍断批。

字体(TMP): 常用字预热;控动态扩图集;少材质变体;列表少富文本、少每帧改字。

虚拟列表: 只创建可见区+缓冲 cell,滑出回收 SetData;固定行高优先;回收解绑事件/红点/异步回调(index/version 防错图)。

排查顺序:Profiler UI Rebuild → Frame Debugger DC → 打开/滑动卡顿查虚拟列表与字体扩容。

需要掌握的工具

  • UGUI:Canvas、Graphic、Layout、EventSystem、Mask、ScrollRect(见下节深度要求)。
  • TextMeshPro:字体资产、Fallback、富文本、缺字处理。
  • UI Toolkit:了解适用场景,尤其是编辑器工具和部分运行时 UI。
  • Unity Profiler UI 模块:分析 Canvas Rebuild、Batch、Layout 和 GC。
  • Frame Debugger:检查 UI Draw Call、图集和材质切换。
  • Device Simulator:验证安全区、分辨率、横竖屏和屏幕比例。
  • Figma、Sketch、Photoshop 标注:对齐视觉尺寸、切图、字体和交互动效。
  • FairyGUI、UGUI Extension 或项目自研 UI 工具:根据团队技术栈掌握。

UGUI 深度要求(高级/资深)

不是「用过组件」,而是能讲原理、排坑、定规范。

组件 要求
Canvas Render Mode 选型;sortingOrder 分层;拆分/嵌套与 Rebuild vs 合批交易;Scaler 与 SafeArea
Graphic 脏与重建;Raycast Target 能关尽关;合批与断批;CanvasGroup 整页淡入
Layout 知代价;固定尺寸/锚点优先;虚拟列表 cell 少套娃;动画不打在 Layout 链上
EventSystem 命中链;单例;UI/场景隔离;Loading/引导门控;手柄焦点
Mask Mask(Stencil) vs RectMask2D;列表用矩形裁;知合批代价;嵌套 Canvas 要测裁剪
ScrollRect 拖点冲突阈值;嵌套滚动;虚拟列表为默认加分/必考

自学优先序:Canvas/Rebuild 合批 → EventSystem 命中 → ScrollRect 虚拟列表 → Layout/Mask 代价 → Graphic 脏与 Raycast。

可继续细分方向

  • UI 生命周期和面板栈。
  • 红点系统和功能入口。
  • UI 适配和多语言布局。
  • UI 性能、图集和虚拟列表。
  • 引导框架与目标寻址。

交互与输入

  • 输入抽象:触控、键鼠、手柄、快捷键统一成项目内的输入事件。
  • UI 与场景隔离:点击 UI 时不触发场景射线,拖拽时不误触点击。
  • 射线检测:地面、NPC、建筑、道具、怪物、UI 的 Layer 和优先级清楚。
  • 交互距离:可点击、可靠近、自动寻路、距离不足提示、交互冷却。
  • 手势:拖拽、滑动、双指缩放、长按、短按、惯性、边界限制。
  • 键鼠:悬停提示、右键菜单、快捷键冲突、输入框焦点。
  • 手柄:焦点导航、确认/取消、摇杆移动、功能轮盘。
  • 防误触:连点保护、点击穿透、Loading 遮罩、网络请求中的按钮状态。

UI 与场景点击优先级

三个角色:

组件 挂哪 作用
EventSystem 场景通常 1 个 收输入、RaycastAll、派发事件
GraphicRaycaster 可点 UI 的 Canvas 命中 Graphic
PhysicsRaycaster 玩法 Camera 命中 3D Collider

流程:InputModule → RaycastAll(UI + 场景)→ 结果排序 → 交给最前对象。

排序概念上比:sortOrderPriority → renderOrderPriority → sortingLayer/Order → depth → distance。GraphicRaycaster 跟 Canvas Sort Order 挂钩;PhysicsRaycaster 默认 priority 往往最低,正常 UI 通常排在 3D 前面——前提是 UI 真被射中。

EventSystem 上没有「UI vs 场景优先级」总开关。 可调的是:

想控制的 去哪设
UI 之间谁先被点 Canvas Sort Order / Sorting Layer
UI 能不能被点 Graphic Raycast Target + GraphicRaycaster
UI 是否被 3D 挡(Camera 模式) GraphicRaycaster Blocking Objects / Mask
场景能点哪些 PhysicsRaycaster Event Mask

两种架构:

  • 场景也走 EventSystem(IPointerClick):排序基本交给引擎;遮罩必须能 Raycast。
  • 场景自研 Physics.Raycast(更常见):引擎不会自动挡住玩法射线,必须门控:IsPointerOverGameObject 或 InputGate.CanSceneInteract()(Loading/模态/引导/指针在 UI 上则禁止场景点击)。

遮罩靠「可 Raycast 的全屏图 + CanvasGroup.blocksRaycasts」,不是靠半透明「看起来挡住了」。拖拽相机时在 PointerDown 采样是否在 UI 上,整段手势策略一致。

需要掌握的工具

  • Unity Input System:抽象触控、键鼠、手柄、快捷键和重绑定。
  • EventSystem、Graphic Raycaster、Physics Raycaster:处理 UI 和场景点击优先级(见上节)。
  • Physics Debugger:排查射线、碰撞层和交互触发问题。
  • Cinemachine:处理镜头跟随、缩放、碰撞和交互聚焦。
  • Device Simulator 与真机调试:验证触控、手势、安全区和设备返回键。
  • 自研输入调试面板:显示当前输入源、命中对象、射线路径和焦点状态。

可继续细分方向

  • UI 输入与场景输入隔离。
  • 触控手势。
  • 键鼠和手柄适配。
  • 射线交互、防误触和焦点系统。

UI 架构常见模块

  • UIManager:统一打开、关闭、查找、层级、返回、缓存。
  • WindowBase:面板生命周期、资源句柄、事件注册、动画状态。
  • ViewModel 或 Presenter:隔离表现和业务数据。
  • RedDotManager:红点树、节点刷新、调试路径。
  • GuideLayer:引导遮罩、聚焦目标、点击穿透、步骤恢复。
  • SafeAreaAdapter:统一处理设备安全区。
  • ListVirtualizer:大量列表复用和增量刷新。

引导框架要点

配置驱动步骤 + 高优先级 GuideLayer;强/弱引导策略不同,共用 Manager。

三坑与底线解法:

  1. 勿用 Hierarchy 绝对路径找按钮 → 稳定 key + 注册表(Register/Unregister);动态目标用业务 ID;预制体可挂 Authoring 组件自动登记。
  2. 引导勿私自 Instantiate 业务面板 → 只调 UIManager.Open;步骤用 requires/commands 声明依赖;恢复读档也跑同一套 Open。
  3. 必须有超时与失败出口 → findTimeout;SkipStep / SkipGuide / AbortUnlockInput;失败必撤遮罩、解锁输入、写档防反复卡;打点 + 远程关引导。

进阶:强类型 TargetId、Step 依赖图、失败降级弱引导、关键进度以服务端为准、能减则减强引导。调试要能回答「卡在哪一步、目标是谁、为什么找不到」。

开发高级/资深关注点

  • UI 打开失败是否有日志和用户可恢复路径。
  • UI 关闭后是否释放事件、异步回调和资源引用(含 Addressables 句柄)。
  • 快速关闭、重复打开、加载完成时 openId 是否仍有效。
  • 红点是否能解释“为什么亮”和“为什么不亮”。
  • 适配是否用规则解决,而不是每个界面手动调。
  • 列表、背包、邮件、排行榜等高频 UI 是否做虚拟化和增量刷新。
  • 引导是否可失败退出、可远程关闭,寻址是否与正式 Open 同路径。
  • UI/场景点击是否有统一输入门控,遮罩是否真正可 Raycast。

评论