Skip to main content

unity

Expert game development with Unity - engine architecture, C# scripting, DOTS ECS, rendering (URP/HDRP), physics, animation, UI, asset pipeline, multiplayer, and performance.

Source facts

Repository
j4flmao/agent-skills
Last source activity
September 19, 2026 at 03:19
Detected SKILL.md language
English
Stars
27
Forks
1

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
unity
description
Expert game development with Unity - engine architecture, C# scripting, DOTS ECS, rendering (URP/HDRP), physics, animation, UI, asset pipeline, multiplayer, and performance.
# Unity Deep Engineering Guide Unity is a component-based game engine built around GameObjects, MonoBehaviours, and a C# scripting layer. Modern Unity adds Data-Oriented Technology Stack (DOTS/E.C.S.) and two Scriptable Render Pipelines (URP/HDRP). This guide covers how a lead engineer thinks about Unity: architecture, C# performance, data-oriented design, rendering, physics, multiplayer, and profiling. ## 1. Engine Architecture ``` Unity Player/Editor Application (UnityEngine) SceneManager -> load/unload Scenes (additive) GameObject (identity + transform) Component (MonoBehaviour, Renderer, Collider, Animator...) PlayerLoop: (fixed update order, engine-driven) ScriptableRunLoops -> custom systems injected Module systems: Physics (PhysX/Box2D), Audio (FMOD), UI (UGUI/UI Toolkit), Rendering (URP/HDRP/Built-in), Input (new Input System) ``` ### 1.1 The Player Loop & Update Order Within one frame Unity calls systems in a fixed order: ```csharp void Update() // per rendered frame void FixedUpdate() // fixed timestep: physics, network ticks (default 0.02s) void LateUpdate() // camera follow, post-movement ``` Rules: - **Never move Rigidbody in Update()**; move via `FixedUpdate` or use `rb.velocity` only in FixedUpdate. Changing `transform` in Update fights the physics solver. - Camera interpolation in LateUpdate. - Scene load is synchronous unless using `Addressables`/`SceneManager.LoadSceneAsync`. ### 1.2 Multiple Scenes (Additive Loading) - Split levels into persistent + gameplay scenes; stream with `LoadSceneAsync` + `AllowSceneActivation = false` to keep the load from blocking. - Use `SceneManager.MergeScenes` or DontDestroyOnLoad root object. ## 2. GameObject & Component Model ```csharp public class Health : MonoBehaviour { public int maxHp = 100; int current; void OnEnable() { /* add self to manager lists */ } void OnDisable() { /* remove self */ } } ``` ### 2.1 GetComponent Performance `GetComponent<T>()` per frame per object is a hot-path mistake: - Cache component references in `Awake()` once. - Prefer `TryGetComponent`. - `GetComponentsInChildren` only at load or on structural change. - Hot update loops use **managers** (single static list of registered components), not per-object `Find`. ### 2.2 FindObjectOfType / GameObject.Find These are O(n) with allocations. They MUST not run on the per-frame path. For singletons, register in `Awake`, destroy in `OnDestroy`: ```csharp public static GameManager I; // global accessor void Awake() { I = this; } void OnDestroy() { if (I == this) I = null; } ``` ## 3. The DOTS / ECS Stack Unity DOTS replaces MonoBehaviours with archetype-based ECS: ```csharp // Component - plain struct, no MonoBehaviour public struct Velocity : IComponentData { public float3 Value; } // System [BurstCompile] partial struct MoveSystem : ISystem { [BurstCompile] public void OnUpdate(ref SystemState state) { foreach (var (transform, v) in SystemAPI.Query<RefRW<LocalTransform>, RefRO<Velocity>>()) { transform.ValueRW.Position += v.ValueRO.Value * SystemAPI.Time.DeltaTime; } } } ``` - Entities stored in archetypes (SoA per component) → cache-friendly. - `Burst` + `Jobs` compiles to native SIMD-optimized code. - SubScene baking: author GameObject scenes → baked entities at build. ### When to use DOTS vs classic GameObjects | Need | Path | |------|------| | 1000s of agents, bullets, particles | DOTS/ECS + Burst + Jobs | | Rapid prototyping / small games | Classic GameObject/MonoBehaviours | | UI, gameplay narrative, inventory | Classic (UGUI) | | Massive open world streaming | DOTS SubScene streaming | ## 4. Rendering Pipelines ### 4.1 Choose the Pipeline | Pipeline | Use | |----------|-----| | Built-in | Legacy, quick prototypes, simple mobile games | | **URP** | Default for most new projects (PC+mobile), SRP Batcher optimal | | **HDRP** | Film-grade lighting: deferred, ray tracing, volumetrics, high-end PC/console | | `ScriptableRenderer` (custom) | You own pass ordering; use with `RenderGraph` | ### 4.2 URP vs HDRP in practice - URP: forward rendering, 2D lights, SRP batcher, simplified. - HDRP: deferred, clustered lighting, volumetric fog, SSR, RT reflections (DXR). - Both are `ScriptableRenderPipeline`; they run in the `Rendering.Update` player-loop phase. ### 4.3 Draw Call Reduction 1. **Enable SRP Batcher** (URP/HDRP property `UseSRPBatcher`) — batches dynamic shadows+lit objects with compatible materials. 2. **GPU instancing**: `Graphics.DrawMeshInstanced` for repeated meshes (grass, crowds, particles). 3. **Static batching**: mark static → combined at build. 4. **Addressables + SDF 2D**: not draw-call related, but stream textures to control bandwidth. 5. Minimize material variants (`KeywordEnum`, `MaterialPropertyBlock` for per-object color). 6. Bake lighting (Lightmapping) instead of dynamic. ### 4.4 Shader Lab & Shader Graph - Shader Graph (node-based) is preferred for artists; expose exposed properties only. - Hand-written shaders: write in HLSL; SRP uses `HLSLPROGRAM` blocks and `#pragma target` 3.0+. - Watch out: Unity editor implicit `#pragma multi_compile _ _ALPHATEST_ON`; use `#pragma shader_feature_local` to avoid variants. ## 5. Physics - Unity uses **PhysX** (3D) and **Box2D** (2D). - Fixed timestep at 0.02s (50Hz); fast small objects tunnel → use `Physics.Burst`/`Physics.Solver` or a dedicated CCD sensor. - Sleeping: overlapping static triggers should disable colliders (`isTrigger = true`) to avoid touches every frame. - Layers & matrix matter: `Physics.IgnoreLayerCollision(playerLayer, enemyLayer, true)` reduces contact generation. - `Physics.SyncTransforms()` forces syncing; avoid calling per-frame. ## 6. Animation & Timing - **Animator** state machine with transitions; avoid heavy tree layers per object. - `Time.deltaTime` may be 0 on pause; use `Time.unscaledDeltaTime` for UI. - Animation events are editor-time; use `OnAnimationEvent` or `AnimationEvent` binding. - For 100s of animating characters prefer playables or `AnimationUpdateMode` fixed with simple lerps. ## 7. UI (UGUI / UI Toolkit / IMGUI) - **UGUI** (Canvas): fine for HUD; keep panels/Canvas to 1-2, enable `Vertex Buffer` reuse, disable raycast on static. - **UI Toolkit** (new): UXML/USS, better for menus and editor tooling; runtime since 2023. - **IMGUI**: editor windows and debugging only, not runtime UI. - Rule: no layout rebuild per frame; cache `RectTransform`s, minify `GraphicRaycaster` blockers. ## 8. Asset Pipeline ### 8.1 Addressables Addressables = load by key, async, memory-managed: ```csharp Addressables.LoadAssetAsync<GameObject>("enemies/wolf").Completed += h => { var go = Instantiate(h.Result); go.AddComponent<DeferredUnload>(); }; Addressables.Release(handle); ``` - Use `AssetReference` fields instead of hardcoded strings. - Set `addressables` remote catalog for updates. - Remember: `Instantiate(referencedObject)` body must be released too. ### 8.2 Streaming & Background Loading - Texture streaming (`Texture2D.streamingEnabled`) or Addressables load in `OnTriggerEnter`. - Audio: `AudioClip.Create` streaming for ambience. - Never `Resources.Load` in Update; use `Resources.LoadAll` once. ## 9. Multiplayer (Netcode for GameObjects / Netcode Transport / UnityTransport) - Authoritative: server owns state; clients send inputs; interpolate snapshot positions. - `[ServerRpc]` from client to server; `[ClientRpc]` from server to clients. - Sim tick = fixed timestep; physics simulation server-side only; clients render interpolated states. - Use `NetworkVariable<T>` — beware of per-frame `NetworkVariable` writes (delta encode, avoid 60/s floats). - Prediction: `PredictedPlayer` pattern with client-side `NetworkTransform` interpolation. ## 10. Performance Rules (Lead Level) 1. **Profile first**: Unity Profiler + Burst metrics; GPU profiler (RenderDoc, GPUView). 2. `ExecuteInEditMode` scripts only where needed; keep editor-only work in `#if UNITY_EDITOR`. 3. Avoid `transform.position` in a loop (accessing Transform is cheap-ish, but allocations from `Find`/`GetComponentsInChildren` are not). 4. Garbage: minimize per-frame allocations; cache arrays; use `NativeArray`/`NativeList` in jobs. 5. Broke update: use `FixedUpdate` only for physics; game logic in `Update`. 6. Root motion vs scripted movement: profile `Animator` cost. 7. Hidden cost: `Camera.main` cached; avoid in Update. ### 10.1 Frame Budget Table Template | System | Budget (ms) @ 60fps | |--------|---------------------| | Simulation (Update) | 2.0 | | Physics (Fixed) | 2.0 | | Animation | 2.0 | | AI / NavMesh | 1.5 | | Rendering (CPU submit) | 3.0 | | Rendering (GPU) | 8.3–14.0 | | UI | 1.0 | | Netcode | 1.0 | | Misc / Driver | 2.0 | Total must fit in 16.6ms (or 1000/refresh). ## 11. Project Structure Convention ``` Assets/ Scripts/ # namespaces by system Scenes/ Prefabs/ Addressables/ # addressable groups Materials/ Textures/ Audio/ _BuildTools/ # editor scripts, CI .gitignore: Library/ Temp/ Logs/ UserSettings/ ``` Use `.asmdef` (assembly definition) per folder to speed up compile times and control references. ## 12. Anti-Patterns | Anti-pattern | Consequence | Fix | |--------------|-------------|-----| | `FindObjectOfType` in Update | GC spikes, slow | cache in Awake | | Rigidbody move in Update | physics jitter | FixedUpdate + rb.Move | | Per-frame allocations (strings) | GC pauses | string builders, pooling | | Resources.Load in loop | load spikes | Addressables + cache | | One Canvas for everything | layout rebuild | split by update frequency | | Scripts in Editor without serialization | lost state | `[SerializedField]` | | Not using .asmdef | long compile | per-system asmdefs | | Physics without layer matrix | wasted contacts | `IgnoreLayerCollision` | ## 13. When to NOT Use Unity - Native console/PC-only huge world with full control → custom engine + Vulkan (see `skills/game/game-development/vulkan/SKILL.md`). - Web "tiny" canvas game → Engines like Phaser or vanilla canvas might be lighter. - Deep customization of renderer required beyond URP/HDRP budget → engine-level control required. - For ECS patterns independent of engine, see `skills/game/game-engine/ecs-pattern/SKILL.md`. ## 14. References - `skills/game/game-development/unity-csharp/SKILL.md` — Unity C# scripting and DOTS internals - `skills/game/game-engine/ecs-pattern/references/unity-dots-in-practice.md` — DOTS in real projects - `skills/game/game-engine/patterns/references/game-loop-and-timestep.md` — update timing patterns - `skills/game/multiplayer-netcode/SKILL.md` — netcode patterns & prediction - `skills/game/game-development/physics-engine/SKILL.md` — physics internals used by engines
View on GitHub