跳到正文
cccskills
免费在 GitHub 公开

unity-performance-and-gc

Use when touching Unity per-frame code, spawning, physics queries, UI updates or rendering setup - profiling with numbers, zero-GC hot paths (caching, NonAlloc queries, no LINQ/closures/boxing, TMP SetText), ObjectPool, allocation tests, Burst/Jobs for bulk math, and rendering/physics settings that cost frames.

中文简介正在准备中。当前显示原文说明。

集成工具Unity
查看安装方法

包含的文件(1)

  • SKILL.md7.0 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

Unity Performance and GC

Overview

Unity's managed heap is garbage-collected; every allocation in Update is a future GC spike, and the incremental collector only spreads the pain. Most frame drops in Unity projects come from a short list: allocations per frame, per-frame lookups, unpooled spawning, physics queries that allocate, UI rebuilds, and draw calls. Find which one with the Profiler, fix it, and prove the fix with numbers (game-performance-budget).

Core principle: Zero managed allocations per frame in gameplay, measured — not assumed — with the Profiler's GC Alloc column or an allocation test.

Profile first

  • Profiler: CPU Usage (Hierarchy, sort by Time ms and GC Alloc), Rendering, Physics, Memory modules. Profile a Development Build with Autoconnect; the Editor adds overhead.
  • Deep Profile only to locate a call, never for timing.
  • Profile Analyzer compares a before and an after capture; Memory Profiler finds leaks across scene loads.
  • Mark your own code: static readonly ProfilerMarker s_Marker = new("Enemy.Think"); using (s_Marker.Auto()) { ... }.

The allocation list

// ❌ allocates or searches every frame
void Update() {
    var enemies = FindObjectsByType<Enemy>(FindObjectsSortMode.None);       // array + scene search
    var close = enemies.Where(e => (e.transform.position - transform.position).sqrMagnitude < r2).ToList(); // LINQ + closure
    hpText.text = "HP: " + hp;                                              // new string every frame
    if (other.tag == "Enemy") { }                                           // allocates the tag string
    var hits = Physics.OverlapSphere(transform.position, 5f);              // new array per call
    StartCoroutine(Flash(new WaitForSeconds(0.1f)));                        // allocation per call
}

// ✅ cached, reused, non-allocating
readonly Collider[] _hits = new Collider[32];
static readonly WaitForSeconds FlashDelay = new(0.1f);
int _shownHp = -1;

void Update() {
    int count = Physics.OverlapSphereNonAlloc(transform.position, 5f, _hits, enemyMask);
    for (int i = 0; i < count; i++) { /* use _hits[i] */ }
    if (hp != _shownHp) { _shownHp = hp; hpText.SetText("HP: {0}", hp); }   // TextMeshPro, no string alloc
    if (other.CompareTag("Enemy")) { }
}

More of the same:

  • 3D queries: RaycastNonAlloc, SphereCastNonAlloc, OverlapBoxNonAlloc. 2D: the overloads taking a ContactFilter2D and a reused List<T>/array (the 2D *NonAlloc methods are obsolete).
  • GetComponentsInChildren(list) overloads that fill a reused list.
  • Animator.StringToHash, Shader.PropertyToID, cached WaitForSeconds/WaitForFixedUpdate.
  • foreach over List<T> and arrays is fine; over an IEnumerable<T> interface it boxes the enumerator.
  • No capturing lambdas, string.Format/interpolation, params arrays or boxing (struct → object/interface) in hot paths.
  • Debug.Log in a hot path allocates and stalls — strip it from builds ([Conditional("UNITY_EDITOR")] wrapper) or remove it.
  • Camera.main is cheaper than it was but is still a lookup — cache it.

Pool what you spawn

using UnityEngine.Pool;

public sealed class BulletSpawner : MonoBehaviour {
    [SerializeField] Bullet prefab;
    ObjectPool<Bullet> _pool;

    void Awake() => _pool = new ObjectPool<Bullet>(
        createFunc: () => { var b = Instantiate(prefab); b.Init(release: x => _pool.Release(x)); return b; },
        actionOnGet: b => b.gameObject.SetActive(true),
        actionOnRelease: b => b.gameObject.SetActive(false),
        actionOnDestroy: b => Destroy(b.gameObject),
        collectionCheck: false, defaultCapacity: 64, maxSize: 256);

