-
Notifications
You must be signed in to change notification settings - Fork 11
Features Editor Tools Editor Tools Guide
Batch tools for the asset work Unity makes you do one asset at a time. Crop a folder of sprites, build clips from a naming convention, apply one set of import settings to 400 textures, or find the serialized field Unity is silently throwing away — 30+ editor tools in all.
Everything here lives under Tools > Wallstop Studios > Unity Helpers unless stated otherwise. Two
tools also expose a public C# API you can drive from your own editor scripts:
Texture Settings Applier and
Sprite Settings Applier.
| I want to... | Tool |
|---|---|
| Strip transparent padding off a folder of sprites | Sprite Cropper |
| Apply one set of import settings to many textures | Texture Settings Applier |
| Apply different sprite settings per folder or name | Sprite Settings Applier |
| Stop guessing texture max sizes | Fit Texture Size |
| Put pivots on the visual center of mass | Sprite Pivot Adjuster |
| Upscale a batch of PNGs | Texture Resizer |
| Blur backgrounds for a UI pause menu | Image Blur Tool |
Turn Run_0.png ... Run_7.png into a clip |
Animation Creator |
| Turn a sliced sheet into several clips | Sprite Sheet Animation Creator |
| Split a sheet back into individual PNGs | Sprite Sheet Extractor |
| Change the timing or frame order of a clip | Sprite Animation Editor |
| Add footstep / hitbox events to a clip | Animation Event Editor |
| Sync clips between two folders | Animation Copier |
| Build a sprite atlas from a regex or a label | Sprite Atlas Generator |
| Catch broken prefabs before they ship | Prefab Checker |
Catch a missing override on a lifecycle method |
Unity Method Analyzer |
| Configure Unity Helpers compiler diagnostics | Analyzer Policies |
| Find a field Unity is silently not serializing | Serialized Field Validator |
| See everything wrong with the project at once | Asset Validation |
| React to assets being created or deleted | Asset Change Detection |
| Recompile without touching a file | Request Script Compilation |
| Keep failing test output after the run | Failed Tests Exporter |
| Start a test run a script outside the editor polls | Test Run Reporter |
Inspector attributes ([WGroup], [WButton], [WEnumToggleButtons], [WNotNull] and friends) are
covered in the Inspector documentation. The drawers documented
here are the ones these tools use most.
Tools > Wallstop Studios > Unity Helpers > Image Blur
You need a blurred copy of a background so the pause menu can sit on top of it without a render texture. This applies a Gaussian blur to a batch of images and writes the results next to the originals.
- Drag your background textures (or the folder holding them) onto Drag & Drop Images/Folders Here.
- Set Blur Radius — the slider runs
1to200;10is a soft haze,50is a heavy frost. - Click Apply Blur.
Each source produces a new file beside it; the originals are left alone:
Assets/UI/Backgrounds/
pause_bg.png # untouched
pause_bg_blurred_24.png # written by the tool
.jpg and .jpeg sources stay JPEG; every other format is written as PNG. Re-running at the same
radius appends _1, _2 and so on rather than overwriting.
The tool temporarily enables Read/Write and uses uncompressed import data while processing each
source. It restores both settings after that texture succeeds or fails, and shows one completion
summary for the whole batch. Small images and one-row or one-column passes avoid worker scheduling;
larger images retain parallel processing. The new blurred files are permanent project changes and
are not covered by Unity's undo history.
Editor scripts can blur an already readable texture without opening the window. ImageBlurAPI.TryBlur
returns a new texture owned by the caller; destroy it after encoding, saving, or displaying it:
if (ImageBlurAPI.TryBlur(source, 12, out Texture2D blurred, out string error))
{
try
{
// Use blurred here.
}
finally
{
UnityEngine.Object.DestroyImmediate(blurred);
}
}The API accepts radii from 1 through 200 and reports invalid or unreadable sources through
error. It does not change importer settings or write files.
For the same file output as the window, call ImageBlurAPI.TryWriteAsset with a project texture and
radius. It temporarily makes the source readable, restores its importer,
writes a uniquely named image beside it using the Unity project directory, and imports the result.
An occupied output name is preserved and the next numbered name is used, including when another
process creates the file while the blur is being written.
New output files are published from a complete staged image in the same directory. A failed
publication leaves the staged file for cleanup and does not expose a partial output. On Linux and
macOS, the output filesystem must support hard links; otherwise the API reports an error.
Publication shares destination ownership with DurableFile writes. If another writer holds that
destination, publication fails and keeps the staged image for cleanup.
On Linux and macOS, if staged-file cleanup fails after publication, the API still imports the
output and returns its path, but returns false with the cleanup error.
This operation writes a file;
Unity Undo cannot remove that output file.
Visual Demo
Adjusting blur radius from 0 to 200 pixels on a UI background texture
Tools > Wallstop Studios > Unity Helpers > Sprite Cropper
Art tools export the whole canvas, so a 40-pixel-tall character often ships inside a 256x256 texture that is mostly transparency. Sprite Cropper trims each texture to its alpha bounds, moves the pivot so the sprite does not shift in the scene, and shrinks the 9-slice border by the same amount. Padding a sprite that has no 9-slice border keeps that border at zero. When padding grows the output beyond the source import size limit, the output limit grows to fit the new image. Large sprites scan alpha in parallel to keep batch cropping responsive.
- Drop
Assets/Sprites/Charactersinto Input directories. - Leave Sprite Name Regex at
.*, or narrow it (^player_) to crop one character. - Set Left/Right/Top/Bottom Padding to
2if a shader samples outside the sprite (outlines, glow); otherwise leave them at0. - Tick Only Necessary so already-tight sprites are skipped.
- Click Find Sprites To Process, then Process N Sprites.
Results are written beside the source, or into Output Directory if you set one:
Assets/Sprites/Characters/
player_idle_0.png # 256x256, mostly transparency
Cropped_player_idle_0.png # 48x64, same pivot, sits in the same place in-scene
Overwrite Originals writes in place instead.
Sprite Cropper stages each PNG before replacing its destination. A failed encoding or staging write
reports an error and keeps the previous image bytes. The final swap has the
platform limits of DurableFile.
Editor scripts can call SpriteCropperAPI.TryFind(inputFolders, spriteNameRegex, singlePaths, multiPaths, out error) with Assets/... folder paths. It returns project asset paths for
single-sprite images and reports multi-sprite sheets separately. The window uses this same
discovery path. Script paths may use backslashes or a differently cased Assets prefix; the API
normalizes them before asset lookup. SpriteCropperAPI.Crop(assetPath, options) crops one sprite with explicit padding,
destination, overwrite, readability, and platform-setting options, and returns the output path,
status, and any error. The window uses the same crop operation for each selected sprite.
SpriteCropperAPI.TryBuildReplacementMap(folders, mapping, out error) finds existing
Cropped_* pairs, and SpriteCropperAPI.ReplaceReferences(folders, applyChanges) previews or
applies reference changes across project assets. Both accept an optional output folder when
cropped files were written away from their sources. Replacement is only for separate Cropped_*
outputs; the Danger Zone button is disabled when Overwrite Originals is on. Script callers can
pass overwriteOriginals: true to make ReplaceReferences reject that combination.
Before you run it:
- Output is always PNG bytes. In overwrite mode a
.jpgsource keeps its.jpgname and holds PNG data. - Only
Sprite Import Mode: Singletextures are cropped. Multi-sprite sheets are reported and skipped — run Sprite Sheet Extractor first if you need them split. - There is no undo, so commit before you run it.
- The collapsed Danger Zone at the bottom rewrites
.prefab,.unity,.asset,.mat,.animand.overrideControllerfiles to point at theCropped_*sprites. It makes you tick "I understand the risks and want to proceed." first, and it is the one action here you cannot walk back without version control.
Visual Demo
Before and after: transparent padding removed while preserving sprite content and pivot
Cropping first also makes Sprite Atlas Generator pack tighter. Crop output reuses dynamically sized pixel buffers, so repeated work does not retain one permanent pool bucket for every texture size. Folder and file-extension filtering scans directly without building temporary iterator chains during large asset searches.
Tools > Wallstop Studios > Unity Helpers > Texture Settings Applier
An artist drops 200 tiles into the project and every one arrives on Unity's defaults — bilinear filtering on pixel art, mipmaps you do not want, 8192 max size. This window applies one set of importer settings to every texture under a folder, with per-platform overrides.
- Drag
Assets/Sprites/Tilesinto Directory Sources (or add individual textures under Specific Textures). - Tick Apply Filter Mode and choose
Point; tick Apply MipMaps and leave Generate MipMaps off. - Under Default Platform Settings, set Max Texture Size to
2048and Compression toCompressedHQ. That group has no apply toggles — whatever it shows is written. - Click Calculate Stats — it reports
Textures to processandTextures that will change, and Preview (N) lists the first 200 paths. - Click Apply Settings to Textures.
Settings that tend to travel together:
| For | Filter Mode | Wrap Mode | Generate MipMaps | Compression |
|---|---|---|---|---|
| Pixel art and UI | Point |
Clamp |
off | CompressedHQ |
| Character sprites | Bilinear |
Clamp |
off | CompressedHQ |
| Tiling world textures | Trilinear |
Repeat |
on | CompressedHQ |
Platform Overrides adds an entry per platform (Standalone, Android, iPhone, WebGL,
Switch, ...). Each entry has its own apply toggles, so you can cap Android at 1024 while
Standalone keeps 2048.
Source image bytes stay unchanged; Unity saves importer settings and reimports changed textures.
The settings change is recorded as an Apply Texture Settings undo step. Require Changes Before
Apply (on by default) skips the reimport entirely when nothing would differ.
The same logic is public, so a build step or a custom importer can use it directly:
using System.Collections.Generic;
using UnityEngine;
using WallstopStudios.UnityHelpers.Editor.Sprites;
public static class TileImportStandard
{
public static bool ApplyTo(IReadOnlyList<string> assetPaths)
{
TextureSettingsApplierAPI.Config config = new()
{
applyFilterMode = true,
filterMode = FilterMode.Point,
applyMipMaps = true,
generateMipMaps = false,
applyPlatformMaxTextureSize = true,
platformMaxTextureSize = 2048,
platformOverrides = new[]
{
new TextureSettingsApplierAPI.PlatformOverride
{
name = "Android",
applyMaxTextureSize = true,
maxTextureSize = 1024,
},
},
};
if (
!TextureSettingsApplierAPI.TryApplyTextureSettings(
assetPaths,
in config,
out int changed,
out bool canceled,
out string error
)
)
{
Debug.LogError(error);
return false;
}
Debug.Log($"Updated {changed} texture importers.");
return !canceled;
}
}Config is a struct with no defaults, so every field you care about must be set explicitly, and the
batch API reimports and saves changed textures. Its optional cancellation callback keeps completed
changes and reports canceled; an error can also leave earlier textures changed. For one texture,
WillTextureSettingsChange and TryUpdateTextureSettings remain available. The latter returns an
importer for the caller to reimport. The default-platform name string is
"DefaultTexturePlatform".
Visual Reference
Texture Settings Applier with filter mode, wrap mode, and compression options
Tools > Wallstop Studios > Unity Helpers > Sprite Pivot Adjuster
A walk cycle where the character leans forward on some frames wobbles if every pivot is (0.5, 0.5),
because the geometric center of the texture is not the visual center of the character. This computes
the center of pixels above the alpha cutoff and writes it as a custom pivot.
- Add
Assets/Sprites/Characters/Playerto Input Directories. - Leave Alpha Cutoff at
0.01so anti-aliased fringe pixels do not drag the pivot outward. - Leave Skip Unchanged (fuzzy) on — it avoids reimporting when the pivot moves less than
0.001. - Click Find Sprites To Process, then Dry Run to see the counts, then Adjust Pivots in Directory.
Editor scripts can call SpritePivotAdjusterAPI.TryFind(folders, nameRegex, paths, out error)
with Assets/... folders, then call SpritePivotAdjusterAPI.Run(paths, options) to preview or
Run(paths, options, applyChanges: true) to apply. The result reports changes, skips, cancellation,
warnings, and errors. These calls normalize backslashes, trailing folder slashes, and the Assets
prefix casing. Invalid paths are counted as skipped and reported in the result errors. Neither call
opens the window.
Each changed importer is recorded as an Adjust Sprite Pivot undo step. The reimport side effect
may require regeneration after Undo.
Center-of-mass scans run directly below 65,536 pixels and for one-row sprites. Larger scans use
parallel row partitions, so small sprite batches avoid worker startup without slowing large art.
Folder and file-extension filtering also scans directly without per-file predicate allocations.
Before you run it:
- Single-sprite textures only.
- Textures without
Read/Write Enabledare skipped, not fixed. Turn Read/Write on with Texture Settings Applier first. - This window shares its remembered directory list with Sprite Cropper.
Visual Reference
Sprite Pivot Adjuster with alpha cutoff slider and directory selection
Tools > Wallstop Studios > Unity Helpers > Sprite Settings Applier
Same idea as Texture Settings Applier, but sprite-specific (pixels per
unit, pivot, sprite mode, extrude) and driven by profiles — so ui_ sprites can import at 100 PPU
with bilinear filtering while everything in Assets/Sprites/World imports at 16 PPU with point
filtering, in one pass.
- Drag
Assets/Spritesinto Directory Sources. - Under Sprite Settings Profiles, add one profile per rule. Each has Match By
(
Any,NameContains,PathContains,Regex,Extension), Match Pattern, and a Priority — the highest matching priority wins. - Tick only the
Apply ...toggles you want; each one reveals its value field. - Calculate Stats, then Apply Settings to Sprites.
Save Profiles Asset writes the rule set to a SpriteSettingsProfileCollection asset so the rest
of the team gets the same rules; Load Profiles Asset reads one back. There is no
Assets > Create entry for that asset — the button is how you make one.
Editor scripts can save and load the same profile assets without opening the window:
using System.Collections.Generic;
using UnityEngine;
using WallstopStudios.UnityHelpers.Editor.Sprites;
public static class SpriteProfilePersistenceExample
{
public static List<SpriteSettings> SaveAndLoad()
{
List<SpriteSettings> profiles = new() { new SpriteSettings { name = "World" } };
if (!SpriteSettingsApplierAPI.TrySaveProfiles(
"Assets/Settings/SpriteProfiles.asset", profiles, false, out string error))
{
Debug.LogError(error);
return profiles;
}
if (SpriteSettingsApplierAPI.TryLoadProfiles(
"Assets/Settings/SpriteProfiles.asset", out List<SpriteSettings> loaded, out error))
{
return loaded;
}
Debug.LogError(error);
return profiles;
}
}The path must be a .asset file in an existing folder under Assets. Saving refuses an occupied
path unless overwriteExisting is true, and never replaces an asset of another type. Saved and
loaded profiles are separate copies. A new save stages its asset and refuses a destination that
becomes occupied before the move. Overwrite refuses a file whose bytes changed before replacement.
Noncooperating external writers can still race with replacement or staging cleanup (tracked in #863). Asset
file writes and reimports cannot be fully reversed by Unity Undo; use version control to recover an
earlier saved asset. A failed save leaves its staged asset in place because another writer may have
changed it; check the reported path before retrying or removing it. If restoring an absent file
fails, the error also identifies the staged restore bytes for inspection.
using System.Collections.Generic;
using UnityEngine;
using WallstopStudios.UnityHelpers.Editor.Sprites;
public static class SpriteImportStandard
{
private static readonly List<SpriteSettings> Profiles = new()
{
new SpriteSettings
{
matchBy = SpriteSettings.MatchMode.PathContains,
matchPattern = "sprites/world",
priority = 1,
applyPixelsPerUnit = true,
pixelsPerUnit = 16,
applyFilterMode = true,
filterMode = FilterMode.Point,
},
new SpriteSettings
{
matchBy = SpriteSettings.MatchMode.NameContains,
matchPattern = "ui_",
priority = 5,
applyPixelsPerUnit = true,
pixelsPerUnit = 100,
applyFilterMode = true,
filterMode = FilterMode.Bilinear,
},
};
public static bool ApplyTo(
IReadOnlyList<string> assetPaths,
out int changed,
out bool canceled)
{
bool succeeded = SpriteSettingsApplierAPI.TryApplyProfiles(
assetPaths,
Profiles,
out changed,
out canceled,
out string error);
if (!succeeded)
{
Debug.LogError(error);
}
return succeeded;
}
}The batch API reimports and saves changed sprites. An optional cancellation callback keeps completed
changes and reports canceled; an error can also leave earlier sprites changed. For one sprite,
PrepareProfiles, WillTextureSettingsChange, and TryUpdateTextureSettings remain available;
the latter returns an importer for the caller to reimport. NameContains matches the file name,
PathContains and Regex match the full asset path, and Regex is always case-insensitive.
Applying a pivot also forces Sprite Alignment to Custom.
Visual Reference
Sprite Settings Applier with profile matching modes and import settings
Tools > Wallstop Studios > Unity Helpers > Texture Resizer
Scales PNGs up on disk. Reach for this when the source art is genuinely too small for the resolution you ship at and you want the bigger pixels baked into the file rather than paid for at runtime.
- Drag a folder into Texture Source Paths, or add textures to Textures.
- Pick Scaling Resize Algorithm:
Pointfor pixel art (no blur),Bilinearfor everything else. - Set Num Resizes — each pass grows the texture by
width / (Pixels Per Unit * Width Multiplier)(and the same for height), so several small passes scale more gently than one large one. - Tick Dry Run and click Resize — the console logs
[DryRun] Would resize <name> to [WxH]for every match. This reads texture dimensions without resizing the loaded texture, reading pixel data, or changing importer settings. - Clear Dry Run and click Resize for real.
Editor scripts can call TextureResizerAPI.TryResizeTextures with selected textures, source
asset-folder paths, sizing settings, an optional output asset-folder path, and a dry-run flag. The
call does not open a window or ask for input. It returns false for invalid settings, a destination
collision, a failed resize, a failed importer restore, or cancellation. Pass null for the output
folder to overwrite the source PNGs; an empty string is invalid. For example, this Editor script
previews one asset folder:
using WallstopStudios.UnityHelpers.Editor.Sprites;
public static class TextureResizeExample
{
public static bool Preview()
{
return TextureResizerAPI.TryResizeTextures(
null,
new[] { "Assets/Art/Sprites" },
1,
TextureResizerWizard.ResizeAlgorithm.Point,
100,
1f,
1f,
"Assets/Art/Resized",
true
);
}
}Blank source folder entries are ignored; other invalid source or output folders are rejected before
any texture changes. Overwrite mode accepts
only textures under Assets/; package textures can be copied into an explicit Assets/ output
folder. Textures are processed in natural name order, so Sprite2 precedes Sprite10. The dry run reports target sizes without changing files or importer settings. A real run
writes PNG files, so Unity Undo cannot fully reverse it. A failed or canceled run may leave earlier
textures resized.
Before you run it:
- Only
.pngfiles are processed; anything else is counted as skipped. - With Output Folder empty this overwrites the originals in place and there is no undo. Set Output Folder to write copies instead.
- Invalid sizing settings and output-name collisions are rejected before any source, importer, or destination file changes. Texture names are compared without regard to case because Unity asset paths must remain portable across project platforms.
- PNG writes are staged before replacement. A failed write reports an error rather than leaving a partly written PNG.
- A failed PNG encode reports an error and leaves the original image unchanged.
- If PNG encoding fails, the original image remains unchanged.
- The stock multipliers (
0.54width,0.245height) grow a texture non-uniformly — a 128x128 becomes 130x133 in one pass. Set both to the same value if you want a square scale. - The final size is clamped to 16384 on each axis.
Visual Reference
Texture Resizer with bilinear/point algorithm selection and multiplier settings
Tools > Wallstop Studios > Unity Helpers > Fit Texture Size
Every texture in a fresh project imports at Max Size: 2048, whatever its real dimensions. A 64x64
icon wastes an import slot; a 4096-wide sky is silently halved. This reads each source file's actual
dimensions and sets Max Size to the power of two that fits.
Closing the window releases its serialized editor state; reopening binds fresh state.
-
Add
Assets/Spritesto the source list (the window pre-fills it if that folder exists). -
Choose a Fit Mode:
Mode Effect GrowAndShrinkSmallest power of two that fits the source. The usual choice. GrowOnlyRaise Max Sizewhen the source is bigger; never lower it.ShrinkOnlyLower Max Sizeto the tightest fit; never raise it.RoundToNearestNearest power of two to the source size, ties rounding up. -
Narrow the run if you want to: Only Current Selection, Only Sprites, Name Filter (substring, or a regex with Use Regex), Label Filter (CSV).
-
Set Min Allowed Size / Max Allowed Size to keep results inside a range (defaults
32and8192). -
Click Calculate Potential Changes. You get
N textures would be modified ... Grows: G, Shrinks: S, Unchanged: U. -
Click Run Fit Texture Size.
A 1920x1080 source under GrowAndShrink lands on 2048; a 64x64 source under ShrinkOnly lands on
64.
Import settings only, recorded as a Fit Texture Size undo step per asset, and cancellable from the
progress bar. Apply to Standalone / Android / iOS additionally writes a platform override with
the same size. Fit Mode is not persisted across a domain reload — re-select it after a
recompile.
Scripts and batch-mode jobs can run the same operation without opening the window. Folder paths accept forward or back slashes:
List<string> textureGuids = new();
if (FitTextureSizeAPI.TryFindTextures(new[] { "Assets/Sprites" }, true, textureGuids, out string error))
{
FitTextureSizeAPI.Options options = new() { FitMode = FitMode.GrowAndShrink, OnlySprites = true };
FitTextureSizeAPI.Result preview = FitTextureSizeAPI.Run(textureGuids, options, false);
if (!preview.Succeeded)
{
Debug.LogError(preview.Error);
}
else if (preview.Changed > 0)
{
FitTextureSizeAPI.Result applied = FitTextureSizeAPI.Run(textureGuids, options, true);
if (!applied.Succeeded)
{
Debug.LogError(applied.Error);
}
}
}
else
{
Debug.LogError(error);
}Run reports counts, cancellation, and errors; a failed apply may report partial changes. Import
settings writes and reimports cannot be fully reversed by Unity Undo alone. Supply explicit asset
paths or GUIDs instead of relying on the current editor selection.
Visual Reference
Fit Texture Size with GrowAndShrink/GrowOnly/ShrinkOnly mode selection
Tools > Wallstop Studios > Unity Helpers > Sprite Animation Editor
Unity's Animation window is built for curves, and reordering four sprite keyframes in it is more work than it should be. This opens a clip as a list of frames you can drag, with the animation playing next to it.
- Click Browse Clips (Multi)... and pick
PlayerWalk.anim(and any other clips you want open at the same time). - Click the clip in the left panel to make it active. The preview starts playing immediately.
- Drag a frame in the Frames panel to move it, or type a new position into its Order: field and press Enter.
- Set Preview FPS and click Apply Preview FPS to see the new speed.
- Click Save Active Clip.
The Preview FPS field works in Unity 2021.3 and later supported editors.
Before you save: saving writes the preview FPS onto the clip and re-spaces every keyframe to match. That is usually what you want after changing the speed of a walk cycle, but it means you cannot save a reorder while leaving the original timing alone.
Editor scripts can update an existing clip without opening the window:
bool saved = AnimationClipFrameSaveAPI.TrySaveFrames(
clip,
new[] { idle0, idle1, idle2 },
12f,
"Character",
out bool usedFallbackBinding,
out string error
);The preferred path selects a SpriteRenderer sprite curve; an empty string selects a curve on the
root object. If the path is absent, the method uses the first sprite curve and sets
usedFallbackBinding. Pass null to select the first curve without a path preference. The method
requires a standalone .anim asset, rejects invalid frames or FPS before editing, preserves other
curves, events, and clip settings, records Undo, and saves the clip asset. Undo restores the clip in
memory; the asset write itself is not fully reversible through Unity Undo.
This is an editor for frame order and speed. Adding or removing frames is
Animation Creator's job; the X next to a loaded clip closes it in this window
rather than deleting anything. One clip previews at a time.
Visual Demo
Drag-and-drop frame reordering with real-time preview updates
Adjusting FPS and seeing immediate preview speed change
Tools > Wallstop Studios > Unity Helpers > Animation Creator
An artist hands you Player_Idle_0.png through Player_Attack_11.png — sixty files, eight
animations. Making those clips by hand is an hour of dragging, and Unity's default sort puts frame 10
before frame 2. This groups the sprites by name and writes the clips.
- Add
Assets/Sprites/Playeras a source folder. - Leave Sprite Name Regex at
.*, or narrow it (^Player_Attack) to build one clip. - Click Generate Auto-Parse Preview to see the groups it found before anything is created.
- Click Auto-Parse Matched Sprites into Animations. You get one entry per group under Animation Data.
- Set FPS and Loop per clip.
- Click Create Animations.
These naming patterns are detected without any configuration:
Player_Idle_0.png, Player_Idle_1.png -> Player_Idle, frames 0..N
slime-walk-01.png, slime-walk-02.png -> slime-walk, frames 1..N
Mage/Attack (0).png, Mage/Attack (1).png -> Attack, frames 0..N
@2x density suffixes are stripped before grouping.
If your files do not fit any of those, tick Enable Custom Group Regex and supply one with base
and index named groups. Use the Regex Tester box to check it against a real file name before
applying:
^(?<base>.*?)(?:_|\s|-)?(?<index>\d+)$ # base plus trailing digits
^Enemy_(?<base>Walk)_(?<index>\d+)$ # only Enemy_Walk_N
Where clips land: each clip is written next to the first sprite in its group, with a unique name.
There is no output folder picker — use Animation Copier to move a generated set
into Assets/Animations.
Editor scripts can call AnimationCreatorAPI.TryCreateClip(data, frames, out clip, out error) to
build a clip in the supplied frame order without writing an asset. Call
AnimationCreatorAPI.TryCreateAsset(data, out path, out error) to ignore null frames, naturally
sort the remaining sprites, and save a uniquely named .anim beside the first sprite. The
sprite must already be an asset under Assets. When creating several clips inside an
AssetDatabaseBatchHelper scope, pass saveAssets: false and call AssetDatabase.SaveAssets()
after the batch.
If saving fails after asset creation, TryCreateAsset returns false with the created path so the
caller can inspect or remove it.
Also worth knowing:
-
Prefix Leaf Folder Name / Prefix Full Folder Path keep
Idlefrom four different characters from colliding, and Resolve Duplicate Animation Names (on by default) renames any that still do. - Frames are ordered by Unity's natural sort at creation time, so
2sorts before10. -
Framerate Mode is
Constant(one FPS, the default at12) orCurve, where anAnimationCurvedrives FPS across the clip — the Flat / Ease In / Ease Out / Sync buttons give you a starting shape. UseCurvefor an attack that snaps and then holds. - Preview plays a clip entry before you create anything.
-
Configuration Persistence saves the whole window state to
.animation-creator.jsonin a source folder, so the next person to import that folder gets your settings. A staging failure keeps the previous config; on platforms withoutFile.Replace, a failed fallback swap can still lose it.
Visual Demo
One-click auto-parse: sprites grouped by naming pattern, clips generated instantly
Next: adjust the results in Sprite Animation Editor, or add events with Animation Event Editor.
Tools > Wallstop Studios > Unity Helpers > Animation Copier
Animation Creator leaves clips beside their sprites, but you keep clips in
Assets/Animations. Re-running it after an art update produces a mix of genuinely new clips,
genuinely changed clips, and dozens that are byte-for-byte what you already have. This tells the three
apart and moves only what matters.
- Set Source Path (defaults to
Assets/Sprites) and Destination Path (defaults toAssets/Animations). - Click Analyze Source & Destination.
- Read the counts: - New:, - Changed:, - Unchanged (Duplicates):.
- Expand New and Changed to review, using Filter (plus Regex) and Select All / Select None to pick what moves.
- Tick Dry Run (no changes) and run the copy once to see what it would do.
- Clear it, then click Copy New (N) or Copy Changed (N).
Dry runs leave destination files and folders untouched, including subfolders that a real copy would create for nested source clips.
Scripts and batch jobs can use AnimationCopierAPI.TryAnalyze with explicit source and destination
folders to receive source entries and destination orphans. Pass selected source paths to Run for
CopyNew, CopyChanged, CopyAll, or DeleteUnchangedSource; pass selected destination paths
for DeleteDestinationOrphans. Set applyChanges: false to preview eligible operations and
applyChanges: true to perform them. The result reports processed, skipped, and failed counts,
cancellation, and per-path diagnostics. The API makes no prompts, rechecks each selected clip before
an operation, accepts standalone .anim files under distinct non-overlapping Assets folders,
and never creates folders during a preview.
Copying a changed clip preserves its GUID, so every Animator that already references it keeps working.
Enable Include Unchanged in Copy All (force replace) when you need to refresh selected clips that the last analysis classified as unchanged. Copy All then replaces those destination files too and preserves their GUIDs.
"Changed" is decided by comparing clip contents field by field — frame rate, length, wrap mode, every curve key, every event and its parameters — not by an asset hash, so a re-import that produces an identical clip does not show up as a change.
Cleanup is where the destructive buttons live: Delete N Unchanged Source Duplicates removes the redundant copies left behind in the source folder, and Mirror Delete Destination Orphans (N) deletes destination clips with no source any more. Both honour Dry Run. Export Preview Report writes the analysis to a file if you would rather review it outside the editor. The complete report is staged before replacing an existing file. Copy, delete, and reimport operations change files on disk and cannot be fully reversed through Unity Undo.
Visual Reference
Animation Copier with new/changed/unchanged/orphan groups and copy actions
To read authored sprite keyframes or find the last bounds movement in a clip, see Sprite animation motion.
Tools > Wallstop Studios > Unity Helpers > Sprite Sheet Animation Creator
Your character is one hero.png sliced into 48 sprites in the Sprite Editor, and frames 0-7 are the
idle, 8-15 the run, 16-23 the attack. This lets you select those ranges visually and turn each into a
clip.
- Assign the sliced texture to Sprite Sheet, or click Load Sprites and pick the file.
- Drag across the thumbnails to select frames 0-7.
- Click Add Animation Definition. Name it
Hero_Idle. - Set Default FPS:, Looping: and Cycle Offset:, then hit Preview This and use ▶ Play to check it.
- Repeat for the run and attack ranges — the Start Idx: / End Idx: fields let you correct a selection by hand.
- Click Generate Animation Files and choose an output folder.
FPS Curve: takes an AnimationCurve instead of a flat rate, so an attack can hold on its impact
frame. The curve changes how far apart keyframes are placed; the generated clip's own frame rate is
always 60, which is what lets fractional timings land cleanly.
Editor scripts can call SpriteSheetAnimationAPI.TryCreate(outputFolder, name, frames, defaultFrameRate, frameRateCurve, loop, cycleOffset, dryRun, out assetPath, out error) with ordered
sprite frames and an Assets/ output folder or an existing Packages/ folder. It generates a
unique .anim name and normalizes the Assets prefix casing. Set dryRun: true
to preview the path without creating folders or assets. A successful creation saves the asset;
Unity Undo cannot reverse the file write. For several clips, pass saveAssets: false and call
AssetDatabase.SaveAssets() once after the batch. If saving fails after creation, the call returns
false and retains the created asset path in assetPath so the caller can inspect or remove it.
Call SpriteSheetAnimationAPI.TryDiscoverFrames(texture, configureImporter, frames, out error) to
load sprite frames into a caller-owned list without opening the window. It clears the list first and
returns frames sorted by name. Pass configureImporter: false to leave a texture with unsuitable
import settings untouched and receive an error. Pass true to change the texture to Sprite type
and Multiple sprite mode and reimport it; that asset change cannot be fully undone.
The texture must already be sliced (Sprite Mode: Multiple) — this window reads Unity's sprites, it
does not slice for you. Declining the window's importer prompt leaves its settings unchanged.
Visual Demo
Drag-select frame ranges on sprite sheet thumbnails with instant preview playback
Tools > Wallstop Studios > Unity Helpers > Sprite Sheet Extractor
The reverse of the tool above: take a packed sheet and write each sprite out as its own PNG. Needed whenever something downstream wants files rather than sub-assets — a third-party animation tool, or Sprite Cropper, which only handles single-sprite textures.
-
Add the folder holding your sheets to Input Directories.
-
Pick a Mode:
Mode Use when FromMetadataThe sheet is already sliced in Unity. Keeps names, rects, pivots and borders. GridBasedEven cells. Set Columns/Rows or leave Grid Size Mode on Auto.PaddedGridEven cells with gutters. Set Left/Right/Top/Bottom padding. AlphaDetectionSprites are scattered irregularly. Tune Alpha Threshold (default 0.01). -
Click Find Sprite Sheets, then Preview Slicing to see the detected rects drawn over the texture (Show Overlay).
-
Choose a Pivot Mode —
Center, the eight edge and corner presets, orCustomwith explicit X / Y. -
Set Output Directory, then click Extract N Sprite(s).
Scripts and batch jobs can call SpriteSheetExtractionAPI.Extract with explicit source and output
asset paths, a pixel rectangle, normalized pivot, and border. The output folder must already exist
under Assets or a writable local Packages path. The API returns extracted and skipped counts
plus errors, and never opens a window or displays a prompt. Set dryRun: true to preview the count
without changing files or importers.
With the default overwriteExisting: false, an output that appears during extraction is skipped
and left unchanged. With overwriteExisting: true, the encoded PNG is staged before replacing the
existing file. New outputs are published from a complete staged image in the same directory.
New output publication shares destination ownership with DurableFile writes. If another writer
holds that destination, extraction reports an error without publishing the staged image.
Extraction attempts to remove its temporary file if publishing fails; a failed publication does not
expose a partial new output. On Linux and macOS, the output filesystem must support hard links.
If temporary-file cleanup fails, the result reports that error alongside the extraction failure.
On Linux and macOS, if cleanup fails after publication, the result still counts and imports the
output and includes the cleanup error in Errors.
Use SpriteSheetExtractionAPI.Discover with folder asset paths and an optional filename regex to
get the same sprite texture list as the window. Its result includes warnings for invalid folders
and an error for an invalid or timed-out regex. Folder paths accept forward or back slashes.
SpriteSheetReferenceReplacementAPI.Run accepts an explicit map from source sprites to extracted
sprites and explicit asset paths. It previews matching references by default; pass
applyChanges: true to write them. The window uses this API after its Danger Zone confirmation.
Unity records object changes for Undo, but saved asset edits should still be protected by version
control.
using UnityEngine;
using WallstopStudios.UnityHelpers.Editor.Sprites;
SpriteSheetExtractionRequest request = new(
"Assets/Sprites/characters.png",
"Assets/Extracted/hero.png",
new Rect(0, 0, 32, 32),
new Vector2(0.5f, 0.5f),
Vector4.zero
);
SpriteSheetExtractionResult result = SpriteSheetExtractionAPI.Extract(new[] { request });Auto-detection: with Grid Size Mode on Auto, the Algorithm dropdown picks how cell size
is inferred: AutoBest (tries each and stops once one reaches 90% confidence), UniformGrid,
BoundaryScoring, ClusterCentroid, DistanceTransform or RegionGrowing. Filling in
Expected Sprite Count dramatically improves the result, and Snap to Divisor keeps the cell
size an exact divisor of the texture.
Alpha detection rejects NaN and infinite thresholds without producing sprite bounds or a grid.
Transparency-based grid detection requires a threshold in [0, 1).
Also worth knowing:
-
Dry Run turns the extract button into
Dry Run: Preview N Sprite(s)and writes nothing. - Per-sheet settings override the global ones; Apply Global to All pushes yours down, and
Save Config writes a
<texture>.spritesheet.jsonbeside the sheet so a re-extraction is reproducible. A staging failure keeps the previous config; on platforms withoutFile.Replace, a failed fallback swap can still lose it. AConfig Stalebadge appears when the texture has changed since. - Preserve Import Settings (on by default) copies the source's importer settings to each output.
- Preview and extracted output reuse dynamically sized pixel buffers instead of retaining one permanent pool bucket for every sprite size.
- The Danger Zone: Reference Replacement section points existing assets at the extracted sprites. Like the one in Sprite Cropper, it requires ticking "I understand the risks and want to proceed." and it is not undoable.
Tools > Wallstop Studios > Unity Helpers > AnimationEvent Editor
Editor scripts can save events without opening the window. Call
AnimationEventSaveAPI.TrySave(clip, events, frameRate, out error) with an explicit clip and event
array. Pass null for frameRate to keep the clip's current rate. The call records Unity Undo,
marks the clip dirty, and saves the asset. It returns false with an error for invalid input or a
failed save. Unity Undo covers clip changes; external asset save side effects may not be fully
reversible.
Putting a footstep sound on frame 4 of a run cycle in Unity's Animation window means finding the right time value and typing a method name from memory. This shows the sprite at each event, lists the methods that are actually callable, and edits the parameter with the right field type.
Animation Creator and Animation Event Editor retain at most 128 sprite previews per window, within a 32 MiB estimated texture-storage budget. The estimate includes mip levels, CPU/GPU copies and a per-texture allowance; it is not a measurement of total editor or driver memory. Recently used previews stay cached, and Unity-destroyed previews are rebuilt when needed.
Changing clips or closing Animation Event Editor releases copied previews. Eviction releases only textures the window created; Unity's shared previews remain owned by Unity. A readable sprite too large to copy within the budget is drawn directly from its source rectangle at the same resolution. A failed preview copy releases its temporary texture.
First, mark the methods you want to fire:
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.Attributes;
public enum Stance { Idle, Crouched, Guarding }
public sealed class PlayerAnimationEvents : MonoBehaviour
{
[AnimationEvent]
public void PlayFootstep() { }
[AnimationEvent]
public void SpawnSlashVfx(string socketName) { }
[AnimationEvent]
public void EnableHitbox(int hitboxIndex) { }
[AnimationEvent]
public void SetStance(Stance stance) { }
}Then:
- Drag the GameObject holding the
Animatorinto Animator Object. - Pick the clip from Animation (use Animation Search to filter a long list).
- Type
4into FrameIndex and click Add Event. - Choose TypeName
PlayerAnimationEvents, then MethodNamePlayFootstep. - Click Save.
Method signatures Unity accepts — and therefore the only ones this window lists — return void
and take either no parameter or exactly one of: int, float, string, UnityEngine.Object, or an
enum. UnityEngine.Object means that exact type; a method taking GameObject or Sprite will not
appear.
Explicit Mode is on by default and lists only [AnimationEvent] methods, which is what keeps the
dropdown readable in a real project. Turn it off to see every signature-valid method on every
MonoBehaviour (private ones included) and use Type Search / Method Search to narrow.
By default an [AnimationEvent] method is offered on its declaring class only. Use
[AnimationEvent(ignoreDerived = false)] when subclasses should offer it too.
Also worth knowing:
- Control Frame Time off (the default) means you work in whole frames; on, you edit the raw time value.
- The sprite preview needs
Read/Write Enabledon the texture. When it is missing you get a Fix button that turns it on. - Move Up / Move Down swap events that sit at the same time; Re-Order sorts everything by time. Duplicate copies an event, and Reset reverts to the last saved state.
- The Save / Reset / Re-Order row only appears once you have unsaved changes; otherwise the window
says
No changes detected.... - Shortcuts:
Deleteremoves the focused event,Ctrl+Dduplicates it, arrow keys navigate. The duplicate shortcut isCtrlonly, including on macOS.
Tools > Wallstop Studios > Unity Helpers > Sprite Atlas Generator
A .spriteatlas asset holds a hand-maintained list of sprites, so every new sprite an artist adds is
one someone has to remember to drag in. This drives the atlas from a rule instead — a regex, an asset
label, or both — and rescans on demand.
Project changes rebuild the serialized editor state and drop removed configurations from the foldout cache while preserving surviving scan results. Refreshes sort configurations in place, and source-sprite checks reuse temporary storage, so large atlas lists do not build query pipelines. Closing the window releases its cached state.
-
Click Create New Config in 'Assets/Data'. That makes a
ScriptableSpriteAtlasasset (you can also useAssets > Create > Wallstop Studios > Unity Helpers > Scriptable Sprite Atlas Config). -
Set Output Sprite Atlas Directory and Output Sprite Atlas Name — the atlas is written to
<directory>/<name>.spriteatlas. -
Click Add New Source Folder Entry and point it at
Assets/Sprites/Characters. -
Choose how sprites are selected:
Selection Mode What it matches RegexEvery pattern in Regexes (AND logic) must match the asset path. LabelsAsset labels, combined by Label Selection Mode: All(every label) orAnyOf(any one).Regex | LabelsBoth, joined by Regex & Tags Logic ( And/Or). -
Click Scan Folders for ''. New folder entries start with Regex selection and no patterns, so they include sprites until you narrow them. The window reports
To Add: N sprites.andTo Remove: N sprites.before anything changes. -
Click Sync List To Scan Result (N add, N remove).
-
Click Generate/Update '.spriteatlas' ONLY, then Pack All Generated Sprite Atlases — or Generate + Pack to do both.
Scripts and batch jobs can use the same configuration without opening the window. Call
ScriptableSpriteAtlasGenerator.TryCreateConfig("Assets/Data/CharacterAtlas.asset", out config, out error)
to create a config at an explicit path outside an active asset batch. The parent folder must exist;
the API refuses an occupied
path at preflight. External writers are not locked out between that check and Unity asset creation.
The window chooses a unique path before calling this API.
Creation writes an asset file, which Unity Undo cannot fully reverse. If finalization fails after
creation, the returned config remains at that path for inspection.
Call
ScriptableSpriteAtlasGenerator.Scan(config, toAdd, toRemove) to preview folder changes, then
Synchronize(config, toAdd, toRemove) to add found sprites while keeping existing manual entries.
Source folder paths in the config accept forward or back slashes.
If a source folder or filter is invalid, Scan returns false with empty results; fix the config
before synchronizing.
Pass removeUnmatchedSprites: true to remove every sprite absent from the scanned folders, including
manual entries. The window's Sync List To Scan Result button uses that removal mode. Generate(config) writes one atlas
and returns whether it changed; GenerateAll() writes all configured atlases in one asset batch and
returns the number changed, or -1 if any config failed. TryGenerateAll(out changed) also reports
whether every config was valid.
PackAll(target) packs for a chosen build target. A CI check can call
TryFindDrift(config, differences) and fail if it returns false or adds any differences. It checks
packables and generated settings without changing the atlas. To regenerate and pack in Unity batch
mode, use -executeMethod WallstopStudios.UnityHelpers.Editor.Sprites.ScriptableSpriteAtlasGenerator.GenerateAndPackAllForBatch.
That command skips packing and exits with code 1 if a config has an invalid or occupied output path.
Generation and packing write assets and may trigger imports; Unity Undo cannot reverse all file and
import effects.
ScriptableSpriteAtlasGenerator.TrySetSourceTexturesUncompressed(config, applyChanges, out count, out error)
previews how many distinct source textures need uncompressed importer settings when applyChanges
is false, and applies those settings without a window or prompt when it is true. It disables
crunch and compression, sets the default platform to RGB24 or RGBA32 based on source alpha, and leaves PNG bytes
untouched. Applying reimports textures, so Unity Undo cannot fully reverse the import effects.
If a later texture fails, the call returns false with an error and the count of textures whose
final reimport succeeded. Earlier changes can remain in place, and a failed import may need
inspection. The API also reports failure if Unity reimports a texture but its resulting settings
do not match the requested values.
A character atlas that picks up every new idle frame automatically:
Folder Path: Assets/Sprites/Characters
Selection Mode: Regex
Regexes (AND logic): ["player_", "_idle_"]
Max Texture Size: 2048
Padding: 4
Compression: CompressedHQ
A UI atlas driven by labels, so artists opt sprites in from the Inspector rather than by filename:
Folder Path: Assets/Sprites/UI
Selection Mode: Labels
Label Selection Mode: AnyOf
Labels: ["icon", "hud"]
Max Texture Size: 1024
Padding: 2
The Labels fields are dropdowns populated from every label in the project — see Sprite Label Processor for what keeps that list current.
Also worth knowing:
- Each source folder entry has an exclusion block — Exclude Regexes (OR logic), Exclude Labels and Exclude Path Prefixes — for keeping work-in-progress or reference art out of a shipped atlas.
- Platform Overrides on the config set per-platform max size and compression for Standalone, iPhone and Android independently.
- Force Uncompressed for N Source Sprites in '' sets the source sprites to uncompressed so the packer has full-quality input. It asks for confirmation first, and it changes those source assets.
- Regex matching is case-insensitive. A pattern that fails to compile is logged and matches nothing.
Visual Reference
Sprite Atlas Generator with regex-based sprite selection and packing settings
Scanning folders and previewing which sprites will be added to the atlas
Tools > Wallstop Studios > Unity Helpers > Asset Validation
A dockable window over your own project checks. Write an IValidationRule -- "every enemy prefab has
a spawn point", "no AudioClip is set to Decompress On Load" -- and this runs them across the whole
project a few milliseconds per editor tick, rather than freezing for thirty seconds.
- Click Validate Project. A counter beside the button shows progress; the button becomes Cancel.
- Findings are colored by severity. Click one to select and ping the asset.
- Narrow the list: the search box matches the rule, path, discriminator and message; At least cycles the severity floor through Info, Warning and Error; Show suppressed is on by default.
-
Suppress Selected appends the finding to
ValidationSuppressions.txt, the same file the headless run reads.
Tick Re-check on import and an import re-validates only the assets it touched. It is off by default and stored per user, because whether the cost is worth paying is a fact about a workstation rather than about a repository.
The summary line distinguishes nothing checked yet from checked, and clean -- an empty list alone would report a project as healthy on the strength of never having looked at it.
Writing rules, the headless -executeMethod run, and the suppression file format are covered in
Asset Validation.
Tools > Wallstop Studios > Unity Helpers > Prefab Checker
Someone deletes a script, and forty prefabs quietly acquire a "Missing (Mono Script)" slot that only shows up when a scene loads. Prefab Checker walks every prefab under a folder and logs the problems with clickable links.
- Click Add Folder and pick
Assets/Prefabs(already added for you if that folder exists). - Leave the default checks on and click Run Checks.
- Click a console line to select the offending prefab.
| Check | Reports | Default |
|---|---|---|
| Missing Scripts | Components whose script asset is gone | On |
| Nulls in Lists/Arrays |
null elements inside a serialized list or array |
On |
| Missing Required Components | A [RequireComponent] dependency that is not on the prefab |
On |
| Null Object References | Unassigned UnityEngine.Object fields |
On |
| Only if [ValidateAssignment] | Narrows the check above to annotated fields only | On |
| Disabled Root GameObject | Prefab roots saved inactive | On |
| Empty String Fields | Serialized string fields left empty |
Off |
| Disabled Components |
Behaviour components saved disabled |
Off |
Everything except Empty String Fields is logged as an error; empty strings are warnings.
Editor scripts can scan without opening the window. Pass project-relative folder paths and explicit
options to PrefabChecker.ScanFolders; the returned result includes counts, any input error, and
findings grouped by prefab path. This call shows no dialogs, records no folder history, and makes no
asset changes. It uses the same default checks as the window; set the options you need before the
call. The window still logs findings and can export its last report.
using WallstopStudios.UnityHelpers.Editor;
PrefabChecker.ScanOptions options = new PrefabChecker.ScanOptions
{
CheckDisabledComponents = true,
};
PrefabChecker.ScanResult result = PrefabChecker.ScanFolders(
new[] { "Assets/Prefabs" },
options
);
if (result.Error == null)
{
foreach (PrefabChecker.ScanReport.Item item in result.Report.items)
{
UnityEngine.Debug.Log($"{item.path}: {string.Join("; ", item.messages)}");
}
}Narrowing a run: Include Labels (comma) and Exclude Labels (comma) filter by asset label, and Deny Component Types (comma names) skips those component types during checks (a debug-only behaviour, say).
Fixing and reporting: Fix Missing Scripts strips dead component slots, but it stays disabled
until you tick Enable Auto-fix options — the gate is deliberate, because the fix deletes data.
Editor scripts can call PrefabChecker.TryRemoveMissingScripts with explicit Assets folders. A
dry run counts affected prefabs and missing scripts without changing assets; an apply run removes
missing slots from prefab roots and children. The call opens no window or dialog, and reports an
error through its final output parameter. Folder paths accept forward or back slashes. It may leave
earlier prefabs repaired if a later prefab fails. Applying the repair writes prefab files, which
Unity Undo cannot fully reverse.
bool repaired = PrefabChecker.TryRemoveMissingScripts(
new[] { "Assets/Prefabs" },
false,
out int changedPrefabs,
out int removedScripts,
out string repairError
);Export Report (JSON) and Export Report (CSV) write the same findings to a file for a build step or a review. The complete report is staged before replacing an existing file.
Annotate the fields you actually care about so the null check stays useful:
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.Attributes;
public sealed class Turret : MonoBehaviour
{
[ValidateAssignment]
public Transform muzzle;
[ValidateAssignment]
public GameObject projectilePrefab;
// Not annotated: optional, and a null here is not a bug.
public AudioClip idleHum;
}Visual Reference
Prefab Checker with configurable validation checks and folder selection
Console output showing detected prefab issues with clickable links
Tools > Wallstop Studios > Unity Helpers > Analyzer Policies
Use this window to make the package's compiler guidance explicit for scripts under Assets:
-
Enable All writes every
WUH001throughWUH018rule asWarning. -
Disable All writes every rule as
None, including diagnostics that are on by default. -
Refresh re-reads
Assets/Default.rulesetand reports missing, duplicate, mixed, malformed, or unknown Unity Helpers policy entries.
The tool preserves rule groups owned by other analyzers. It refuses malformed XML instead of overwriting it. Ruleset writes are staged before replacement, and failures are reported without leaving a partly written ruleset. The ruleset is a project file change outside Unity's undo system, so commit it when the policy should be shared with the team. The window lists every diagnostic with a short explanation; the Analyzer reference has examples and fixes.
Editor scripts and batch mode can call AnalyzerPolicyAPI.TrySetEnabled(true, out string message)
to enable all Unity Helpers policies in Assets/Default.ruleset, or pass false to disable them.
The overload accepting an asset path manages another .ruleset under Assets. Both calls import
the written ruleset immediately and return false with a message when the path, ruleset, write, or
import fails. Ruleset file changes cannot be reversed through Unity Undo.
Tools > Wallstop Studios > Unity Helpers > Unity Method Analyzer
A private void Start() in a subclass whose base class also has private void Start() compiles
cleanly, and Unity calls only one of them. Roslyn reports that family of mistake during compilation; this window presents the compiler diagnostics.
- Compile the project, then select
Assets/Scriptsin the report directories. - Click Refresh Report; use Recompile Scripts when a fresh compiler snapshot is needed.
- Double-click a result to jump to the line.
| It finds | Example |
|---|---|
A missing override
|
A derived method hiding the base one instead of overriding it |
| A wrong lifecycle signature |
OnCollisionEnter(Collider c) where Unity calls OnCollisionEnter(Collision)
|
| A shadowed lifecycle method | Base and derived both declare private void Start()
|
| A static lifecycle method |
static void Awake(), which Unity never calls |
Results group by file, severity or category, and export as JSON or Markdown. Editor scripts can use
UnityMethodAnalyzerReportExportAPI.TryExportMarkdown or TryExportJson with explicit findings,
coverage status, and output path. The status identifies missing coverage or compiler errors; an
empty partial report is not a passing CI gate.
Silence a deliberate case — a test fixture that exists precisely to be wrong — with
[SuppressAnalyzer]:
[SuppressAnalyzer("Test fixture for analyzer validation")]
public sealed class DeliberatelyShadowedStart : BaseBehaviour
{
private void Start() { }
}Full reference: Unity Method Analyzer Guide
Visual Demo
The report displays categorized compiler issues; refresh controls replace the former source scan
Tools > Wallstop Studios > Unity Helpers > Validate Serialized Fields In Selection
Unity declines to serialize some types and declines silently. Select the script and run the command; it names every field that will be empty after the next domain reload, and what to use instead.
public sealed class Loot : ScriptableObject
{
public Dictionary<string, int> drops; // gone on the next domain reload
public (int, float) weightedRoll; // gone, and it IS [Serializable]
}SerializedObject.FindProperty("drops") returns null, JsonUtility.ToJson omits the field, and
nothing is logged. Whatever a designer authored into it is gone, usually discovered from a build.
[Serializable] is not the discriminator, which is what makes the rule hard to work out from
outside: ValueTuple<int, float> carries it and is dropped anyway.
The console tells you the fix:
Loot.drops is declared as Dictionary<string, int>, which Unity does not serialize. Anything
authored into it is gone on the next domain reload. Use SerializableDictionary<string, int>
instead.
| Declared as | Use instead |
|---|---|
Dictionary<TKey, TValue> |
SerializableDictionary<TKey, TValue> |
SortedDictionary<TKey, TValue> |
SerializableSortedDictionary<TKey, TValue> |
HashSet<T> |
SerializableHashSet<T> |
SortedSet<T> |
SerializableSortedSet<T> |
Nullable<T> |
SerializableNullable<T> |
ValueTuple<...>, Tuple<...>
|
SerializableValueTuple<...> |
KeyValuePair<TKey, TValue> |
SerializableValueTuple<TKey, TValue> |
Queue<T>, Stack<T>, LinkedList<T>
|
List<T> |
How it decides: it constructs the type, wraps it in a SerializedObject, and asks which fields
arrived. That is Unity's own answer rather than a model of its rules, so it cannot misreport the
generic user types Unity has serialized since 2020. Every public or [SerializeField] field with
no SerializedProperty is reported, inherited ones included.
Silencing a field: mark it [NonSerialized]. That is the standard way to say "runtime only",
Unity honours it, and so does this.
Why the selection and not the whole project: validating a type means constructing one, and
constructing every type in a project runs the startup half of the project. Select what you just
wrote — a MonoScript, a prefab, a scene object or an asset. A prefab contributes every component on
it.
MatchColliderToSprite reshapes a PolygonCollider2D through its synchronous RebuildCollider()
command. Inspector edits and the MatchColliderToSprite button run that command with a
Match Collider To Sprite undo step for the component and selected collider, including prefab
overrides. Its runtime update loop also rebuilds when the sprite or trace settings change.
Editor OnValidate() only resolves component references. Editor tools that previously called it to
regenerate geometry must call RebuildCollider() and record Undo for both objects first. This keeps
Undo/Redo validation from replacing the polygon Unity just restored. See
MatchColliderToSprite for exact-art
settings and programmatic use.
An auto-generated PolygonCollider2D from a detailed sprite can carry hundreds of points, and Physics
2D pays for every one of them. This inspector shows a single Tolerance field and an Optimize
button that simplifies the outline in place.
Raise Tolerance until the silhouette starts to visibly change, then back off one step. 0.1–0.5
is the usual working range; above 2.0 you are trading shape for point count. Set Tolerance to
0 and click Optimize to restore the original outline — the component keeps it. Optimize is
also recorded as an Optimize Polygon Collider undo step.
See PolygonCollider2DOptimizer for the component.
EnhancedImage extends Unity's Image with an HDR tint and a shape mask, both of which need the
package's BackgroundMask material to do anything. The custom inspector is mostly there to notice
when that material is missing.
- If the component is still on Unity's Default UI Material, a yellow
Incorrect Material Detected - Try Fix? button appears. Clicking it finds and assigns
Shaders/Materials/BackgroundMask-Material.matas aFix EnhancedImage Materialundo step. -
HDR Color multiplies the image; push intensity above
1.0to make it bloom under post-processing. -
Shape Mask takes a
Texture2D. It only does something if the assigned material's shader exposes a_ShapeMasktexture property — the inspector says so in the tooltip rather than failing silently.
See EnhancedImage for the component and its runtime API.
These are the inspector attributes the tools above lean on most. The Inspector documentation covers the full set. Collection inspectors release their cached button textures on script reload and editor exit; the next draw rebuilds the styles as needed.
Tuning an ability means selecting the AbilityConfig asset, editing it, then selecting the character
again to see the result. [WInLineEditor] draws the referenced asset's own inspector underneath the
field so you never leave.
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.Attributes;
public sealed class AbilityConfig : ScriptableObject { }
public sealed class AbilityLoadout : MonoBehaviour
{
// Inherits the project-wide default from Unity Helpers Settings.
[WInLineEditor]
public AbilityConfig primary;
// Always open, no object picker, taller body.
[WInLineEditor(WInLineEditorMode.AlwaysExpanded, inspectorHeight: 260f, drawObjectField: false)]
public AbilityConfig secondary;
// Collapsed by default, with the texture preview turned on.
[WInLineEditor(WInLineEditorMode.FoldoutCollapsed, drawPreview: true, previewHeight: 96f)]
public Texture2D icon;
}| Mode | Behaviour |
|---|---|
UseSettings |
Default. Follows the Inline Editors setting in Project Settings. |
AlwaysExpanded |
Inspector is always drawn. |
FoldoutExpanded |
Foldout, open on first draw. |
FoldoutCollapsed |
Foldout, closed on first draw. |
Constructor parameters, in positional order:
mode, inspectorHeight (default 200, floored at 160), drawPreview (false),
previewHeight (64, floored at 40), drawObjectField (true), drawHeader (true),
enableScrolling (true), minInspectorWidth (520; below this width a horizontal scrollbar
appears, 0 disables that). Prefer named arguments — the order is easy to get wrong.
Field-only. Unlike the other attributes here, [WInLineEditor] targets fields, so on an
auto-property you need [field: WInLineEditor].
It reuses Unity's own editor for the target, so custom inspectors, validation and undo all still work. Foldout animation speed lives in Inspector Settings.
Visual Reference
WInLineEditor with embedded inspector for a ScriptableObject reference with foldout and collapse transitions
Half the fields on a spawner only matter when it is set to burst mode, and showing them the rest of
the time is how designers set the wrong one. [WShowIf] hides a field until its condition holds.
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.Attributes;
public sealed class EnemySpawner : MonoBehaviour
{
public enum SpawnMode
{
Continuous = 1,
Burst = 2,
}
public SpawnMode mode = SpawnMode.Continuous;
[WShowIf(nameof(mode), expectedValues = new object[] { SpawnMode.Burst })]
public int burstSize = 5;
[WShowIf(nameof(mode), inverse: true, expectedValues: new object[] { SpawnMode.Burst })]
public float spawnsPerSecond = 1f;
public bool useSpawnBudget;
[WShowIf(nameof(useSpawnBudget))]
public int maxAlive = 20;
[WShowIf(nameof(maxAlive), WShowIfComparison.GreaterThan, 50)]
public bool warnAboutPerformance;
public string overrideLayerName;
[WShowIf(nameof(overrideLayerName), WShowIfComparison.IsNotNullOrEmpty)]
public int sortingOrder;
}inverse and comparison are constructor arguments (inverse: true), not object-initializer
assignments — inverse = true will not compile. expectedValues accepts either form.
| Comparison | Use for |
|---|---|
Equal, NotEqual
|
Any equatable value; the default is Equal
|
GreaterThan, GreaterThanOrEqual
|
Numbers and any IComparable
|
LessThan, LessThanOrEqual
|
Numbers and any IComparable
|
IsNull, IsNotNull
|
Object references |
IsNullOrEmpty, IsNotNullOrEmpty
|
Strings and collections |
WShowIfComparison.Unknown exists only for backward compatibility and is marked obsolete.
The condition can be a field, a property (including a [field: SerializeField] auto-property), a
parameterless method, or a dotted path into a nested serialized type:
[WShowIf(nameof(damage) + "." + nameof(DamageProfile.isCritical))]
public float criticalMultiplier;Visual Reference
Field appears/disappears based on enum toggle state
A string field that has to match an animator state name or an asset label is a typo waiting to
happen. [StringInList] turns it into a dropdown of the values that are actually valid.
using System.Collections.Generic;
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.Attributes;
using WallstopStudios.UnityHelpers.Core.Helper;
public sealed class SpawnRule : MonoBehaviour
{
// Fixed set.
[StringInList("Idle", "Run", "Jump")]
public string startState = "Idle";
// Every asset label in the project, kept current by the Sprite Label Processor.
[StringInList(typeof(Helpers), nameof(Helpers.GetAllSpriteLabelNames))]
public List<string> requiredLabels = new();
// A provider on this same type.
[StringInList(nameof(GetPoolNames))]
public string poolName;
private IEnumerable<string> GetPoolNames()
{
yield return "Grunt";
yield return "Archer";
yield return "Brute";
}
}That second field is how the package itself populates the label pickers in Sprite Atlas Generator.
A provider method takes no parameters and returns string[] or IEnumerable<string>. It may be
private, and it may be static or an instance method. [StringInList(typeof(T), "Method")] looks for
a static method on T first, then an instance one; [StringInList("Method")] looks on the decorated
object's own type.
- On an
intfield the dropdown selects by index. - On an array or list you get a UI Toolkit list with a dropdown per element, plus add, remove and drag-to-reorder.
- Typing filters the list.
Tabaccepts the highlighted match,Enterjust fills the search box. - Long lists paginate. The page size is
Project Settings ▸ Wallstop Studios ▸ Unity Helpers ▸ StringInList Page Size (default
25), shared with theSerializableTypedrawer.
Visual Reference
StringInList dropdown showing search filtering and pagination
StringInList on a List field with per-element dropdowns and drag reordering
For an int that only has a handful of legal values, a free text field lets someone type 3000 into
a texture size. [IntDropDown] restricts it to the list.
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.Attributes;
public sealed class AtlasSettings : MonoBehaviour
{
[IntDropDown(32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384)]
public int maxTextureSize = 2048;
[IntDropDown(0, 2, 4, 8, 16, 32)]
public int padding = 4;
}Those two fields are lifted from ScriptableSpriteAtlas, which is exactly how
Sprite Atlas Generator keeps its packing settings legal.
The attribute is spelled IntDropDown with a capital D. Like StringInList it also accepts a
provider: [IntDropDown(typeof(T), nameof(T.Method))] or [IntDropDown(nameof(Method))], returning
int[] or IEnumerable<int>.
Visual Reference
IntDropDown for texture sizes showing power-of-two options
StringInList and IntDropDown for everything else — floats, bools, enums, Unity structs, and your
own serializable types.
using System.Collections.Generic;
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.Attributes;
public sealed class WeaponTuning : MonoBehaviour
{
[WValueDropDown(0.5f, 1.0f, 1.5f, 2.0f)]
public float damageMultiplier = 1.0f;
[WValueDropDown("Easy", "Normal", "Hard", "Insane")]
public string difficulty = "Normal";
// Instance provider: the options depend on this component's own state.
[WValueDropDown(nameof(GetMuzzleOffsets), typeof(Vector2))]
public Vector2 muzzleOffset;
public List<Vector2> configuredMuzzles = new();
private IEnumerable<Vector2> GetMuzzleOffsets()
{
return configuredMuzzles;
}
}There is a params overload for every primitive (bool, char, all the integer widths, float,
double, string). For anything else, use a provider:
-
[WValueDropDown(typeof(Library), nameof(Library.GetPresets))]— static provider, element type inferred from the return type. -
[WValueDropDown(nameof(GetOptions), typeof(Vector2))]— instance provider on the decorated type, value type stated. -
[WValueDropDown(typeof(Preset), presetA, presetB)]— an inline list of custom-typed values.
Providers follow the same rules as StringInList: parameterless, may be private, returning an array
or IEnumerable. Labels come from ToString(), so give a custom type a readable one.
Full constructor reference: Inspector Selection Attributes.
Visual Reference
WValueDropDown showing predefined integer, float, and string values
Shows a serialized value in the inspector without letting anyone edit it — useful for a number your code derives and a designer should only ever read.
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.Attributes;
public sealed class Health : MonoBehaviour
{
public int baseHealth = 100;
public int equipmentBonus;
[WReadOnly]
public int totalHealth;
private void OnValidate()
{
totalHealth = baseHealth + equipmentBonus;
}
}No parameters and nothing to configure. It works on any serialized type and keeps the field's normal height and layout.
For attributes that reject a bad value rather than just displaying it, see Inspector Validation Attributes.
Visual Reference
WReadOnly field showing totalHealth as a non-editable calculated value
Runs automatically on editor load. No menu item.
ScriptableObjectSingleton<T> loads its asset from Resources, which means a new teammate's first
run of the game fails on a settings asset nobody committed. This watches for singleton types with no
asset and creates them, and relocates any that end up in the wrong folder.
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.Attributes;
using WallstopStudios.UnityHelpers.Utils;
public sealed class GameSettings : ScriptableObjectSingleton<GameSettings>
{
public float masterVolume = 1f;
public bool enableVSync = true;
}
[ScriptableSingletonPath("Settings/Audio")]
public sealed class MusicSettings : ScriptableObjectSingleton<MusicSettings>
{
public float musicVolume = 0.8f;
}Write those two files, let the editor reload, and you have:
Assets/
Resources/
GameSettings.asset # no [ScriptableSingletonPath]
Settings/
Audio/
MusicSettings.asset # [ScriptableSingletonPath("Settings/Audio")]
Then GameSettings.Instance.masterVolume works in play mode and in a build, with nothing to
remember.
Move GameSettings.asset somewhere else by hand and the next domain reload moves it back — the
attribute is the source of truth for where it lives.
See Singleton Utilities for the runtime base class, its lookup order and Odin compatibility.
Runs automatically on sprite import. No menu item.
Asset labels are a good way to say "this sprite belongs in the UI atlas", but there is no cheap way
to ask Unity for every label in the project — you would have to load every asset. This
AssetPostprocessor maintains the list as sprites are imported, so a dropdown can be populated
instantly.
That is what makes this one line work:
using System.Collections.Generic;
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.Attributes;
using WallstopStudios.UnityHelpers.Core.Helper;
public sealed class AtlasRule : MonoBehaviour
{
[StringInList(typeof(Helpers), nameof(Helpers.GetAllSpriteLabelNames))]
public List<string> labels = new();
}Helpers.GetAllSpriteLabelNames() returns the cached array; the
GetAllSpriteLabelNames(List<string> destination) overload fills a list you own instead of
allocating. Sprite Atlas Generator uses exactly this for its label
pickers.
Editor-only. The cache is built by an AssetPostprocessor, so both methods return empty in a
player build, in batch mode, and in CI. Do not build runtime behaviour on them.
Only .png, .jpg and .jpeg assets under Assets whose importer type is Sprite contribute
labels, and the result is sorted so a dropdown is stable between sessions.
Tools > Wallstop Studios > Unity Helpers > Request Script Compilation — Ctrl/Cmd + Alt + R
You generated a .cs file from outside Unity — a codegen step, a script pulled by a package tool —
and the editor has not noticed. Rather than touching a file to force a rebuild, press the shortcut.
It runs a forced synchronous AssetDatabase.Refresh (so the new file is imported first) and then
CompilationPipeline.RequestScriptCompilation(). If Unity is already compiling it logs
Script compilation already in progress; manual request skipped. and does nothing, so leaning on the
shortcut is harmless.
Rebind it under Edit > Shortcuts in Wallstop Studios ▸ Request Script Compilation.
Visual Reference
Edit > Project Settings > Wallstop Studios > Unity Helpers
One panel for the package's project-wide defaults: inspector pagination, inline editor behaviour, button and toggle colours, and the coroutine wait buffers.
Coroutine wait buffers. yield return new WaitForSeconds(0.25f) allocates every time it runs.
The package pools those instructions, and this panel controls the pool: a quantization step (how
finely wait times are bucketed), a cap on distinct cached entries, and whether the cap evicts
least-recently-used. Settings are written to
Assets/Resources/Wallstop Studios/Unity Helpers/UnityHelpersBufferSettings.asset and applied on
domain reload and player start, so they ship with the build.
Inspector pagination. StringInListPageSize (default 25) sets how many options a
StringInList dropdown shows per page; EnumToggleButtonsPageSize
(default 15) does the same for [WEnumToggleButtons] grids. Raise them if lists feel cramped,
lower them if a big enum makes the inspector sluggish. Individual fields can override both.
Full reference: Inspector Settings.
Visual Reference
Centralized configuration panel in Unity's Project Settings
Runs automatically on editor load and after every recompile. No menu item.
The Effects system and the
relational component attributes resolve fields
by reflection. Doing that on the first frame is a visible stall, and on IL2CPP some of it cannot be
done at all. This walks your types at edit time and bakes the answers into an
AttributeMetadataCache asset that ships with the build.
The scan filters each discovered type once and avoids additional temporary query stages during reloads.
You do not call the generator. It runs on load, and the AttributeMetadataCache asset's inspector
has a Purge & Refresh Cache button for when you want to force it.
Read the baked data if you are building tooling of your own:
using System;
using UnityEngine;
using WallstopStudios.UnityHelpers.Tags;
public static class AttributeInspectorSupport
{
public static void Dump(Type componentType)
{
AttributeMetadataCache cache = AttributeMetadataCache.Instance;
// Every attribute name declared anywhere in the project.
string[] all = cache.AllAttributeNames;
if (cache.TryGetFieldNames(componentType, out string[] fieldNames))
{
Debug.Log($"{componentType.Name} declares {fieldNames.Length} of {all.Length} attributes.");
}
if (
cache.TryGetRelationalFields(
componentType,
out AttributeMetadataCache.RelationalFieldMetadata[] relational
)
)
{
foreach (AttributeMetadataCache.RelationalFieldMetadata field in relational)
{
Debug.Log($"{field.fieldName} is a {field.attributeKind} field.");
}
}
}
}What gets baked: the attribute fields on every AttributesComponent type, the
[ParentComponent] / [ChildComponent] / [SiblingComponent] metadata, assembly-qualified type
names so they resolve at runtime, whether each field is a single value, array, List or HashSet,
and whether its element type is an interface.
WallstopStudios.UnityHelpers.Editor.Utils.EditorUtilities
A wizard that creates an asset should create it where the user is looking, not in Assets/. Unity
does not expose the Project window's selected folder, so this does.
using UnityEditor;
using UnityEngine;
using WallstopStudios.UnityHelpers.Editor.Utils;
public static class CreateAbilityAsset
{
[MenuItem("Assets/Create/Game/Ability Config")]
private static void Create()
{
AbilityConfig asset = ScriptableObject.CreateInstance<AbilityConfig>();
string folder = EditorUtilities.GetCurrentPathOfProjectWindow();
string directory = string.IsNullOrEmpty(folder) ? "Assets" : folder;
AssetDatabase.CreateAsset(
asset,
AssetDatabase.GenerateUniqueAssetPath($"{directory}/NewAbilityConfig.asset")
);
AssetDatabase.SaveAssets();
Selection.activeObject = asset;
}
}GetCurrentPathOfProjectWindow() returns an asset-relative path such as Assets/Data/Abilities, or
an empty string when it cannot determine one — always have a fallback, as above. It reaches an
internal Unity API by reflection, so treat the empty-string case as normal rather than exceptional.
IsInvokedByTestRunner() reports whether this editor process was launched with Unity Test Runner
command-line arguments, by looking for runTests, testResults or testPlatform in the command
line. A run started from the in-editor Test Runner window does not carry those arguments and is not
detected — this answers "was this editor launched to run tests", not "is a test session running
right now". A window's OnEnable runs during a command-line test session too, so work that
consumes fixtures or writes logs should be gated on it — together with Application.isBatchMode
and Helpers.IsRunningInContinuousIntegration,
the other two legs of the same question:
using UnityEditor;
using UnityEngine;
using WallstopStudios.UnityHelpers.Core.Helper;
using WallstopStudios.UnityHelpers.Editor.Utils;
public sealed class AssetReportWindow : EditorWindow
{
private void OnEnable()
{
if (
Application.isBatchMode
|| EditorUtilities.IsInvokedByTestRunner()
|| Helpers.IsRunningInContinuousIntegration
)
{
return;
}
ImportReports();
}
private void ImportReports() { /* fixture-consuming work */ }
}It answers false rather than throwing, even on a host that refuses to share its command line. If
all you want is to skip dialogs, EditorUi already folds this in behind
EditorUi.Confirm/EditorUi.Info; reach for this predicate when the work itself, not just the
prompt, should be skipped.
IndentLevelScope.Indent() and IndentLevelScope.AtLevel(level) change
EditorGUI.indentLevel without per-scope garbage after the shared owner has warmed to the maximum
concurrent nesting depth. Copies are safe to dispose more than once. Nested scopes also restore the
newest still-active level when copies are disposed out of order, rather than replaying a stale
global value over an inner drawer.
Tools > Wallstop Studios > Unity Helpers > Export Failed Tests / Clear Failed Tests
A flaky test that fails once in twenty runs is invisible if the only record is the Test Runner window
you already closed. Enable this in
Edit > Project Settings > Wallstop Studios > Unity Helpers and every failure — name, message, stack
trace — is captured and written to failed-tests-YYYY-MM-DD-HHmmss.txt in a folder you choose
(project root by default).
Disabled by default. Both menu items are greyed out until there are failures to act on.
Full setup and API: Failed Tests Exporter.
Tools > Wallstop Studios > Unity Helpers > Run EditMode Tests With Summary /
Run PlayMode Tests With Summary
Starts a test run and returns immediately, having first opened
<project>/Temp/unity-helpers-test-run-<mode>.txt with a running marker. When the run ends the
file is rewritten with one summary line, one line per assembly, and one line per failure carrying
its message and source location. A process driving an editor it does not own polls that file instead
of scraping the console, which can be cleared out from under it.
EditMode and PlayMode write different files, a second run is refused while one is in flight, and the callbacks are re-registered after the domain reload a PlayMode run causes.
Format, poller example and recovery from a lost run: Test Run Reporter.
MultiFileSelectorElement is the UI Toolkit control the tools above use for "pick several files or
folders". It is public, so your own editor windows can use it too.
In the editor it remembers selections through EditorPrefs and offers Reveal in Finder / Show in
Explorer. In a player build it falls back to PlayerPrefs and enumerates files under the
application's data root, dropping the editor-only affordances.
Persistence is opt-in per element, via a persistenceKey. Stored scopes are managed from
Tools > Wallstop Studios > Unity Helpers > Multi-File Selector Persistence, which can
Run Cleanup Now or drop scopes unused for more than Max age (days) (default 30) on editor
startup.
There is a runnable example in the UI Toolkit - MultiFile Selector (Editor) sample, importable from
the Package Manager.
Everything under Tools > Wallstop Studios > Unity Helpers:
| Menu item | Section |
|---|---|
| Animation Copier | Animation Copier |
| Animation Creator | Animation Creator |
| AnimationEvent Editor | Animation Event Editor |
| Clear Failed Tests | Failed Tests Exporter |
| Export Failed Tests | Failed Tests Exporter |
| Fit Texture Size | Fit Texture Size |
| Image Blur | Image Blur Tool |
| Multi-File Selector Persistence | MultiFile Selector |
| Prefab Checker | Prefab Checker |
| Proto Schema Exporter | Serialization |
| Request Script Compilation | Request Script Compilation |
| Run EditMode Tests With Summary | Test Run Reporter |
| Run PlayMode Tests With Summary | Test Run Reporter |
| Sprite Animation Editor | Sprite Animation Editor |
| Sprite Atlas Generator | Sprite Atlas Generator |
| Sprite Cropper | Sprite Cropper |
| Sprite Pivot Adjuster | Sprite Pivot Adjuster |
| Sprite Settings Applier | Sprite Settings Applier |
| Sprite Sheet Animation Creator | Sprite Sheet Animation Creator |
| Sprite Sheet Extractor | Sprite Sheet Extractor |
| Texture Resizer | Texture Resizer |
| Texture Settings Applier | Texture Settings Applier |
| Unity Method Analyzer | Unity Method Analyzer |
| Validate Serialized Fields In Selection | Serialized Field Validator |
Elsewhere:
-
Assets > Create > Wallstop Studios > Unity Helpers > Scriptable Sprite Atlas Config— Sprite Atlas Generator -
Edit > Project Settings > Wallstop Studios > Unity Helpers— Project Settings
Runs with no menu item: ScriptableObject Singleton Creator, Sprite Label Processor, Attribute Metadata Cache Generator, Asset Change Detection.
Shared across the windows: folder fields accept dragged Project-window folders, the folders you pick are remembered per tool and offered again next time, and long operations report progress in the console rather than blocking silently.
Progress bars close even when an operation, asset save, or batch cleanup fails. This includes prefab checks, sprite atlas generation and source settings, sprite and texture settings, texture resizing, sprite reference replacement, and animation copy or delete operations. Canceling a prefab scan keeps findings already collected; canceling a settings batch saves the importer changes already processed. A processing error still stops the operation and may leave earlier changes applied. Reimport and generated-file side effects are not a transaction and cannot be fully reversed with Undo.
Most of these tools only change import settings, which Unity records as an undo step and which you
can revert by reverting the .meta file. These are the ones that write real files:
| Tool | Effect |
|---|---|
| Image Blur Tool | Writes new files; restores temporary source import-setting changes |
| Sprite Cropper | Writes PNGs; overwrites sources when Overwrite Originals is on |
| Texture Resizer | Overwrites source PNGs unless Output Folder is set |
| Sprite Sheet Extractor | Writes new PNGs; the Danger Zone rewrites referencing assets |
| Sprite Cropper Danger Zone | Rewrites .prefab, .unity, .asset, .mat, .anim, .overrideController
|
| Animation Copier | Copies and (in Cleanup) deletes .anim assets |
| Prefab Checker | Read-only unless you enable auto-fix and click Fix Missing Scripts |
Everything else — Texture Settings Applier, Sprite Settings Applier, Sprite Pivot Adjuster, Fit Texture Size — touches import settings only and records a named undo step.
None of the tools that rewrite image data have an undo. Commit before running them.
A folder of new character art:
- Sprite Sheet Extractor if it arrived as sheets.
- Sprite Cropper to trim the padding.
- Sprite Settings Applier for PPU and filter mode.
- Sprite Pivot Adjuster so the pivots agree.
- Animation Creator to build the clips.
- Animation Event Editor for footsteps and hitboxes.
- Sprite Atlas Generator to pack them.
Before you open a pull request:
- Prefab Checker over the prefab folders you touched.
-
Unity Method Analyzer over
Assets/Scripts. -
Serialized Field Validator on any new
MonoBehaviourorScriptableObject.
Trimming build size:
-
Fit Texture Size in
GrowAndShrinkmode acrossAssets/Sprites. - Sprite Cropper on anything still padded.
- Texture Settings Applier with crunch compression and per-platform max sizes.
| Symptom | Likely cause |
|---|---|
| A sprite tool skips files silently | It is a multi-sprite texture, or Read/Write Enabled is off. Cropper and Pivot Adjuster need Single sprites. |
| Scan Folders finds nothing | The regex does not match, or the folder path is outside Assets/. Invalid regexes are logged and match nothing. |
| An atlas looks worse than the source | Run Force Uncompressed for N Source Sprites before packing. |
| Clips landed in the wrong folder | Animation Creator writes beside the first sprite. Move them with Animation Copier. |
An [AnimationEvent] method is not listed |
It must return void and take zero or one int / float / string / UnityEngine.Object / enum parameter. |
| Prefab Checker reports nothing | The relevant checks may be off — Empty String Fields and Disabled Components are off by default. |
| Fix Missing Scripts is greyed out | Tick Enable Auto-fix options first. |
| A tool did nothing in a CI or batch run | Dialogs and folder pickers are suppressed in batch mode, so anything that needs one is skipped. |
- Inspector Attributes — the full attribute set
- Asset Change Detection — run code when assets change
- Unity Method Analyzer — the analyzer in detail
- Failed Tests Exporter — capturing test failures
- Test Run Reporter — starting a run and polling a summary file
-
Singleton Utilities —
ScriptableObjectSingleton<T> - Effects System — what the attribute metadata cache serves
📦 Unity Helpers | 📖 Documentation | 🐛 Issues | 📜 MIT License
- Inspector Button
- Inspector Conditional Display
- Inspector Grouping Attributes
- Inspector Inline Editor
- Inspector Overview
- Inspector Selection Attributes
- Inspector Settings
- Inspector Validation Attributes
- Utility Components
- Visual Components
- Data Structures
- Helper Utilities
- Math And Extensions
- Pooling Guide
- Random Generators
- Reflection Helpers
- Singletons
- Asset Change Detection
- Asset Validation
- Authored Asset Validation
- Editor Tools Guide
- Failed Tests Exporter
- Sprite Animation Motion
- Test Run Reporter
- Unity Method Analyzer
- Ai Model Backends
- Bundled Assembly Conflicts
- Mcp Ecosystem
- Mcp Local Setup
- Odin Migration Guide
- Unity Devcontainer Licensing


















