Checks Reference
intermediate featureThis page lists every check Editor Doctor Pro performs, grouped by the Doctor tab where the finding surfaces. Each check has a stable Rule ID (used by reports, the suppression store and the CLI), a title, a default severity, and an Auto-fix indicator. Total: 120 checks across 5 categories.
Fix tiers: Auto means EDP can apply the fix itself from the Doctor row; Manual opens the offender so you can resolve it by hand; None means the rule is advisory (typically Code Audit findings that depend on author intent).
Asset Health (46)
14 Auto-fix, 32 Manual.
Asset import and reference health: textures, materials, meshes, audio, animation, scriptable objects, prefab references, project settings. When the Scene Pilot Pro bridge is enabled, an additional 64 in-scene SPP checks run across every scene and prefab in the project; they surface under the Scenes tab and follow the same severity / suppression model. Click any finding in the Doctor to expand the explanation panel (Why / Safe fix / Risk); rule copy lives in Editor/Data/rule-explanations.json so teams can extend it without forking EDP.
| # | Rule ID | Title | Severity | Auto-fix | Description |
|---|---|---|---|---|---|
| 1 | EDP-ADDR-001 | Asset is both Resources and Addressable | Warn | Manual | An asset under a Resources/ folder is also marked Addressable. Pick one runtime loading path to avoid duplicate inclusion and ownership ambiguity. |
| 2 | EDP-ANIM-001 | Animation clip has no curves | Warn | Manual | A standalone .anim clip carries zero curves and zero object-reference curves. Usually a leftover from a renamed import or a half-authored clip; populate or delete to stop shipping dead weight. |
| 3 | EDP-ANIM-002 | Animator layer has no entry state | Error | Manual | An AnimatorController layer has zero states, or its default state is null. The Animator silently sits in AnyState at runtime. Pick a default state per affected layer. |
| 4 | EDP-ANIM-003 | Animator layer missing avatar mask | Warn | Manual | An additive or override Animator layer has non-zero weight but its Avatar Mask is null. The layer silently drives all body parts instead of the masked subset. |
| 5 | EDP-ANIM-004 | Animation clip has stale events | Warn | Manual | An AnimationClip carries one or more events with an empty function name. The event silently no-ops at runtime; wire it to a method or strip it. |
| 6 | EDP-ANIM-005 | Override controller has missing source | Warn | Manual | An AnimatorOverrideController points to a deleted source controller, so it no longer drives anything. Repoint to a valid source or delete the override. |
| 7 | EDP-ANIM-006 | Animator references missing parameters | Warn | Manual | AnimatorController transitions or blend trees reference parameter names that are not declared on the controller. The conditions never evaluate as authored. |
| 8 | EDP-AUD-001 | Long clip uses Decompress On Load | Warn | Auto | A clip longer than ~5s ships with load type Decompress On Load, holding the full decompressed PCM in RAM as soon as it is referenced. Switch to Compressed In Memory unless instant playback is required. |
| 9 | EDP-AUD-002 | Stereo clip is a force-mono candidate | Info | Auto | A stereo clip lacks Force To Mono. Stereo on a 3D-spatialised source is downmixed at runtime, doubling memory for no gain. Enable Force To Mono if the clip is only played 3D. Heuristic: opt-in because stereo can be intentional. |
| 10 | EDP-AUD-003 | Short clip ships at high bitrate | Info | Manual | A clip shorter than ~1s imports at >192 kbps. The extra quality is inaudible at that duration and costs disk + bandwidth. Lower the quality on the default sample settings. |
| 11 | EDP-AUD-004 | Short clip uses Streaming | Warn | Auto | A clip shorter than ~1s imports with load type Streaming. Streaming a tiny clip keeps a file handle open per-instance for no benefit. Switch to Decompress On Load. |
| 12 | EDP-AUD-005 | Long clip preloads audio data | Warn | Auto | A clip longer than ~5s has Preload Audio Data on. Long clips loaded eagerly stall scene transitions. Disable to lazy-load on first play. |
| 13 | EDP-MAT-001 | Material has no shader assigned | Error | Manual | The material's shader reference is null (deleted or GUID changed). Every renderer pointing at it falls back to the magenta error material at runtime. Pick a replacement shader manually. |
| 14 | EDP-MAT-002 | Material's shader has compile errors | Error | Manual | The material points at a shader that fails to compile. Renderers will fall back to magenta at runtime. Open the shader source and resolve before shipping. |
| 15 | EDP-MAT-003 | URP shader used without URP installed | Warn | Manual | The material is wired to a Universal Render Pipeline shader, but the URP package is not loaded in this project. The material renders as magenta. Install URP or swap the shader. |
| 16 | EDP-MAT-004 | Material could enable GPU Instancing | Info | Auto | The material's shader supports instancing (Standard / URP Lit / Simple Lit / Unlit) but Enable Instancing is off. Enabling lets repeated instances draw in fewer batches. |
| 17 | EDP-MAT-005 | Material overrides render queue | Info | Auto | The material sets an explicit render queue different from the shader's default. Often a leftover from manual debugging that breaks transparent / opaque sort order across pipelines. Reset unless the override is intentional. |
| 18 | EDP-MAT-006 | Material has orphan shader keywords | Warn | Auto | The material has shader keywords enabled that its current shader does not declare. Auto-fix strips the orphan keywords; the material's render look is unchanged because Unity already ignored them at build time. |
| 19 | EDP-MESH-001 | Mesh has Read/Write enabled | Warn | Auto | The model importer keeps a CPU-side copy of the mesh, doubling memory. Disable unless the mesh is sampled from C# code (Mesh.vertices, MeshCollider with non-convex collider, etc.). |
| 20 | EDP-MESH-002 | Mesh exceeds vertex budget | Warn | Manual | Total vertex count across all sub-meshes exceeds the budget (default 65k). Consider authoring an LOD chain or simplifying the mesh. |
| 21 | EDP-MESH-003 | Model imports redundant materials | Info | Auto | The model imports materials with location InPrefab. If your project assigns materials manually, the imported sub-assets are dead weight; set Material Import Mode to None. |
| 22 | EDP-MESH-004 | Model imports normals as None | Warn | Manual | The model importer's normals are set to None, so meshes ship unlit-looking. Often a leftover from a thumbnail / props preset reused on production assets. Pick Import or Calculate. |
| 23 | EDP-MESH-005 | Model imports clips with compression off | Warn | Auto | The model imports animation but Animation Compression is Off. Wastes disk and runtime memory. Pick Keyframe Reduction or Optimal to shrink clips. |
| 24 | EDP-REF-001 | Prefab has missing scripts | Error | Manual | A prefab asset has at least one component slot whose script reference is null (the script was deleted or its GUID changed). Remove or replace the slots to restore the prefab. |
| 25 | EDP-REF-002 | Scene has missing scripts | Error | Manual | A scene asset contains GameObjects whose component slot points at a script that no longer resolves. Open the scene and remove or replace the slots. |
| 26 | EDP-REF-003 | Prefab has broken material reference | Error | Manual | A Renderer component on a prefab carries a material slot whose reference is missing (the material asset was deleted while the prefab kept the fileID). Repoint or clear the slot. |
| 27 | EDP-REF-004 | Prefab has broken sprite reference | Error | Manual | A SpriteRenderer or UI Image component carries a sprite slot whose reference is missing (the sprite asset was deleted while the prefab kept the fileID). Repoint or clear the slot. |
| 28 | EDP-REF-005 | Prefab has broken mesh reference | Error | Manual | A MeshFilter or SkinnedMeshRenderer carries a mesh slot whose reference is missing. Repoint or clear the slot so the prefab renders. |
| 29 | EDP-REF-006 | Prefab has broken audio reference | Error | Manual | An AudioSource on a prefab carries a clip slot whose reference is missing. Repoint or clear the slot to silence the warning at runtime. |
| 30 | EDP-REF-007 | Prefab has dangling object references | Warn | Auto | A custom MonoBehaviour field on the prefab points at a deleted asset (the fileID still serializes a non-zero instance id). Clearing the slot is safe and removes the warning. |
| 31 | EDP-REF-008 | Prefab has broken animator reference | Error | Manual | An Animator component carries a controller slot whose reference is missing. The Animator does nothing at runtime; repoint or clear the slot. |
| 32 | EDP-REF-009 | Scene or prefab has broken serialized references | Warn | Manual | A scene or prefab serializes object references whose asset GUID no longer resolves. Repoint the fields or clear them in the inspector. |
| 33 | EDP-SETTINGS-001 | ProjectSettings has broken asset references | Error | Manual | A ProjectSettings asset serializes a GUID that no longer resolves, commonly a deleted URP/HDRP pipeline asset, renderer, profile or build setting. |
| 34 | EDP-SO-001 | ScriptableObject has missing script | Error | Manual | The ScriptableObject's m_Script reference is null (type renamed, deleted or moved without a FormerlySerializedAs migration). The asset deserialises to null at runtime. |
| 35 | EDP-SO-002 | ScriptableObject has broken references | Error | Manual | A ScriptableObject field points at a deleted asset. Repoint or strip the field manually; auto-clearing might silently neutralise behaviour that depends on it. |
| 36 | EDP-SO-003 | ScriptableObject has no references | Info | Manual | No other asset in the project references this ScriptableObject through AssetDatabase. It may still be loaded by name from Resources/ or Addressables; confirm or remove. |
| 37 | EDP-SO-004 | Resources/ ScriptableObject is never loaded | Info | Manual | The asset lives under Resources/ but no script in the project mentions its load name. Strong heuristic for 'ships in the build but never used'. Move out of Resources/ or remove. |
| 38 | EDP-SO-005 | ScriptableObject reference cycle | Warn | Manual | Two or more ScriptableObjects reference each other, directly or transitively. Often safe at runtime but resists clean import / version-control diffs. Break the cycle on whichever side makes sense. |
| 39 | EDP-TEX-001 | Normal map imported as sRGB | Error | Auto | The asset's name (_n / _normal / etc.) strongly suggests a normal map but its importer type is not NormalMap. The texture is sampled as sRGB, producing wrong lighting at runtime. |
| 40 | EDP-TEX-002 | UI / Sprite texture has mipmaps | Warn | Auto | A texture imported as Sprite (or living under a UI / Sprites folder) has mipmaps enabled. UI is screen-space; mipmaps waste ~33% of texture memory for no visual gain. |
| 41 | EDP-TEX-003 | Texture maxSize above 2048 for smaller source | Warn | Auto | The importer's maxTextureSize is above 2048 even though the source image is smaller. Wastes import / build time without adding any detail. Clamp to a tighter cap. |
| 42 | EDP-TEX-004 | Texture ships uncompressed | Warn | Manual | A non-tiny texture (256px or larger) ships with compression set to Uncompressed. Rarely intentional and inflates build size dramatically. Pick a compressed format unless lossless quality is required. |
| 43 | EDP-TEX-005 | Texture has Read/Write enabled | Warn | Auto | The texture importer keeps a CPU-side copy, doubling memory. Auto-fix clears Read/Write on the importer; suppress per-asset when the texture is sampled from C# (GetPixels, custom CPU shaders). |
| 44 | EDP-TEX-006 | Non-power-of-two texture (no scale) | Info | Manual | A non-Sprite texture has non-POT dimensions and NPOT Scale is set to None. Compressed formats often fall back to RGBA32 in that combination, blowing up texture memory. |
| 45 | EDP-TEX-007 | NormalMap type without normal-map name | Info | Manual | The importer's texture type is NormalMap but the filename has no normal-map markers. Often a leftover from a copy-pasted preset; confirm the type is correct. |
| 46 | EDP-TEX-008 | Sprite alpha but transparency off | Warn | Auto | The texture is imported as Sprite, the source has an alpha channel, but Alpha Is Transparency is off. Edges show dark bleed in atlases / UI. |
Prefab Drift (9)
Prefab instances vs. their source assets: redundant overrides, added or removed components and GameObjects, deep variant chains, sibling instances that have drifted apart. Findings group automatically into drift clusters - every instance of the same source prefab is shown together so you can reconcile in bulk via Apply to source or Revert all in cluster. Every drift action is recorded in Action History and reverts as a single batch.
| # | Rule ID | Title | Severity | Auto-fix | Description |
|---|---|---|---|---|---|
| 1 | EDP-DRIFT-001 | Prefab instance has override sprawl | Warn | Manual | An instance carries more property overrides than the configured threshold. Signals that values are being authored per-instance instead of on the asset. Apply or revert in bulk to keep instances aligned. |
| 2 | EDP-DRIFT-002 | Redundant property override | Info | Auto | A property modification on the instance carries the same value as the source's current value. The override exists for no reason and can be reverted safely. |
| 3 | EDP-DRIFT-003 | Added component on prefab instance | Warn | Auto | A prefab instance carries a component that does not exist on its source. Structural drift; decide whether to apply the component to the asset, keep it on the instance only, or revert. |
| 4 | EDP-DRIFT-004 | Removed component on prefab instance | Error | Manual | A prefab instance is missing a component that exists on its source. Often someone deleted a required component on one instance only. Decide whether to re-add it or apply the removal to the source. |
| 5 | EDP-DRIFT-005 | Added GameObject on prefab instance | Warn | Auto | A prefab instance has a child GameObject that does not exist on its source. Structural drift; revert the extra child or apply it back to the asset. |
| 6 | EDP-DRIFT-006 | Removed GameObject on prefab instance | Error | Manual | A prefab instance is missing one or more child GameObjects that exist on its source. The removal usually breaks references the source prefab still ships. Re-add it or apply the removal to the source. |
| 7 | EDP-DRIFT-007 | Variant chain too deep | Warn | Manual | A prefab variant chain is longer than the maintainable threshold (3 levels). Long chains force every change to cascade through every intermediate variant. Flatten an intermediate level. |
| 8 | EDP-DRIFT-008 | Variant has missing parent prefab | Error | Manual | A prefab variant declares a parent that no longer exists in the project. The instance still loads but future edits behave unpredictably. Restore the parent asset or unpack the variant. |
| 9 | EDP-DRIFT-009 | Sibling instances drift apart | Info | Manual | Two or more instances of the same source prefab carry different override sets. Suggests the prefab is being authored ad-hoc per instance instead of on the asset; reconcile via Bulk Apply on a canonical instance. |
Build Review (6)
EditorBuildSettings consistency plus shipped-payload hygiene: orphan scene paths, missing-on-disk entries, duplicates, scenes referenced but disabled, paths that escape the project, and demo packages that ship a stray Resources/ folder. All auto-fixes operate on a whole-array snapshot of EditorBuildSettings.scenes (or the renamed folder, for BUILD-006), so reverting from Action History restores the exact state before the fix - even when a single fix removes multiple entries. The Apply All Auto Fixes button at the top of the Build Review tab batch-fixes every auto-tier finding in one operation and reverts as a single Action History entry.
| # | Rule ID | Title | Severity | Auto-fix | Description |
|---|---|---|---|---|---|
| 1 | EDP-BUILD-001 | Build settings entry orphan path | Error | Auto | An EditorBuildSettings.scenes entry has a path that does not resolve to a .unity asset on disk, even though the GUID still resolves elsewhere. Auto-fix removes the orphan entry. |
| 2 | EDP-BUILD-002 | Build settings entry missing on disk | Error | Auto | An EditorBuildSettings.scenes entry refers to a deleted asset (its GUID resolves to nothing). The build still ships the dead entry. Auto-fix removes it. |
| 3 | EDP-BUILD-003 | Duplicate scene in build settings | Warn | Auto | The same scene path is listed twice in EditorBuildSettings.scenes. Auto-fix dedupes by keeping the first occurrence. |
| 4 | EDP-BUILD-004 | Scene disabled in build but referenced | Warn | Manual | A scene is disabled in EditorBuildSettings yet a ScriptableObject under Resources/ still holds a SceneAsset reference to it. The runtime load will fail. Enable the scene or drop the reference. |
| 5 | EDP-BUILD-005 | Scene path traverses outside project | Error | Auto | An EditorBuildSettings.scenes entry contains '..', is absolute, or otherwise escapes the project's canonical layout. Auto-fix normalises to a project-relative path or removes the entry. |
| 6 | EDP-BUILD-006 | Resources folder under demo content | Warn | Auto | A Resources/ folder sits inside a Demo or Demos tree. Unity bundles every Resources/ folder into the player regardless of Build Settings, so the demo assets ship with every production build. Auto-fix renames the folder to DemoResources/ (GUID-preserving); rename back in the editor when you need to run the vendor demo. |
Conventions (3)
Project-wide structural rules. Beyond the three native checks listed below, the Conventions module exposes user-defined rules: see Custom Rules to author predicates (Asset Path / Asset Type / Prefix / HasComponent / Not) paired with recipes (Flag / MoveAsset / Rename / Ignore) for project-specific contracts. Tag-related rules are configured under Settings > Conventions > Tag Contracts.
| # | Rule ID | Title | Severity | Auto-fix | Description |
|---|---|---|---|---|---|
| 1 | EDP-LINT-STRAY | Stray asset outside dominant folder | Warn | Manual | An asset of a given type lives outside the folder where most of its peers live within the same content root and editor/runtime context (learned per package, not hardcoded). Move it to the dominant folder if the placement is unintentional. |
| 2 | EDP-LINT-TAG-CLOSED | Tag declared but unused | Info | Manual | A tag declared in the user-defined tag contract is not used by any GameObject in the scoped prefabs or scenes. Prune the entry or re-tag the relevant objects. |
| 3 | EDP-LINT-TAG-CONTRACT | Tag missing required component | Warn | Manual | A GameObject inside a prefab carries a tag whose contract requires specific components, but at least one is missing. Add the component (or relax / strict-flag the entry). |
Code Audit (56)
IL-level analysis of every assembly the project compiles. Performance pitfalls inside per-frame messages, lifecycle correctness, async hygiene, naming conventions, Unity-API anti-patterns. Code Audit walks every assembly with Mono.Cecil (Unity's com.unity.nuget.mono-cecil package, no fork). Every Code Audit rule ships as Manual tier because the fix requires author intent: replacing a string-typed API call, moving an Instantiate out of Update, or adding a CancellationToken to an async method always depends on how the call is used. Most checks are Info / Warn by default; enable the convention rules under Settings > Rules to enforce a house style.
| # | Rule ID | Title | Severity | Auto-fix | Description |
|---|---|---|---|---|---|
| 1 | EDP-CODE-LIFECYCLE-ALLOCATE-IN-UPDATE | Texture2D / Mesh / Material constructed every frame | Error | None | A per-frame message body constructs a new Texture2D, Mesh, Material, RenderTexture, Sprite or Cubemap. These wrap unmanaged memory that GC cannot release on its own; leaving the constructor inside Update leaks memory every frame. Move it to Awake or pair it with a Destroy call. |
| 2 | EDP-CODE-PERF-ANIMATOR-STRING-PARAM | Animator.Set*/Get* with string parameter name | Info | None | Animator.SetFloat / SetInteger / SetBool / SetTrigger (and Get / Reset) hash the string parameter every call. Cache the hash with Animator.StringToHash("Name") in a static readonly int and pass the int overload to bypass the hash on every call. |
| 3 | EDP-CODE-ASMDEF-DUPLICATE-NAME | Duplicate asmdef name | Error | None | Two or more AssemblyDefinition files declare the same 'name' field. Unity rejects all but one and silently demotes the rest to Assembly-CSharp; the obvious symptom is that runtime references to the demoted asmdef stop resolving. Rename one of the duplicates. |
| 4 | EDP-CODE-ASMDEF-EDITOR-RT-LEAK | Runtime asmdef references editor-only asmdef | Critical | None | An AssemblyDefinition that ships in the player references an editor-only AssemblyDefinition (includePlatforms set to ['Editor']). The build will fail; the error only surfaces when the user actually triggers a player build, sometimes days after the reference was added. Move the consumed types into a runtime asmdef, add a runtime-side stub, or drop the reference. |
| 5 | EDP-CODE-ASMDEF-EMPTY | Empty asmdef (no scripts) | Warn | None | An AssemblyDefinition file owns a folder tree with no C# scripts. Empty assemblies still ship in the build, slow the compile graph, and usually mean an incomplete refactor (scripts moved away, asmdef left behind). Either populate the assembly or delete the asmdef. |
| 6 | EDP-CODE-ASMDEF-MISSING-FOLDER | Scripts not under any asmdef | Info | None | A folder contains C# scripts but no AssemblyDefinition (.asmdef) file owns them. Scripts in this state are compiled into the global Assembly-CSharp catch-all, which slows incremental compile and lets every other script reference them implicitly. Author an asmdef in this folder (or an ancestor) to scope the assembly. |
| 7 | EDP-CODE-ASMDEF-NAME-MISMATCH | Asmdef name does not match file name | Warn | None | An AssemblyDefinition file's 'name' field disagrees with its file name on disk. Unity uses the JSON name for assembly identity, so a rename on one side without the other silently breaks every reference. Decide which side is canonical and align the other. |
| 8 | EDP-CODE-ASYNC-VOID | async void method | Warn | None | An async void method has no awaitable handle and its exceptions are unobservable - in modern .NET an unhandled async-void exception kills the process. Switch to async Task and await it from the caller, or wrap the body in a try/catch that logs via Debug.LogException. |
| 9 | EDP-CODE-API-BARE-EXCEPTION | Throwing the bare Exception base type | Info | None | throw new Exception(...) (or ApplicationException) forces every callsite into catch (Exception). Use a specific exception subtype (ArgumentException, InvalidOperationException, a domain-specific subclass) so callers can opt into the failure they care about. |
| 10 | EDP-CODE-PERF-BOXING-IN-UPDATE | Value-type boxing inside per-frame message | Warn | None | Per-frame method body contains a box IL instruction. Boxing wraps a value type in a heap object - one allocation per call, every frame. Common triggers: string.Format with int args, List<object>.Add(struct), non-generic Equals. Use generic overloads or EqualityComparer<T> to avoid the box. |
| 11 | EDP-CODE-PERF-CAMERA-ALLCAMERAS-IN-UPDATE | Camera.allCameras inside per-frame | Info | None | Camera.allCameras / .allCamerasCount allocate a fresh Camera[] every read. Inside per-frame messages this becomes a hot allocation. Cache on Awake, maintain a manual list via Camera.onPreCull, or switch to the allocation-free Camera.GetAllCameras(buffer) overload (not flagged by this rule). |
| 12 | EDP-CODE-LIFECYCLE-CAMERA-MAIN-IN-UPDATE | Camera.main accessed every frame | Warn | None | Camera.main is implemented as FindWithTag("MainCamera") on every access; calling it inside Update / FixedUpdate / LateUpdate scans the active scene every frame. Cache the Camera reference in Awake or expose a serialized field instead. |
| 13 | EDP-CODE-CONV-NAMING-CLASS | Type not in PascalCase | Info | None | Class / struct / enum name does not start with an uppercase letter. .NET / C# convention is PascalCase for every type. Disabled by default. |
| 14 | EDP-CODE-CONV-NAMING-CONST | const field not in PascalCase | Info | None | Const field name does not start with an uppercase letter. The .NET Framework design guidelines specify PascalCase for constants; SCREAMING_SNAKE_CASE is a Java / C++ holdover that breaks C# reading rhythm. Disabled by default. |
| 15 | EDP-CODE-API-DEBUG-LOG-IN-PLAYER | Debug.Log* in runtime-shipping code | Info | None | Debug.Log / LogWarning / LogError / LogFormat called from runtime-shipping code. Each call allocates a string and pays the platform-log cost in a player build. Wrap with [Conditional("UNITY_EDITOR")] or strip via a Player Settings define for release builds. |
| 16 | EDP-CODE-PERF-DEBUG-LOG-IN-UPDATE | Debug.Log* inside per-frame message | Warn | None | Debug.Log inside Update / FixedUpdate / LateUpdate allocates the formatted string and pays the console cost every frame. Forgotten log lines in hot paths are a common framerate-regression source. Gate via [Conditional] or a verbosity flag. |
| 17 | EDP-CODE-API-DESTROY-IMMEDIATE-AT-RUNTIME | DestroyImmediate from runtime code | Warn | None | Object.DestroyImmediate skips Unity's deferred destruction queue, breaks coroutines that reference the object, and is intended for editor tooling only. From a runtime-shipping assembly use Destroy instead. |
| 18 | EDP-CODE-API-EMPTY-CATCH | Empty catch block | Info | None | A catch block has an effectively empty body (no log, no rethrow, no recovery). Swallowed exceptions hide real bugs and make field debugging nearly impossible. At minimum, log the exception or rethrow. |
| 19 | EDP-CODE-CONV-EMPTY-NAMESPACE | Type in global namespace | Info | None | Type lives in the global namespace. Global types clash with anything an external package brings in under the same name and pollute IDE auto-import. Wrap in a namespace named after the product / company / module. |
| 20 | EDP-CODE-LIFECYCLE-EMPTY-UPDATE | Empty Update / FixedUpdate / LateUpdate | Warn | None | A MonoBehaviour declares Update / FixedUpdate / LateUpdate with an empty body. Unity still pays the per-frame, per-instance dispatch cost even when the body does nothing; populated scenes lose 5-15% framerate to the flat floor. Delete the method. |
| 21 | EDP-CODE-LIFECYCLE-EVENT-NOT-UNSUBSCRIBED | Event subscribed without matching unsubscribe | Error | None | An event is subscribed in OnEnable / Awake / Start but never removed in OnDisable / OnDestroy. The publisher keeps the subscriber alive, leaking the GameObject and firing the handler on destroyed instances. Pair every += with a -= in the matching disposal message. |
| 22 | EDP-CODE-LIFECYCLE-FIND-IN-STARTUP | Find* / FindObject* called in Awake / Start | Warn | None | Find / FindWithTag / FindObjectOfType / FindAnyObjectByType (and the rest of the Find* family) scan the full active hierarchy. Calling them inside Awake / Start / OnEnable adds tens to hundreds of milliseconds to scene load on populated scenes. Wire a serialized field in the Inspector so the reference resolves at import time. |
| 23 | EDP-CODE-LIFECYCLE-FINDOBJECT-IN-UPDATE | FindObjectOfType / FindObjectsByType called every frame | Error | None | FindObjectOfType / FindAnyObjectByType / FindObjectsByType (and their pre-6.0 cousins) scan every loaded GameObject. Calling them inside Update / FixedUpdate / LateUpdate turns a 60fps scene into a slideshow as the Hierarchy grows. Cache the reference in Awake or expose a serialized field. |
| 24 | EDP-CODE-API-FINDOBJECTS-INCLUDE-INACTIVE | FindObjects opts into includeInactive | Warn | None | FindObjectsOfType / FindObjectsByType / FindAnyObjectByType called via the (bool includeInactive) overload. Scanning inactive objects is markedly slower than the default. If the call really needs inactive objects, suppress the rule for that site; otherwise drop the bool argument. |
| 25 | EDP-CODE-ASYNC-FIRE-AND-FORGET | async method called without await | Warn | None | A Task-returning method is called and its result discarded. Exceptions land in the unobserved-task queue and surface on the finalizer thread, making field reports impossible to triage. Await the call, or route it through a fire-and-forget helper that logs Task.Exception in a continuation. |
| 26 | EDP-CODE-LIFECYCLE-GETCOMPONENT-IN-UPDATE | GetComponent called every frame | Warn | None | GetComponent / GetComponentInParent / GetComponentInChildren (and the plural GetComponents variants) walk the GameObject's component list on every call. Per-frame use shows up as a flat profiler hot spot. Cache the reference in Awake or expose a serialized field. |
| 27 | EDP-CODE-PERF-INSTANTIATE-IN-UPDATE | Instantiate inside Update | Error | None | Object.Instantiate inside per-frame messages clones the full prefab hierarchy, runs every Awake/Start, reparents transforms and rebakes physics every frame. Replace with an object pool warmed on Start and toggle pooled instances via SetActive. |
| 28 | EDP-CODE-CONV-NAMING-INTERFACE | Interface name doesn't start with I | Info | None | Interface name does not start with the letter I. .NET / C# convention reserves the leading I prefix for interfaces so callers can distinguish 'depends on a contract' from 'depends on a type' at a glance. Disabled by default. |
| 29 | EDP-CODE-API-INVOKE-STRING | Invoke / StartCoroutine by string name | Warn | None | Invoke / InvokeRepeating / StartCoroutine called with a string method name. The IDE will not catch the dangling literal on rename; the call is also slower because Unity has to look the method up reflectively. Switch to the typed overload (method group or lambda). |
| 30 | EDP-CODE-PERF-LINQ-IN-UPDATE | LINQ inside Update / FixedUpdate / LateUpdate | Warn | None | Every LINQ chain allocates an enumerator and most terminal operators (.ToList, .ToArray, .Count, .First) walk the source eagerly. Per-frame use is a stable GC contributor that the profiler attributes to opaque allocations. Replace the chain with manual loops or precompute the result outside the per-frame messages. |
| 31 | EDP-CODE-CONV-LONG-METHOD | Method body over ~400 IL instructions | Info | None | Method body exceeds the convention's instruction threshold (~400 IL ops, roughly 100-200 source lines). Long methods are a known maintenance smell. Split into smaller helpers, extract guard clauses, or move logic into a dedicated type. Disabled by default. |
| 32 | EDP-CODE-PERF-MATERIAL-INSTANCE | Renderer.material instantiates | Info | None | Reading Renderer.material (or .materials) instantiates a per-renderer copy of the shared material on first access, allocates GC, and breaks SRP batching. Use sharedMaterial when reading; reach for material only when the per-instance copy is the goal. |
| 33 | EDP-CODE-CONV-MULTIPLE-TYPES-PER-FILE | Multiple top-level types in one file | Info | None | Source file declares two or more top-level types. The one-type-per-file convention makes navigation predictable, aligns with IDE refactor heuristics, and scopes merge conflicts. Disabled by default. |
| 34 | EDP-CODE-PERF-NEW-ARRAY-IN-UPDATE | New array allocated every frame | Warn | None | Per-frame message body contains a newarr IL instruction. Each call allocates the array header plus the element buffer. Reuse a field-stored array (and clear if needed), or rent from ArrayPool<T>.Shared for variable-length scratch space. |
| 35 | EDP-CODE-PERF-NEW-COLLECTION-IN-UPDATE | New collection allocated every frame | Warn | None | Per-frame message constructs a new List / Dictionary / HashSet / Queue. Each constructor allocates the object and a backing array. Reuse a field-stored instance with Clear, or construct it outside Update. |
| 36 | EDP-CODE-ASYNC-NEW-THREAD | Manual System.Threading.Thread creation | Warn | None | Creating raw threads with new Thread(...) bypasses the .NET thread pool, breaks under WebGL (which disallows them), and integrates poorly with async/await. Use Task.Run for one-shot background work, Unity's Job system for parallel compute, or UniTask.RunOnThreadPool when shipping with UniTask. |
| 37 | EDP-CODE-ASYNC-NO-CANCELLATION-TOKEN | Async API without CancellationToken | Info | None | Task.Run / Task.Delay called without a CancellationToken cannot be cancelled when the scene unloads, the app quits, or the user moves on. The task runs to its natural end and may call into destroyed Unity objects. Pass a CancellationToken sourced from a CancellationTokenSource you cancel in OnDestroy. |
| 38 | EDP-CODE-API-OBSOLETE | Obsolete API call | Warn | None | Project code calls a method, type or property marked [Obsolete]. Migrate to the recommended replacement before the deprecated symbol is removed in a future Unity / .NET version. |
| 39 | EDP-CODE-API-PARSE-NO-FORMAT-PROVIDER | Numeric Parse / TryParse without IFormatProvider | Info | None | int / float / double / decimal Parse (or TryParse) called without an IFormatProvider uses the current culture; the same code parses '1,5' as 15 in German or fails on '1,500' in English. Pass CultureInfo.InvariantCulture for stored data, a specific culture for user input. |
| 40 | EDP-CODE-PERF-PHYSICS-CAST-NO-LAYERMASK | Physics cast without LayerMask | Info | None | Physics.Raycast / OverlapSphere / SphereCast called without a LayerMask uses the default ~0 mask, forcing the broad-phase to test every collider in the scene. Pass a layer mask keyed to the expected hit set. |
| 41 | EDP-CODE-CONV-NAMING-PRIVATE-FIELD | Private field doesn't start with underscore | Info | None | Private instance field name does not start with an underscore. The Unity / Microsoft / .NET Foundation convention is _camelCase for privates and PascalCase for publics. Disabled by default; enable in Settings to enforce. |
| 42 | EDP-CODE-CONV-NAMING-PRIVATE-STATIC | Private static field naming | Info | None | Private static field name should start with s_ or _ to make 'this is process-wide state' visible at the call site. Convention used by Roslyn / Microsoft style guides. Disabled by default. |
| 43 | EDP-CODE-CONV-PUBLIC-FIELD-IN-MONOBEHAVIOUR | Public field in MonoBehaviour | Info | None | A MonoBehaviour exposes a public field. Convention is [SerializeField] private + a method or event for state changes so external scripts can't mutate it without notification. Disabled by default; enable in Settings if your team's convention forbids public fields. |
| 44 | EDP-CODE-CONV-NAMING-PUBLIC-METHOD | Public method not in PascalCase | Info | None | Public method name does not start with an uppercase letter. .NET / C# convention reserves camelCase for parameters and locals; public API surface is always PascalCase. Disabled by default. |
| 45 | EDP-CODE-API-RESOURCES-LOAD | Resources.Load / LoadAll call | Info | None | Resources.Load forces every asset under any Resources/ folder into the build payload and bypasses Addressables / AssetBundles. For larger projects migrate to Addressables; small projects can keep Resources/. |
| 46 | EDP-CODE-API-SCENE-LOAD-STRING | SceneManager.LoadScene literal not in build | Warn | None | A SceneManager.LoadScene or LoadSceneAsync string literal does not match an enabled Build Settings scene or Addressable scene address. |
| 47 | EDP-CODE-LIFECYCLE-STARTCOROUTINE-IN-UPDATE | StartCoroutine called every frame | Warn | None | StartCoroutine inside Update / FixedUpdate / LateUpdate allocates a new Coroutine + IEnumerator state machine on every call and stacks parallel coroutines on top of each other. Move the StartCoroutine to Start (a long-lived loop) or replace it with a regular timer pattern that doesn't allocate. |
| 48 | EDP-CODE-API-STRING-COMPARE-CULTURE | String comparison without StringComparison | Info | None | String.Compare / Equals / StartsWith / EndsWith called without a StringComparison argument uses the current culture, behaving differently in Turkish and other non-invariant locales. Pass StringComparison.Ordinal (or InvariantCulture / OrdinalIgnoreCase, etc.) to make the comparison deterministic. |
| 49 | EDP-CODE-PERF-STRING-FORMAT-IN-UPDATE | string.Format / Concat / Join inside per-frame | Info | None | string.Format / Concat / Join inside Update / FixedUpdate / LateUpdate allocates the result every frame and (for Format with value-type args) boxes the args into the params array. Cache the string and rebuild only when source data changes. |
| 50 | EDP-CODE-ASYNC-RESULT-WAIT | Task.Result / Task.Wait / GetAwaiter().GetResult() | Error | None | Synchronously blocking on a Task (Result, Wait, GetAwaiter().GetResult()) deadlocks the Unity main thread when the task tries to continue on the same SynchronizationContext. Use await instead - if the call site can't be async, restructure the code path so the blocking call lives on a dedicated thread. |
| 51 | EDP-CODE-PERF-TRANSFORM-FIND-IN-UPDATE | Transform.Find inside per-frame | Warn | None | Transform.Find walks the transform's children comparing names every call. Inside Update / FixedUpdate / LateUpdate the cost scales with sibling count and is invisible until the hierarchy grows. Cache the resolved Transform in a field on Awake. |
| 52 | EDP-CODE-PERF-OBJECT-NULL-CHECK-IN-UPDATE | UnityEngine.Object null check inside per-frame | Info | None | The == / != operator on UnityEngine.Object is overloaded so it crosses into native code to check whether the underlying object was destroyed. Per-frame null checks accumulate that cost. Cache the result in a local, or use ReferenceEquals when the destroyed-state check isn't needed. |
| 53 | EDP-CODE-PERF-VECTOR-MAGNITUDE | Vector magnitude (sqrt) read | Info | None | Vector2 / Vector3 / Vector4 .magnitude does a square root every call. When the result is only compared against another length (or zero), switch to sqrMagnitude and square the threshold; the math identity holds and the sqrt drops out. |
| 54 | EDP-CODE-PERF-WAITFOR-NOT-CACHED | WaitFor* allocated inline | Info | None | Coroutines re-evaluate the yield expression on every iteration. Inline new WaitForSeconds(x) / new WaitForFixedUpdate() allocates a fresh object on the heap each tick. Cache the instance as a readonly field and yield the field; for variable durations, reuse a single WaitForSeconds and call its constructor only when the duration changes. |
| 55 | EDP-CODE-API-SEND-MESSAGE | SendMessage / BroadcastMessage by string name | Warn | None | SendMessage / BroadcastMessage / SendMessageUpwards resolve the callee by string at runtime. Renames silently break the call and Unity walks the hierarchy reflectively on every dispatch. Replace with a direct method call, a UnityEvent, an Action delegate, or an interface. |
| 56 | EDP-CODE-API-TAG-STRING-COMPARE | Component.tag compared with == | Warn | None | Reading Component.tag allocates a fresh string every call; comparing it with == / != compounds the cost. Switch to CompareTag("Player") - allocation-free and the same result. |
Suggest an improvement
Help us improve this documentation page.