    public void Fire(Vector3 pos, Vector3 dir) { var b = _pool.Get(); b.Launch(pos, dir); }
}

Pre-warm at load (get and release defaultCapacity instances) so the first fight does not instantiate. A pooled object resets all its state in actionOnGet/Launch, not in Start.

Prove zero allocations

using Is = UnityEngine.TestTools.Constraints.Is;

[Test] public void Tick_SteadyState_DoesNotAllocate() {
    var sim = TestSim.WithEnemies(100);
    sim.Tick(0.016f);                                    // warm-up: first call may JIT/initialise
    Assert.That(() => sim.Tick(0.016f), Is.Not.AllocatingGCMemory());
}

Burst and Jobs for bulk work

For the same math over hundreds or thousands of items (flocking, projectiles, terrain), a Burst-compiled job on native containers:

[BurstCompile]
struct MoveJob : IJobParallelFor {
    public NativeArray<float3> Positions;
    [ReadOnly] public NativeArray<float3> Velocities;
    public float Dt;
    public void Execute(int i) => Positions[i] += Velocities[i] * Dt;
}
// schedule: var h = new MoveJob { ... }.Schedule(count, 64); later h.Complete();
  • Native containers are disposed (Allocator.Persistent in OnDestroy; TempJob within four frames; Temp within the frame).
  • Complete handles before reading results; schedule early, complete late.
  • At entity scale, consider DOTS (unity-dots-ecs) — only if the project already uses it or the task decides it.

Rendering and physics settings that cost frames

  • URP/HDRP: keep the SRP Batcher compatible (shaders with CBUFFERs; avoid per-renderer MaterialPropertyBlock where it breaks batching); GPU instancing for repeated meshes; static batching for static scenery; Sprite Atlases for 2D.
  • UI: split static and frequently changing elements into separate Canvases; turn off Raycast Target on non-interactive graphics; never animate layout groups every frame.
  • Overdraw: transparent particles and full-screen effects, especially on mobile; LODs and occlusion culling for 3D.
  • Physics: the layer collision matrix trims pair checks; primitive colliders over mesh colliders on moving bodies; Physics.autoSyncTransforms off; tune Time.fixedDeltaTime deliberately.
  • Mobile: set Application.targetFrameRate explicitly; watch thermal throttling over minutes, not seconds.

Common Mistakes

  • Optimising from intuition without a Profiler capture.
  • Instantiate/Destroy for bullets, hit effects and damage numbers.
  • Updating TextMeshPro text every frame with string concatenation.
  • Allocation test without a warm-up call, failing on first-call initialisation.
  • Leaking NativeArrays (the leak detection warning in the console is a real bug).

Red Flags

  • GC.Alloc above zero in a gameplay Update in the Profiler.
  • LINQ, FindObjectsByType, GetComponent or new inside Update/FixedUpdate.
  • A performance claim in the closing message without before/after numbers.

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

Use when writing acceptance criteria for a task - express each as an observable Given/When/Then that QA can execute, including negative cases

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月18日 更新

Use when the diff adds or changes an endpoint, resolver, RPC, job or query that takes an object id, a role check, a request binding or a tenant filter - BOLA/IDOR, function-level authorization, mass assignment and tenant scoping

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月18日 更新

Use on every UI change - semantic HTML, labels for controls, keyboard-navigable dialogs/menus, visible focus, and never color as the only signal

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月18日 更新

Use when a task changes any screen, form, dialog, menu or control - Lighthouse/axe scan of the changed screens, a keyboard walk, and the thresholds that fail a task

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月18日 更新

How to work a task returned with review, QA or UAT findings. Use when a task is in need_revision or PR review comments are in your context.

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月18日 更新

Use when the repository keeps its alerting in code, or a person asks for alerts outside TaskTrooper - PrometheusRule YAML, a Cloud Monitoring alert policy in Terraform, a Datadog monitor, each routed to TaskTrooper's alert ingest URL so it opens an incident here

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月18日 更新

查看 makifbaysal 的全部技能

报告此技能的问题