Unity 编辑器扩展、工程协作与项目优化实践
AI 摘要
汇集Unity编辑器扩展语法、Git协作规范(CRLF处理)、静态内存管理及逻辑表现分离架构原则。详细剖析了预制体引用失效、Visual Studio配置、异步JSON加载优化、UI虚拟列表缓存及红点系统实现,旨在通过确定性时间步长与资源预加载策略提升项目稳定性与帧率性能。
除了写玩法逻辑,工程里还有大量「编辑器工具、协作规范、架构与优化」的经验值得留档。这篇是这些内容的合集。
一、编辑器扩展:给菜单项加快捷键
MenuItem 支持在路径后面追加快捷键语法:
using UnityEngine;
using UnityEditor;
public class QuickSelectionInfo
{
// % = Ctrl(Windows) / Command(Mac);&e = Alt+E
[MenuItem("Tools/Quick Selection Info %e")]
static void QuickPrintSelectionInfo()
{
if (Selection.activeGameObject == null)
{
Debug.Log("没有选中任何对象");
return;
}
GameObject selected = Selection.activeGameObject;
Debug.Log("选中: " + selected.name);
Debug.Log("位置: " + selected.transform.position);
Debug.Log("组件: " + selected.GetComponents<Component>().Length + " 个");
}
}
快捷键修饰符:% = Ctrl / Cmd,# = Shift,& = Alt,_ = 无修饰键。上例中 %e 即 Ctrl+E。
二、协作与工具链的坑
1. .meta 文件导致的预制体引用错误
现象:预制体里脚本已经绑定好所有信息,但运行后出现丢引用。
排查结论:.meta 文件记录的是之前缺少引用的旧信息,没有及时更新到最新预制体信息,导致运行后引用出错。根因是换行符 LF / CRLF 不一致。
解法:
- 添加
.gitattributes做过滤; - Git 禁用
auto crlf(core.autocrlf=false)。
2. Visual Studio 突然没有提示和跳转
排查顺序:
- 先看项目有没有被卸载;
- 如果没卸载,去 Unity 的 Preferences → External Tools,把外部工具重新设置为 VS;
- 重新生成工程(Regenerate project files)。
一般这两步就能解决。
3. Visual Studio 的 Git 分支显示
VS 的 Git 界面里,「本地分支」与「远端分支 / merged」这类展示容易让人误解,它指的是本地分支与远端跟踪分支的关系,而不是字面理解的「Head 是本地、merged 是远端」。判断分支状态以命令行的 git status / git branch -vv 为准。
三、静态池的生命周期
静态字段(如 Timer._pool)的生命周期与应用程序域(AppDomain)相同,不会随着某个管理器(如 TimerManager)的销毁而自动清理。
所以静态池要么提供显式的清理入口,要么在设计上接受「跨管理器存活」—— 否则容易留下「看起来已销毁、实际还占着内存」的隐患。
四、架构原则:逻辑与表现分离
项目的核心架构原则之一是逻辑表现分离:
- 英雄、怪物、特效都做表现分离;
- 技能和天赋大部分只有逻辑处理,主动技能和部分被动技能才带 UI 处理;
- 时间驱动都利用时间步长进行 tick,入口在 BattleManager 的 Update。
这样逻辑层可确定性推进、表现层可独立替换,也便于性能分析(比如关注 1% low FPS)。
五、一批项目优化实践
数据与加载
- 线程池异步加载大量 JSON 文件,优化界面打开时间。
- 大量不涉及 Unity API 的数据加载放到其它线程(Task),能明显降低卡顿。
- 弹珠台抽奖采用「轨迹回放 + 自动录制」:录制阶段 → 路径库存储 → 主界面预热 → 动画界面纯播放,即 Precompute(预计算)+ Preload(预加载)+ Playback(轻量播放)。
- 虚拟列表里不要大量做字符分割(会产生大量 GC):可以把列表项对应的分割数据缓存在界面中。比如 n 项奖励配置、每项奖励需要字符串分割读取,就在列表渲染前把全部奖励数据处理完。
UI 与交互
- 多子界面用「页签按钮组件 + 页签页组件」管理,没有点开的界面不需要加载。
- 界面跳转设计:维护一份跳转表(以 id 为键,记录界面名字,以及跳转的一级界面和二级界面的值),通用界面基类传入子界面索引。
- 功能解锁与 icon 显示的通用逻辑。
- 红点系统:红点逻辑类 + UI 组件类。支持红点逻辑绑定刷新 UI 组件(用于显示),UI 组件也可以通过对应的红点计算方法重新计算数量,再刷新显示。
- 粒子透明度渐隐渐显利用 startColor。
时间与状态
- 定时器采用固定步长计算,以真实时间为准。
- 本地时间管理。
- 本地计时管理:体力恢复、部分商品刷新、icon 显示(分「带时间倒计时的」和「常驻的」)。
- 本地每日重置管理器:到重置点后派发重置事件,需要重置的界面注册该事件,然后定点请求相应数据 —— 达到「界面没关也能刷新数据」的效果。
网络与数据
- 网络业务层设计:程序集扫描 + 特性绑定接收消息的函数。每个接收函数都有特性(内含对应的协议号枚举),程序运行时会扫描程序集自动注册这些函数委托。
表现与资源
- Spine 换装和换武器:多个 Spine 按路线进场,同时按路线入场。
内存与 GC
- 解决节点脚本因一直未激活、不执行
OnDestroy导致的「内存泄漏」。 - 用
StringBuilder减少字符串 GC(如时间格式转换方法)。 .meta问题解决:Git 设置auto crlf=false,添加.gitattributes过滤。
编码习惯
- 简单的状态标记使用位标记方法。
小结
- 编辑器工具用
MenuItem快捷键语法,%/#/&/_对应不同修饰键。 - 协作问题里,
.meta+ CRLF 是预制体丢引用的经典元凶,用.gitattributes+ 关掉auto crlf解决。 - 架构上坚持逻辑表现分离、时间步长 tick 统一入口。
- 优化围绕「少 GC、少阻塞、少重复计算」展开:异步加载、轨迹预计算、列表数据预处理、静态池显式管理。
这是一条笔记,不是成稿文章。它可能不完整、可能有错,也可能过段时间就被我推翻。 想看成体系的文章,去 Blog。
linked from2 处引用了这篇