ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Unity 代码质量守门人:UnityStarter 24 条 Roslyn 分析器规则完整清单

Unity 代码质量守门人:UnityStarter 24 条 Roslyn 分析器规则完整清单

UnityStarter 内置一套名为 CycloneGames.Analyzers 的 Roslyn 分析器(Roslyn Analyzer)模块,相当于 Unity 项目的"代码质量守门人":它在编译期自动扫描代码,把 GC 分配、GameObject.Find、async void 这类隐患拦在提交之前。本文整理出全部 24 条已实现规则的完整清单,帮你快速掌握这套 Unity 代码质量工具的规则体系。

分析器是什么:编译期就把隐患拦住

传统做法是等运行时卡帧、出 Bug 了再回头排查;而 UnityStarter 的 Roslyn 分析器把检查前移到编译期——只要代码不符合团队约定,Unity 编辑器打开时错误窗口(Error Window)就会给出提示,甚至直接报编译错误。

模块位于 UnityStarter/Analyzers/CycloneGames.Analyzers/,规则定义集中在:

  • 规则 ID 声明:DiagnosticIds.cs
  • 规则描述与默认严重级别:DiagnosticRules.cs
  • 五大规则分类:DiagnosticCategories.cs
  • 中文说明文档:README.SCH.md

项目还自带一套真实编译验证工具(CycloneGames.Analyzers.Verifier/),会在隔离的临时 Unity 工程中实际编译一段违规代码,确认分析器真的能触发诊断,而不是"纸面规则"。

24 条规则完整清单:按 5 大分类速查

所有规则 ID 统一以CG开头。DiagnosticIds共声明 28 个 ID,其中24 条已实现、4 条为预留(详见文末)。

1️⃣ 性能类(CycloneGames.Performance):守护热路径

ID规则触发场景默认级别
CG0001热路径中的foreachIL2CPP 下枚举器可能产生分配,建议改for索引循环Warning
CG0002热路径中的 LINQLINQ 会产生临时可枚举对象/委托Warning
CG0003热路径中的字符串构造插值、string.Format、拼接都会分配Warning
CG0004热路径中的Camera.main本质是隐藏的FindGameObjectWithTag场景搜索Warning
CG0041链式component.transform访问每次访问都是 native 互调,应缓存 TransformWarning
CG0043热路径中的Debug.Log每帧打印既分配又拖慢Warning
CG0044热路径中的GetComponent<T>昂贵的 native 互调,应缓存结果Warning
CG0045热路径中的装箱转换值类型赋给 object/接口会触发装箱Warning
CG0046热路径中的 lambda / 匿名方法可能分配委托Warning

2️⃣ 安全类(CycloneGames.Safety):禁止危险 API

ID规则为什么危险默认级别
CG0010生产代码中的GameObject.Find脆弱且慢,应使用序列化引用、DI 或注册表Error
CG0011场景级FindObjectOfType系列 APIO(n) 全场景搜索Error
CG0012SendMessage/BroadcastMessage基于字符串、无编译期安全Error
CG0013MonoBehaviour.Invoke系列 API字符串方法查找,建议改UniTask.Delay或显式计时器Error
CG0014Resources.Load绕过项目资产管理管线(资产管线 Provider 实现可语义豁免)Warning

3️⃣ 规范类(CycloneGames.Convention):保持代码风格

ID规则说明默认级别
CG0030MonoBehaviour上的 public 实例字段建议[SerializeField] private+ 属性;嵌套 struct/普通 class 不误报Warning
CG0031Runtime 代码中的using static降低可读性,要求显式限定类型Warning
CG0032Runtime 代码中的#region常掩盖糟糕的组织方式,建议拆小类型Info(默认关闭)
CG0033框架代码中的[Obsolete]框架 API 直接删除并更新调用方,不留废弃接口Warning

4️⃣ API 契约类(CycloneGames.ApiContract):统一日志契约

ID规则说明默认级别
CG0049绕过统一日志 API受治理的程序集禁止直接调用Debug.Log*、print、Console.Write*等,必须走日志门面Error
CG0050日志门面之外调用LogChannel.Create每个程序集只允许一个可发现的日志门面边界Error

这两条规则守护的是 UnityStarter 的统一日志系统——所有模块的日志都收敛到各自的 assembly log facade(例如CycloneGames.Audio.Runtime的Diagnostics/AudioRuntimeLog.cs),由门面统一投递到 Unity Console、文件等 sink。效果上,Console 里每条日志都带分类标签且可点击跳转到源码行:

官方说明:两条日志规则刻意不提供 CodeFix——因为安全替换涉及模块分类、异常上下文、延迟格式化等决策,单个调用点无法推断(见 README.SCH.md 第 121 行附近的说明)。

