mcpField
U
Unity

Unity

github.com/juliankerignard开发855 次安装32 周前更新
代码仓库

关于 Unity

Unity 是面向 Unity 游戏开发者的开发指南,涵盖从项目搭建到发布。它将 C# 实践、架构选择和性能建议与专题参考文件结合起来。

主要功能

  1. 01项目结构与资源规范
  2. 02C# 和 MonoBehaviour 模式
  3. 03架构与设计模式
  4. 04性能分析与优化建议
  5. 05工作流、测试与构建实践
  6. 06Unity 6 与专项系统

适用场景

  1. 01整理不断扩大的 Unity 项目
  2. 02编写或审查游戏脚本
  3. 03排查性能问题
  4. 04选择游戏架构与工作流

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/juliankerignard/unity-skills --skill unity

安装后在 Claude Code 中输入/unity确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
architecture-advanced.md
architecture.md
workflow.md
performance.md
csharp-patterns.md
systems.md
specialized.md
project-structure.md

详细介绍

Unity 开发指南

经过实战验证的 Unity 最佳实践,涵盖从项目设置到发布的各个环节。这里是入门指南;如需深入了解,请参阅相应的参考文件。

参考文件 — 何时阅读哪些内容

你正在进行的工作……阅读此文件
整理文件夹、命名资源、设置导入选项references/project-structure.md
编写或审查 C# 脚本references/csharp-patterns.md
排查性能问题或进行优化references/performance.md
设计游戏架构、模式和系统references/architecture.md
设置 Git、测试、CI/CD 或构建references/workflow.md
动画、2D、Shader/VFX Graphreferences/specialized.md
调试、本地化、无障碍、AI/Inference Enginereferences/systems.md
设置 UI Toolkit 或 Cinemachinereferences/architecture-advanced.md

编写代码前,请阅读相关参考文件;其中的模式可以帮你避免大量返工。


1. 项目结构

与按类型划分的文件夹相比,按功能划分的文件夹更易于扩展,因为随着项目增长,相关代码仍能放在一起:

Assets/
├── _Project/
│   ├── Scripts/
│   │   ├── Core/          # Game loop, managers, bootstrap
│   │   ├── Player/        # Controller, input, camera
│   │   ├── Enemies/       # AI, spawners, enemy types
│   │   ├── UI/            # Views, presenters, screens
│   │   ├── Data/          # ScriptableObject definitions
│   │   └── Utils/         # Extensions, helpers
│   ├── Prefabs/
│   ├── Scenes/
│   ├── ScriptableObjects/ # Data asset instances
│   ├── Art/               # Models, Textures, Materials, Animations, Shaders
│   └── Audio/             # SFX, Music
├── Plugins/               # Third-party SDKs
└── Settings/              # Render pipeline, input, quality

为什么要用 _Project/?下划线前缀会将它固定在 Assets 文件夹顶部,让你的代码与导入的包和第三方资源分开。

为什么不用 Resources/?Unity 会在启动时将 Resources/ 中的所有内容加载到内存中。对于少量小型资源以外的内容,更推荐使用 Addressables 系统;它可以按需加载,并支持远程内容分发。

有关文件夹组织、资源命名约定、导入设置、预制件/场景约定,以及如何从游戏创作活动扩展到大型项目的深入说明,请参阅 references/project-structure.md。

程序集定义

将代码拆分到程序集(.asmdef)中,是提升迭代速度最有效的方法之一。没有程序集定义时,任何脚本发生更改都会触发全部内容重新编译。使用程序集定义后,Unity 只会重新编译受影响的程序集:

Scripts/Core/    → Game.Core.asmdef
Scripts/Player/  → Game.Player.asmdef  (refs: Game.Core)
Scripts/UI/      → Game.UI.asmdef      (refs: Game.Core)
Scripts/Utils/   → Game.Utils.asmdef   (no dependencies)
命名约定
元素约定示例
类 / 结构体PascalCasePlayerController
公共方法PascalCaseTakeDamage()
私有字段_camelCase_currentHealth
序列化字段[SerializeField] + _camelCase[SerializeField] private float _speed;
接口I 前缀IDamageable
ScriptableObjectSO 后缀WeaponDataSO
枚举PascalCase,使用单数形式WeaponType { Sword, Bow }

