8.总结
8.1 知识点

学习的主要内容

UI功能制作套路

拼面板——注意事项

写代码——注意事项

思考

8.2 核心要点速览
工程与 UIRoot 准备
搭建顺序:新建工程 → 导入 NGUI → 接入 XML 管理脚本 → 导入美术与 UI 资源。
| 目录 | 常见用途 |
|---|---|
Resources |
Instantiate 动态加载的 UI 预设路径 |
StreamingAssets |
可读配置(如服务器列表 XML) |
Scripts / ArtRes |
脚本与美术资源,按项目习惯固定 |
UIRoot 与相机
- 场景里建根节点挂 UIRoot;对照分辨率可先用 Texture 铺底,再切 Sprite 看像素级对齐;横屏常勾 适应高,短边铺满。
- NGUI 相机只渲 UI,主相机不要渲 UI:深度、适配、射线都更干净。
拼面板与 DrawCall 习惯
| 做法 | 目的 |
|---|---|
| Panel 下 Texture 对照示意图 | 对齐比例,拼完删掉对照底图 |
| 登录 / 注册图集分开 | 减体积、减无关图挤同一图集 |
| Sprite 九宫格 + 锚点统一 | 多分辨率下底和按钮不易拉花 |
| Collider + UIButton / Input / Toggle | NGUI 点击与输入依赖碰撞体 |
| Label Depth 提高、少与图穿插 | 降低合批被打断、DrawCall 飙高 |
自测清单
- 长文案、换分辨率是否还能读。
- 打开 DrawCall 统计,穿插严重就调 Widget Depth。
面板基类与提示条
| 时机 | 做什么 |
|---|---|
Awake |
instance = this as T,注册泛型单例 |
Start |
调子类 Init(),子类一般不再写 Start |
ShowMe / HideMe |
默认 SetActive;动画、暂停等子类重写 |
public abstract class BasePanel<T> : MonoBehaviour where T : class
{
private static T instance;
public static T Instance => instance;
protected virtual void Awake() { instance = this as T; }
protected virtual void Start() { Init(); }
public abstract void Init();
public virtual void ShowMe() { gameObject.SetActive(true); }
public virtual void HideMe() { gameObject.SetActive(false); }
}
TipPanel:常驻场景,**Init 末尾 HideMe**;确定键用 EventDelegate 关板。对外 ChangeInfo 改文案,调用方先 ShowMe 再改字。
TipPanel.Instance.ShowMe();
TipPanel.Instance.ChangeInfo("账号和密码都必须大于6位");
登录、注册与本地存档
| 类型 / API | 作用 |
|---|---|
LoginData |
账号、密码、frontServerID、记住密码、自动登录 |
RegisterData + SerizlizerDictionary<string,string> |
注册表,便于 XML 序列化 |
LoginMgr |
构造里 LoadData 三类数据;SaveLoginData / SaveRegisterData 写回 |
RegisterUser / CheckInfo |
注册判重、登录校验 |
ClearLoginData |
注册成功后 frontServerID = 0,不沿用旧账号选服 |
Toggle 互锁(业务语义)
| 操作 | 结果 |
|---|---|
| 取消「记住密码」 | 强制取消「自动登录」 |
| 勾选「自动登录」 | 强制勾选「记住密码」 |
面板切换
| 操作 | 代码思路 |
|---|---|
| 登录 → 注册 | HideMe() → RegisterPanel.Instance.ShowMe() |
| 注册 → 取消 | HideMe() → LoginPanel.Instance.ShowMe() |
| 注册成功 | LoginPanel.Instance.SetInfo(user, pw) 回填 |
服务器面板与选服流程
frontServerID 与登录后去向
| 条件 | 打开谁 |
|---|---|
frontServerID == 0 |
未持久化过选服 → 选服面板 |
!= 0 |
有过记录 → 服务器面板 |
if (LoginMgr.Instance.LoginData.frontServerID == 0)
ChooseServerPanel.Instance.ShowMe();
else
ServerPanel.Instance.ShowMe();
ServerPanel 要点
- 换区:
ChooseServerPanel.ShowMe+ 自己HideMe。 - 进游戏:先
SaveLoginData(把当前服 ID 落盘)再LoadScene。 ShowMe:用frontServerID查serverDic更新labInfo。- 返回:
LoginPanel.ShowMe+HideMe。
选服面板要点
- 左侧
ServerItem:每 5 个区一段,InitInfo(begin, end)。 UpdatePanel:先Destroy旧右侧项 →Clear列表 → 再Instantiate右侧ServerChooseItem。- 右侧点击:
frontServerID = nowInfo.id→ 关选服 →ServerPanel.ShowMe。
其它
Server字段标[XmlAttribute],XML 更紧凑;配置可放StreamingAssets,联网可改为服务端下发。- 自动登录:
Init里若autoLogin,校验与分支同登录按钮。 - 多面板
Start里抢显隐:用 Script Execution Order 排稳顺序。
实现顺序建议
| 步骤 | 内容 |
|---|---|
| 1 | 工程、UIRoot、目录与资源 |
| 2 | BasePanel + TipPanel + 事件委托 |
| 3 | 登录 UI → LoginData / LoginMgr → 注册 → RegisterData / SetInfo |
| 4 | ServerPanel:换区、进场景、返回 |
| 5 | ServerInfo XML + ChooseServerPanel + UpdatePanel |
| 6 | 串联 frontServerID、SaveLoginData、ClearLoginData、自动登录与执行顺序 |
8.3 面试题精选
基础题
1. 做 UI 功能之前为什么要画流程图
题目
做一个登录注册选服类的界面系统,需求阶段为什么要先画基本流程图,而不是直接进编辑器摆控件?
深入解析
- 流程图先把状态与跳转固定下来:哪些界面互斥、哪些可以返回上一级、异常或网络失败时落在哪一屏,这些是「逻辑骨架」。
- 没有流程图时,常见问题是做到一半发现漏了分支,或同一业务在多个脚本里各写一套跳转,后期联调成本高。
- 流程图不要求细到每个按钮,但要覆盖主路径和关键分支(例如登录失败、断线重连入口),与策划或自己的需求表对齐即可。
答题示例
先把界面跳转和状态主干画清楚,避免做到一半才发现漏分支或多人各写一套跳转。
流程图对齐主路径和关键异常路径,再进编辑器摆控件、写逻辑,返工少。
参考文章
- 1.需求分析
2. NGUI 里按钮点击一般怎么接
题目
NGUI 的 UIButton 要响应点击,Inspector 里通常还要配什么?EventDelegate 大致起什么作用?
深入解析
- 需要 Collider(或与 NGUI 兼容的碰撞体)才能接收射线;
UIButton负责按下态与回调列表。 onClick.Add(new EventDelegate(() => { ... }))把委托塞进按钮的回调列表,和 UnityEvent 类似,但是 NGUI 自己的一套序列化委托封装。- 多个回调可以链式添加,适合拆 UI 初始化里统一注册。
答题示例
按钮对象要有 Collider,UIButton 上通过 onClick 添加 EventDelegate。
EventDelegate 把匿名方法或目标方法挂进按钮回调列表,点击时按顺序执行。
参考文章
- 3.提示面板
- 4.登录面板
3. UIRoot 和主摄像机为什么要分开
题目
教程里强调 NGUI 相机只渲 UI、主相机不渲 UI,不这样做会有什么问题?
深入解析
- 深度与排序:UI 通常用正交相机、固定 Near/Far,和场景透视相机混在同一相机上容易深度打架、UI 被场景遮挡或反过来。
- 分辨率与适配:UIRoot 负责缩放与适配策略,独立相机便于只对该层做像素对齐。
- 性能与层:UI 可单独 Culling Mask,减少无关物体进管线;事件系统也更好只对 UI 相机射线。
答题示例
UI 用独立正交相机和 UIRoot 做适配,主相机专注场景。
混在同一相机上容易深度错乱、适配难拆,也不利于单独调层和性能隔离。
参考文章
- 2.准备工作
进阶题
1. 类图在 UI 项目里具体约束什么
题目
UML 类图对 NGUI 这类多面板项目有什么实际帮助?类图里你最关注哪些关系?
深入解析
- 依赖方向:谁引用谁、谁通知谁,尽量单向、避免循环依赖,否则改一个面板牵一片。
- 职责划分:面板脚本、数据层、网络回调各放哪,类图里用组合或关联标出来,和「一个脚本干完所有事」对比,后者难测也难复用。
- 和 Prefab 的关系:类图是逻辑层的图,不必和 Hierarchy 一一对应,但要能对上「哪个 MonoBehaviour 负责哪块交互」,避免同名不同职责的类混用。
答题示例
类图主要约束依赖方向和职责:谁持有谁、事件或数据怎么流,减少循环引用和上帝类。
不必和场景节点一一对应,但要能对上哪个脚本负责哪块交互,Prefab 只是视图载体。
参考文章
- 1.需求分析
2. BasePanel 泛型单例为什么要约束 T 为 class,Awake 里 as T 要注意什么
题目
BasePanel<T> where T : class 里用 instance = this as T 注册单例,可能踩什么坑?
深入解析
- 约束为 class:值类型不能作为
T与as T的目标,避免无意义的泛型组合。 - as T 失败:若子类声明与挂载的组件类型不一致(例如 prefab 上错脚本),
as得null,之后Instance为空,表现为点击无响应或 NRE。 - 多实例:场景里误放两个同类型面板时,后
Awake的会覆盖instance,调试困难;实践上要保证同类型面板场景内唯一或改注册策略。
答题示例
T 约束为引用类型,as T 在类型不匹配时得到 null,单例就废了。
要保证 Prefab 上挂的是对应子类脚本,且同类型面板不要重复注册两份单例。
参考文章
- 3.提示面板
3. 记住密码和自动登录为什么要互相联动
题目
登录面板里「不记住密码则强制取消自动登录」「自动登录则强制勾选记住密码」,业务上为什么合理?
深入解析
- 自动登录依赖本地存有凭据;若不记住密码却仍自动登录,语义矛盾,也容易留下安全与合规争议。
- 用
UIToggle.onChange双向约束,避免玩家点到非法组合状态,减少后续保存LoginData时的脏数据。
答题示例
自动登录必须以持久化账号密码为前提,否则逻辑不自洽。
在 onChange 里互锁两个 Toggle,比登录时再判断组合更省事、更不容易存脏状态。
参考文章
- 4.登录面板
4. 动态文字为什么容易拉高 DrawCall,文中怎么缓解
题目
拼登录注册面板时,为什么「文字穿插在图之间」会增加 DrawCall?教程里怎么改?
深入解析
- NGUI 合批依赖材质、纹理、Shader 与 绘制顺序;Label 与 Sprite 深度交错会打断同一批次的连续区间。
- 把 文字 Widget 的 Depth 整体调高,让同一界面里图先画、字后画(或保持连续块),减少批次切换次数。
- 根治还要结合图集规划、动态字与静态图是否同图集等,但调 Depth 是成本最低的排查手段。
答题示例
文字和图深度穿插会打断合批,DrawCall 上去。
把 Label 深度统一提高,让绘制顺序更连续,能减少批次。
参考文章
- 4.登录面板
- 5.注册面板
深度题
1. 需求变更时怎么维护流程图与类图
题目
做到一半策划改了登录流程或新增一个中间页,你会怎么更新流程图和类图,并保证代码结构跟得上?
深入解析
- 流程图:先改主干与新增节点的入边、出边,标出旧路径废弃或兼容策略,再让实现跟着改跳转条件;避免只改代码不更新图,否则下一波需求又失真。
- 类图:新增界面通常对应新 View 或新 Panel 脚本,看是否应复用现有控制器还是抽公共基类;若出现横切逻辑(如全局 Toast),考虑抽到独立模块而不是每个面板复制。
- 工程习惯:小步提交、图与关键跳转注释或文档同步,比一次性大改更易 Code Review。
答题示例
先更新流程图的节点与边,再动代码跳转;类图同步新面板职责和依赖,避免只改 Prefab 不改逻辑边界。
横切能力抽公共模块,小步改、图与代码一起评审,减少图实不一致。
参考文章
- 1.需求分析
2. 为什么选中服务器后不一定立刻 SaveLoginData
题目
选服流程里,点击右侧服会把 frontServerID 写进内存里的 LoginData,但教程强调进游戏按钮里再 SaveLoginData,这样设计有什么好处或取舍?
深入解析
- 避免无效写盘:玩家可能反复点开选服、换区,若每次点都写 XML,IO 频繁且易产生中间态存档。
- 与「确认进入」对齐:只有点「进入游戏」才持久化,表示玩家确认本次选择;若希望每次选服都落盘,可改为右侧点击后即 Save,但要接受频繁 IO 与崩溃时半成品状态。
- 与自动登录配合:内存中先更新 ID,服务器面板
ShowMe已能显示;持久化延迟到进游戏,和教程中的测试节奏一致。
答题示例
点服只改内存,确认进游戏再 Save,减少反复选服写盘和无效 IO。
若产品要求断电不丢选服,可改为选服后即存,属于产品向取舍。
参考文章
- 7.选服面板
3. 多面板同时 Start 时为什么会出现「谁都不显示」
题目
自动登录里先打开了服务器面板,但服务器面板 Init 末尾又 HideMe,可能和什么机制冲突?怎么解决?
深入解析
- Unity 按脚本 执行顺序 调用各
Start/Awake;后执行的面板若在Init里默认隐藏自己,会覆盖先前面板的显示结果。 - 通过 Script Execution Order 让登录、服务器、选服等脚本的初始化顺序符合业务(例如先完成「谁该显示」的决策),或使用显式状态机统一驱动,而不是各面板各自在
Start里抢显隐。
答题示例
各面板 Start 顺序不确定,后执行的 Hide 会盖掉先执行的 Show。
在 Project Settings 里调 Script Execution Order,或抽一层流程控制显隐,避免分散在多个 Start 里打架。
参考文章
- 7.选服面板
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com