5️⃣ Unity 最佳实践类(CycloneGames.UnityBestPractices)

ID规则说明默认级别
CG0040Runtime 代码中的async void异常无法捕获,建议async UniTaskError
CG0042Editor 目录外使用UnityEditor命名空间应移入 Editor 代码或包UNITY_EDITORWarning
CG0047引用 UniTask 时使用async TaskTask是 class 必分配,UniTask是 struct 可池化Warning
CG0048静态类循环依赖风险通过调用链成环检测,建议拆出非静态帮助类Warning

分阶段强制策略:从 Warning 到 Error 的平滑迁移

规则不是"一刀切"。项目通过 Assets 根目录的 Default.ruleset(Unity 会自动应用到全工程)管理强制策略:

规则策略
CG0010保持Error,生产代码零容忍
CG0011 / CG0013暂降为Warning——存量调用点迁移完成前,避免"启用分析器 = 编译中断"

这份策略文件由契约测试(UnityActivationArtifactTests.cs)守护:只有当 owning 模块还清迁移债,才允许把 Warning 提升为 Error。此外,CG0010/CG0011对Editor/、Tests/、Samples/目录下的代码保持沉默——编辑器工具和测试里做场景扫描是合法行为。

附赠能力:2 个 CodeFix 一键修复

并非所有规则都只有"报错",以下两条支持一键修复(源码见 CodeFixes/):

诊断修复动作
CG0001集合具备Count/Length和 int 索引器时,自动把foreach转换为for索引循环(ForEachToForCodeFix.cs)
CG0004自动新增缓存的Camera字段并在Awake中赋值,替换热路径里的Camera.main(CameraMainCachingCodeFix.cs)

热路径怎么算?别误伤

热路径规则只针对每帧执行的方法,默认命中以下方法名:

Update, LateUpdate, FixedUpdate, OnGUI, Tick, OnTick, OnUpdate, PreUpdate, PostUpdate, OnPreTick, OnPostTick

设计原则是宁可漏报、不可误报——大范围误报只会逼着团队去禁用分析器,那才是对代码质量最大的伤害。

如何在项目中启用与调整规则

  1. 确认激活:项目已通过 Assets/Analyzers/CycloneGames.Analyzers.dll 及其.meta(带RoslynAnalyzer标签)激活,模板开箱即用。
  2. 调整 Unity 编译严重级别:修改 Default.ruleset,例如<Rule Id="CG0011" Action="Warning" />。
  3. 兼容 IDE / 独立 .NET 宿主:用.editorconfig,例如dotnet_diagnostic.CG0001.severity = error。
  4. 局部抑制要克制:仅当分配或 API 成本是有意行为并记录原因时,才使用#pragma warning disable CG00xx包裹。

一条新规则要"默认启用"还需过质量门槛:语义级检查(而非脆弱的字符串匹配)、正例反例测试齐全、对现有 UnityStarter 模块零/低误报、改写安全才配 CodeFix。

预留规则:4 个已占位的 ID

以下 ID 已在 DiagnosticIds.cs 中声明但尚未实现,属于规划中的规则:

  • CG0015:NativeContainerLeak(原生容器泄漏)
  • CG0022:ActorStartBaseCall(重写Actor.Start必须调用base.Start(),否则BeginPlay()不触发)
  • CG0023:PoolOnDespawnOverride(FastObjectPool子类必须重写OnDespawn,防止池化对象状态泄漏)
  • CG0024:GameplayTagImplicitCast(GameplayTag 的字符串隐式转换应先校验)

这 4 条在 DiagnosticRules.cs 中已写好诊断描述,属于"规则先行、实现待补"的预留位,也说明这套规则清单还在持续演进。

总结

UnityStarter 的 Roslyn 分析器把"零 GC 热路径、安全 API、统一日志、框架规范"四大主题拆成 24 条可执行规则,用编译期强制替代事后救火,配合分阶段升级策略避免存量代码被"误伤"。对新手来说,读懂这份清单,也就读懂了这个框架对代码质量的期待。

📎 延伸资料:

  • 规则总览(英文):README.md
  • 分析器测试用例:CycloneGames.Analyzers.Tests/
  • 统一日志包说明:CycloneGames.Logging.Unity/README.SCH.md

【免费下载链接】UnityStarter

Production-ready modular Unity framework: UE-inspired Gameplay Ability System, zero-GC core, HybridCLR hot-update, Luban config pipeline,>项目地址:https://gitcode.com/gh_mirrors/un/UnityStarter

点击查看免费下载

相关推荐

上一篇:告别数据混乱:Univer自定义数据验证规则完全指南
下一篇:虚拟机性能突破指南:WinApps基于KVM的终极优化方案

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进