2. 核心 C# 模式

这些模式几乎会出现在每个 Unity 项目中。如需查看完整目录(生命周期顺序、异步模式、扩展方法、编辑器脚本),请参阅 references/csharp-patterns.md。

整洁的 MonoBehaviour

结构良好的 MonoBehaviour 会遵循统一的布局:顶部是序列化配置,接着是事件、私有状态,最后是生命周期方法。这样可以一眼看清类的结构。

public class PlayerHealth : MonoBehaviour, IDamageable
{
    [Header("Config")]
    [SerializeField] private int _maxHealth = 100;
    [SerializeField] private float _invincibilityTime = 0.5f;

    [Header("Refs")]
    [SerializeField] private HealthBarUI _healthBar;

    public event Action<int, int> OnHealthChanged;  // current, max
    public event Action OnDeath;

    private int _currentHealth;
    private bool _isInvincible;

    private void Awake() => _currentHealth = _maxHealth;

    private void Start() => _healthBar?.SetMaxHealth(_maxHealth);

    public void TakeDamage(int damage)
    {
        if (_isInvincible || _currentHealth <= 0) return;
        _currentHealth = Mathf.Max(0, _currentHealth - damage);
        OnHealthChanged?.Invoke(_currentHealth, _maxHealth);
        if (_currentHealth <= 0) { OnDeath?.Invoke(); return; }
        StartCoroutine(InvincibilityCo());
    }

    private IEnumerator InvincibilityCo()
    {
        _isInvincible = true;
        yield return new WaitForSeconds(_invincibilityTime);
        _isInvincible = false;
    }
}
核心原则

优先使用 [SerializeField] private,而不是 public 字段。 公共字段会破坏封装——其他脚本可以随意写入,导致难以追踪的错误。[SerializeField] 能在 Inspector 中显示字段,同时让它对其他代码保持私有。

每个组件只负责一项职责。 将 PlayerMovement、PlayerHealth、PlayerCombat 分开,而不是使用一个庞大的 Player。这样你可以迭代单个系统,而不影响其他系统,也能让组件更易于复用。

在 Awake() 中缓存引用。 GetComponent、Find 和 FindObjectOfType 都是搜索操作。在运行时调用它们(尤其是在 Update 中)会浪费 CPU 周期。应在初始化时缓存一次。

优先使用 TryGetComponent<T>(),而不是 GetComponent<T>()——它会返回一个 bool,避免空引用异常,而且速度略快。

使用事件进行通信,而不是直接耦合。 当系统 A 需要告知系统 B 某件事已经发生时,使用 C# 事件或 ScriptableObject 事件通道。这样能让系统彼此独立且易于测试。

ScriptableObject 架构

ScriptableObject(SO)是存放在项目中、可跨场景持久存在的资源。它们是实现高内聚、低耦合 Unity 项目的基石。

数据容器——武器属性、敌人配置、关卡设置:

[CreateAssetMenu(menuName = "Game/Weapon Data")]
public class WeaponDataSO : ScriptableObject
{
    public string weaponName;
    [Range(1, 100)] public int damage = 10;
    [Range(0.1f, 5f)] public float attackSpeed = 1f;
    public AudioClip[] attackSounds;
}

事件通道——系统之间完全解耦的发布/订阅:

[CreateAssetMenu(menuName = "Events/Void Event")]
public class VoidEventChannelSO : ScriptableObject
{
    private Action _onRaised;
    public void Raise() => _onRaised?.Invoke();
    public void Subscribe(Action fn) => _onRaised += fn;
    public void Unsubscribe(Action fn) => _onRaised -= fn;
}

其妙处在于:发布者只需在 SO 资源上调用 Raise()。订阅者引用同一个资源并监听事件。双方都不知道对方的存在。你可以通过 Inspector 将它们连接起来。完整的模式集合(运行时集合、共享变量)请参阅 references/architecture.md。


