ArTchie Studios

Unused Asset Finder

Lists assets under Assets/ that look unused. Never deletes files. Optional quarantine moves them with AssetDatabase.MoveAsset to Assets/AnvilReports/quarantine/.

Editor-only. Unity 2021.3+. Lives in Anvil.Editor (includePlatforms: Editor). No extra packages.

What "unused" means

Default (reachable-from-roots OFF): an unused asset is one whose GUID is never referenced by another asset. Own .meta does not count as a usage. Two assets that only reference each other are not reported.

Reachable from roots ON: build scenes plus anything under Resources/, StreamingAssets/, Editor/, or Plugins/, and .asmdef files, are roots. Anything not reachable from those roots via AssetDatabase.GetDependencies (direct edges, BFS) is unused. Mutual-only islands are reported in this mode (A↔B with no path from a root).

Always-used rules still apply in both modes (those assets are never listed).

Editor window

ArTchie / Anvil / Unused Asset Finder

Control What it does
Include / Exclude Comma-separated assetPath substrings. Empty include = all. Exclude wins. Applied on Scan and as a live row filter.
Type filter Substring on Unity type name (Texture2D, MonoScript, …). Empty = all types. Applied on Scan and live.
Ignore scripts (.cs) Default ON. Hides .cs so code-only scripts do not flood the list. Toggle then Scan to include them.
Reachable from roots Default OFF. Scan again after toggling.
Sort by size (else path) Display sort. Size is largest-first using on-disk file length.
Scan Builds a reverse GUID map from YAML/text assets and .meta files under Assets/ (skips Packages/, Library/, and Assets/AnvilReports/quarantine).
Copy JSON Puts the last scan's report JSON on the clipboard.
Write report Writes <project>/AnvilReports/unused-assets.json (creates the folder if needed).
Move to AnvilReports/quarantine (Undo-ish: use AssetDatabase.MoveAsset) Moves checked unused rows, or all currently filtered rows if none are checked. Skips if the destination already exists. Does not delete. Then rescans.
Ping / Select AssetDatabase.LoadMainAssetAtPath then EditorGUIUtility.PingObject (Select also sets Selection.activeObject).

Each row is: assetPath, guid, Unity type name, and bytes (file length).

Batchmode (CI)

Unity -batchmode -nographics -quit -projectPath <proj> -executeMethod Anvil.UnusedAssetFinder.RunBatch -anvil-out <file.json> -anvil-fail
Arg Meaning
-anvil-out <file.json> Output path. Default: <project>/AnvilReports/unused-assets.json
-anvil-fail Exit code 1 if any unused assets were found; otherwise 0
-anvil-include <substr> Repeatable. Keep only unused rows whose assetPath contains at least one include.
-anvil-exclude <substr> Repeatable. Drop unused rows whose assetPath contains any exclude.
-anvil-type Texture2D Optional. Keep rows whose type contains this substring.
-anvil-include-scripts Include .cs files. Default is to exclude them.
-anvil-reachable-roots Turn reachable-from-roots mode ON. Default is OFF (never-referenced).
-anvil-quarantine After the scan, AssetDatabase.MoveAsset matching unused rows to Assets/AnvilReports/quarantine/, then write JSON with "quarantined". Without this flag, batch never moves files.

RunBatch only calls EditorApplication.Exit when Application.isBatchMode is true. It logs count=, assetsScanned=, quarantined=, and the output path.

Also accepts -anvil-out=file.json, -anvil-include=substr, -anvil-exclude=substr, and -anvil-type=Texture2D.

JSON

{
  "tool": "UnusedAssetFinder",
  "generatedUtc": "2026-08-18T02:00:00Z",
  "count": 1,
  "assetsScanned": 42,
  "quarantined": 0,
  "items": [
    {
      "assetPath": "Assets/Art/orphan.png",
      "guid": "deadbeefdeadbeefdeadbeefdeadbeef",
      "type": "Texture2D",
      "bytes": 12345
    }
  ]
}

bytes is on-disk file length. assetsScanned is how many Assets/ files were considered (folders, Packages, Library, and quarantine excluded). quarantined is how many files MoveAsset actually moved (0 unless the window button or -anvil-quarantine ran). Batch JSON items still list the unused paths from the scan (pre-move).

Always treated as used (never reported)

  • Anything under an Editor, Resources, StreamingAssets, Plugins, Gizmos, or Editor Default Resources folder
  • Scenes listed in EditorBuildSettings.scenes
  • Currently open / active editor scenes when that list is readable
  • .asmdef assets
  • Files named like a README (README, Readme.md, read_me, …)
  • Folders themselves (not listed as unused)

Quarantine

  • Destination: Assets/AnvilReports/quarantine/<path under Assets/> (for example Assets/Art/orphan.pngAssets/AnvilReports/quarantine/Art/orphan.png).
  • Uses AssetDatabase.MoveAsset (Unity may offer Undo in the editor). Never deletes.
  • Skips a file if the destination already exists (no overwrite, no timestamp rename).
  • Refreshes AssetDatabase after moves.
  • Quarantined files are skipped on later scans so they do not flood the list.

Notes

  • Default reference map: guid: tokens in YAML/text (.unity, .prefab, .asset, .mat, .controller, .anim, .preset, .vfx, .shadergraph, .asmref, and other text assets) plus every .meta. Also AssetDatabase.GetDependencies (direct) so binary-serialized assets are not missed.
  • C# using / type references are not GUID references. Ignore-scripts (default ON) keeps those from filling the report; pass -anvil-include-scripts or uncheck the box and Scan if you want them.
  • Addressables, AssetBundles, and Resources-style loads by string are not modeled beyond the always-used folders / root folders above.
  • Empty projects and a missing AnvilReports folder are fine.
  • Does not touch runtime player code. Whole file is #if UNITY_EDITOR.

Source in the box: Assets/ArTchie Studios/ArTchie-Suite/com.artchie.anvil/Documentation/UnusedAssetFinder.md