8.NGUI实践项目总结

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 飙高

自测清单

  1. 长文案、换分辨率是否还能读。
  2. 打开 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:用 frontServerIDserverDic 更新 labInfo
  • 返回:LoginPanel.ShowMe + HideMe

选服面板要点

  • 左侧 ServerItem:每 5 个区一段,InitInfo(begin, end)
  • UpdatePanelDestroy 旧右侧项 → 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 串联 frontServerIDSaveLoginDataClearLoginData、自动登录与执行顺序

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:值类型不能作为 Tas T 的目标,避免无意义的泛型组合。
  • as T 失败:若子类声明与挂载的组件类型不一致(例如 prefab 上错脚本),asnull,之后 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

×

喜欢就点赞,疼爱就打赏