3. 性能——基本要点

性能问题主要分为三类:CPU(每帧处理的逻辑过多)、GPU(渲染内容过多)和内存(分配导致 GC 暂停)。首要原则是先分析性能,再进行优化——在凭猜测之前,先打开 Window > Analysis > Profiler。

帧预算: 60fps 时为 16.6ms,30fps 时为 33.3ms。每超出预算 1ms,就会丢失一帧。

热路径中零分配

垃圾回收器(GC)运行时会暂停游戏。在 Update()、FixedUpdate() 或任何逐帧代码中进行分配,是导致这一问题的主要原因。

// Problematic — allocates every frame
void Update() {
    var enemies = FindObjectsOfType<Enemy>();      // array allocation
    string s = "HP: " + _health;                    // string concat
    var list = new List<int>();                      // list allocation
}

// Better — pre-allocate, reuse
private readonly StringBuilder _sb = new();
private readonly List<int> _reusable = new();

void Update() {
    _sb.Clear().Append("HP: ").Append(_health);
    _reusable.Clear();
    // ... fill _reusable as needed
}
对象池

频繁调用 Instantiate() / Destroy() 会导致 GC 峰值和内存碎片。应改用对象池。Unity 6+ 开箱即提供 UnityEngine.Pool.ObjectPool<T>,也可以使用 Queue<GameObject> 自行构建。

渲染速效优化
  • 对静止对象使用 Static Batching(在 Inspector 中勾选“Static”)
  • 对重复网格使用 GPU Instancing(在 Material 上启用)
  • 为复杂 3D 模型使用 LOD Groups
  • 尽可能使用烘焙光照——实时光照开销很大,尤其是在移动设备上
  • 在室内场景或复杂场景中使用 Occlusion Culling

完整的性能优化指南(物理、移动设备目标、纹理预算、性能分析工作流、Jobs/Burst)请参阅 references/performance.md。


4. 架构决策

根据项目规模选择
规模团队推荐方案
Game jam / 原型1-2单例 + 直接引用
小型独立游戏1-3ScriptableObject 架构
中型3-10SO 架构 + Service Locator
大型 / 长期运营服务10+DI 框架(VContainer)+ 按需使用 ECS

不要为项目规模不必要地过度设计。Game jam 不需要依赖注入,而一款运营 2 年的长期服务型游戏则需要。

关键模式摘要
模式使用场景
SO Event Channels跨场景的解耦通信
State Machine玩家状态、AI、游戏流程、UI 屏幕
Service Locator无需单例即可访问全局系统
Command输入处理、撤销/重做、回放
MVP(Model-View-Presenter)UI 系统
Object Pool频繁生成/销毁的对象

每种模式的完整实现请参阅 references/architecture.md。


5. 现代 Unity(Unity 6+)

Unity 6 是当前一代版本。最新的 LTS 版本是 Unity 6.3(2025 年 12 月),这是新项目推荐的基线版本。Unity 按季度更新(6.1 → 6.2 → 6.3 LTS → 6.4+)。

渲染管线选择

除非需要特定的 HDRP 功能(光线追踪、体积雾、高端主机级画面保真度),否则请从 URP 开始。URP 支持移动设备、VR、2D 和中端 PC。Unity 正在转向统一渲染器(共用 Render Graph 后端),以衔接 URP 和 HDRP——但目前,在项目中途切换渲染管线仍然很麻烦。请尽早决定。

Addressables

在生产环境中用 Addressables 替代 Resources.Load()。Addressables 可按需加载资源、支持异步加载,并支持远程内容分发。请牢记:用完后务必释放句柄,否则会造成内存泄漏。

新输入系统

如果项目面向多个平台或输入设备,请使用新的 Input System 包。它通过一个统一的抽象层(Input Actions 资源)处理键盘、游戏手柄、触控和 XR 输入。

Unity 6 中的 API 变更

为提高可读性,多个 API 更改了名称:

  • Rigidbody.velocity → Rigidbody.linearVelocity(Rigidbody2D 同样适用)
  • Rigidbody.angularVelocity 仍然保留,但请留意弃用警告
  • Cinemachine 2.x → Cinemachine 3.x(CinemachineVirtualCamera → CinemachineCamera,组件名称有显著变化——参见 architecture.md §10)
Unity 6.3 LTS 亮点
  • Box2D v3 底层 2D 物理 API(多线程、确定性、可视化调试)——可与现有 API 并行运行,最终将取而代之
  • Platform Toolkit——用于跨 PS5/Xbox/Switch/Steam/Android/iOS 管理账户、成就和存档数据的统一 API
  • Mesh LOD——在编辑器中为静态网格和蒙皮网格自动生成 LOD
  • Shader Graph——支持地形着色器、8 组纹理坐标,以及模板浏览器
  • Scriptable Audio Pipeline——通过 Burst 编译的 C# 处理器扩展音频链
  • UI Toolkit 新增自定义着色器、滤镜、SVG(现已成为核心模块)和增强的世界空间支持
  • 2D Renderer 现在可在 2D URP 中与精灵一同渲染 Mesh Renderer 和 Skinned Mesh Renderer
  • Sprite Atlas Analyser 工具,用于查找图集打包效率问题
DOTS / ECS

当你需要处理数千个相似实体时(大规模模拟、群体 AI、服务器端多人游戏),ECS 才能发挥优势。对于大多数活跃实体少于几百个的独立游戏和中型项目,传统的 MonoBehaviour 方式更简单,也足以满足需求。如果使用 DOTS,请保留 Unity.Mathematics 类型,使用 EntityCommandBuffer 进行结构性更改,并结合使用 ECS(逻辑)与 GameObjects(创作/渲染)。Unity 的路线图计划在未来版本中进一步统一 ECS。

Netcode for GameObjects

对于多人游戏:从第一天起就采用服务器权威架构。在服务器上验证所有输入,使用 NetworkVariable<T> 同步状态,并应用客户端预测以提升响应感。仅同步增量,尽量减少带宽使用。


6. 常见反模式

反模式不良影响更好的做法
臃肿的 GameManager 类难以维护,所有内容彼此耦合拆分为职责明确的系统
在 Update 中使用 Find*()每帧都进行线性搜索在 Awake 中缓存,使用事件
到处使用公共字段缺乏封装,难以追踪错误使用 [SerializeField] private
基于字符串的动画参数拼写错误会导致静默失败缓存 Animator.StringToHash() 的结果
深层继承结构僵化,难以重构组合 + 接口
硬编码魔法数字无法在不重新编译的情况下调整使用 ScriptableObjects 或 const

7. 快速决策参考

决策默认选择在以下情况下考虑替代方案……
渲染管线URP需要光线追踪、体积雾 → HDRP
资源加载Addressables小型项目,资源少于 20 个 → Resources
输入New Input System仅限编辑器的工具,无需游戏手柄 → 旧版 Input
异步Awaitable(Unity 6+)Unity 6 之前的版本 → UniTask 或 Coroutines
多人游戏Netcode for GameObjectsMMO/大规模项目 → 专用服务端 + ECS
UI(菜单、HUD)UI Toolkit世界内 UI 或旧版项目 → uGUI Canvas
摄像机Cinemachine非常简单的固定摄像机 → 手动脚本
物理内置 3D/2D Physics刚体超过 10K → DOTS Physics
粒子(少于 100 个)Shuriken(Particle System)粒子超过 1000 个或需要 GPU 特效 → VFX Graph
粒子(1000 个以上)VFX Graph不支持计算着色器的移动平台 → Shuriken
本地化Unity Localization package小型 Game Jam 项目 → 硬编码字符串
版本控制Git + LFS大型团队、二进制文件较多 → Perforce / Plastic
动画混合Blend Trees无需混合的离散状态 → 简单过渡
2D 物理内置 Physics2D需要高性能/确定性 → Box2D v3 低级 API(6.3+)
跨平台服务Platform Toolkit(6.3+)较旧的 Unity 版本 → 按平台手动使用 SDK

评论

支持 Markdown