CHANGELOG - Ambiguous-Interactive/unity-helpers GitHub Wiki
All notable changes to Unity Helpers will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Add public
ObjectsTypeTraits<T>with cached value-type, exact-object, and Unity-object classifications for generic code (guide, #976). - Add opt-in
WALLSTOP_PROTO_ONLYbuilds that omit protobuf-net contracts and fallback, plus caller-owned WProto buffer writers for pooled equality and hashing (#937). - Add value-returning
WProtoFacadeserialize and read methods that reject unsupported types and report typed serialization failures (#937). - Add
WPROTO050to report generated deserialization constructors whose base has no accessible zero-argument constructor (#937). - Add
WProtoMember.IsPacked = falseto write unpacked numeric and enum collections matching protobuf-net defaults, while preserving existing packed writes and both read forms (#937). - Add
SystemArrayPool<T>.TryWithBufferfor synchronous borrowed spans with explicit state, optional results, and reported callback or cleanup errors (#924). - Add
WUH022to report boxing disposable structs and suggest concrete or constrained generic disposal (#924). - Add opt-in
WUH021to flag repeated size observations, including interfaceCount, knownLength/LongLengthproperties, and non-predicate LINQCount()/LongCount(), when one snapshot is intended. - Add
WUH020to suggest non-throwing queue and stack reads when compatibleTrymethods are available (#912). - Add explicit scalar default omission for WallstopProto consumer migrations, with ordinary consumer migration coverage and runtime-model compatibility guidance (#343).
- Add prompt-free Sentinel workspace APIs for authored rules, preferences, profiles, build gates, and frame budgets, with isolated inputs, blank-path rejection, diagnostics, and persisted undo/redo (#901).
- Add Mantel-Haenszel pooled odds ratios and chi-square upper-tail probabilities, including zero-cell boundaries and extreme-tail evaluation (#742).
- Add informational
WPROTO048migration guidance for protobuf-net runtime model registrations, which do not configure generated formatters (#343). - Add
WUH013coverage for custom collections with public struct enumerators and immediately cached full-sequence bounds, while retaining indexed traversal for mutation and dictionary keys (#900). - Add a compact UI Toolkit analyzer policy list with search, syntax-highlighted code examples, per-analyzer severity controls, automatic ruleset synchronization, and a Project Settings shortcut (#883).
- Add protobuf support for
Vector4,Matrix4x4,KeyframeandBoneWeightas roots and members on both serialization routes, including IL2CPP (#343). - Add opt-in
WUH019to suggest caching stable loop bounds while keeping live bounds for changing collections (#894). - Add cached read-only enum values and an ordinal enum-name comparer for allocation-free iteration and warmed name ordering (#889).
- Fix effect removal callbacks losing reapplied modifiers or repeating cleanup. Stop periodic damage after an attribute callback cancels its effect (#977).
- Fix loading caches leaking disposable factory results when weight or lifetime callbacks throw, while preserving references retained by other entries (#972).
- Fix first-save initialization so failed writes leave the save path free for retry and competing creators keep their complete files (#964).
- Fix WallstopProto map entries losing earlier members when one entry repeats its message value; separate entries for the same key still replace the value. Reject malformed fragments across message boundaries (#970).
- Fix weighted caches losing entries when storage fills, exceeding their budget on replacement or overflow, and changing existing values after a failed weight calculation (#969).
- Fix completed
ValueTaskcoroutine adapters retaining pooled source resources when no result callback is supplied; completed cancellation is now propagated in those paths (#967). - Fix invalid background-worker idle timeouts stranding queued save jobs; supported fractional and infinite waits retain their behavior (#965).
- Fix extra allocations during WProto polymorphic list reads by reusing unseeded and replacement lists; preserve constructor list identity and append order (#937).
- Fix scene retrieval in packed players and already loaded nonactive scenes; borrowed scenes stay loaded, while owned scene disposal waits for loading and releases callbacks (#963).
- Fix queued dispatcher tasks hanging after destruction. Cancel abandoned work, reject uninitialized or destroyed dispatchers, and preserve running task completion (#961).
- Fix dispatcher scope cleanup attempting to destroy loaded prefab assets; scene instances still receive cleanup (#827).
- Fix editor imports in projects without Unity Test Framework; test reporting tools activate when the framework is installed (#950).
- Fix legacy protobuf maps losing present nullable defaults, while preserving null values and historical nonnullable bytes (#953).
- Fix deque and cyclic buffer serialization retaining pooled scratch buffers after failed writes, including nested serialization of the same instance (#952).
- Fix package-installed player builds losing private serialization fields and callbacks to code stripping (#950).
- Fix cached enum display labels to use the declared member name when an
InspectorNamecontains only whitespace; nonblank labels retain their exact text (#867). - Fix legacy dictionary comparisons and nullable fields; preserve runtime map, wrapper, surrogate, enum and external-provider behavior without dynamic generic construction. Reject indexers before tuple classification (#950).
- Fix direct texture settings API calls to ignore blank platform override names while preserving other settings (#867).
- Fix blank key labels in duplicate dictionary warnings for Unity objects with whitespace-only names; object references and literal string keys stay unchanged (#867).
- Fix
DurableFilereplacements to preserve existing saves and report failure when atomic replacement is unsupported (#946). - Fix blank custom dropdown labels and popup titles while preserving literal string options (#867).
- Fix concurrent thread pool disposal returning before running work and handle cleanup finish (#827).
- Fix SINGLE_THREADED pool purges when callbacks rent, dispose or purge the same pool; selected entries receive cleanup once without stale list indices (#827).
- Fix singleton asset recreation after stale GUID cleanup by requesting synchronous cleanup and pausing batching during creation and import (#942).
- Fix unexpected singleton asset creation failures blocking later attempts until a domain reload (#936).
- Stop WallstopProto
WPROTO044inheritance diagnostics at an intermediate[WProtoNotSerialized]boundary, including referenced assemblies, while retaining contradictory declaration errors (#937). - Fix WallstopProto map keys to match protobuf-net 3.2.56: write zero enum keys and omit zero floating-point keys while preserving reads of older payloads (#937).
- Fix periodic effects repeating ticks before their cadence at large clocks; initial delays, tick limits, refresh phase, and catch-up limits are preserved (#934).
- Fix cache and effect lifetimes at large clocks, including small timing offsets and jitter; preserve remaining effect time and disabled cache expiry (#932).
- Fix discarded worker jobs retaining captured references after shutdown, including submissions racing disposal (#844).
- Fix pool acquisition and warm-up failures leaking items or leaving phantom active rentals; acquisition callbacks can query pool state across threads (#827).
- Fix timed cache refresh failures changing cached values or consuming initial jitter, and preserve expiry when finite lifetime plus jitter exceeds float range (#817).
- Fix singleton loading to use the Resources root when configured subfolders contain only slashes and whitespace; spaces inside valid folders are preserved (#867).
- Fix invisible whitespace-only toggle captions by showing
(Unnamed); literal string values and padded captions stay unchanged (#867). - Fix sprite sheet automatic config lookup throwing for invalid paths; invalid paths and missing sidecars preserve loaded settings (#929).
- Fix sprite sheet configuration save/load accepting blank texture paths; valid filenames containing spaces and serialized hashes keep their exact values (#867).
- Fix blank custom validation messages hiding the default warning in standard and Odin inspectors; nonblank messages and serialized string values retain their exact text (#867).
- Fix blank prefixes in inline editor headers when a field label contains only whitespace (#867).
- Fix dropdowns showing invisible labels for Unity objects with whitespace-only names; object names, references, and literal string options stay unchanged (#867).
- Fix IntMap lookups for keys sharing low bits, keep small live windows at their starting capacity during churn, and replace existing values without rebuilding the table (#926).
- Fix main-thread guard diagnostics to omit blank context labels and use the source filename for blank member names; nonblank labels retain their exact text (#867).
- Fix blank serialized member names creating synthetic backing fields, and reject backing-field names with blank property names (#867).
- Fix newly constructed serialization failure diagnostics with blank reasons so they show
operation failed; nonblank reasons and literal input descriptors keep their exact text (#867). - Fix async durable writes and copies publishing after cancellation during staging; the previous file stays intact when cancellation is observed before publication (#827).
- Fix Prefab Checker JSON exports to include scanned folders and findings, with empty collections and escaped text (#870).
- Fix WShowIf condition names accepting whitespace-only identifiers; literal whitespace comparison values keep their meaning (#867).
- Fix Unity Method Analyzer recovery from corrupt compiler snapshots while preserving saved report compatibility (#870).
- Fix test run summary ownership so blank tokens cannot finish or discard running markers; valid tokens retain their literal spaces (#867).
- Fix subtype manifest ownership lookup accepting whitespace-only directories; valid folder names containing spaces remain unchanged (#867).
- Fix relational component assigner and injection constructors being stripped from High-stripping IL2CPP players using Zenject, VContainer, or Reflex (#916).
- Fix WButton coroutines retaining resources when cancelled, completed, or faulted. Nested callback updates also leave each routine intact (#911).
- Fix persistent runtime singletons losing their authored world placement when detached from transformed parents (#644).
- Fix
PartitionPooledretaining outstanding lists when the source throws during disposal. Cleanup no longer replaces an exception from batch processing (#827). - Fix
TrackedObjectPoolcallback cleanup so disposed pools retain no returned items, nested takes keep distinct ownership, and callback failures do not interrupt cleanup (#781). - Fix
FileHelper.InitializePathaccepting whitespace-only paths on some platforms. See File Operations (#867). - Fix percentile interpolation across extreme finite values and preserve infinite endpoints without boxing primitive elements (#742).
- Fix command-line helpers so blank option names return no values before reading process arguments; padded names and literal values retain exact matching (#867).
- Fix blank scene queries and temporary scene loads, and reject blank external package identifiers when resolving asset paths (#867).
- Fix dropdown providers so blank method names return no options before method discovery (#867).
- Fix blank animation configuration folders and sprite profile asset paths so they stop before file access or profile matching (#867).
- Fix blank multi-file selector paths and saved directories, reject blank sprite extension filters, and prevent test run summaries from claiming blank paths (#867).
- Fix the Pool Purging settings foldout so hysteresis and spike threshold stay with the other global options (#883).
- Fix disposal lease slot growth when a lease is acquired on one thread and disposed on another (#888).
- Fix sprite pixel helpers for readable compressed textures and avoid reading a full atlas when extracting one sprite (#886).
- Fix UI pointer helpers so hits on other objects and invalid canvas raycast positions do not return points off the target rectangle (#884).
- Fix editor test-runner detection so project paths and log filenames containing test flag names do not suppress confirmation dialogs (#885).
- Fix
SemaphoreLeaseso a stray permit release raisesSemaphoreFullExceptionwhen its lease is disposed (#887).
- Reduce repeated pool purge and disposal snapshot allocations with bounded buffer reuse that preserves nested callback ownership.
- Improve directly constructed
PooledResource<T>wrappers with no return callback by avoiding disposal tracking; callback-backed leases remain copy-safe (#924). - Improve large
Deque<T>array transfers with contiguous copies, preserving logical order, offsets, and covariant destination behavior (#904). - Improve
JesseSortfor mixed regions, sparse disorder, and natural runs using the upstream live-phase design (#747). - Improve parent and child collection transfers with bulk copying for
Componentfields while retaining typed enumeration for derived components (#781).
- Remove production test-only APIs and injected callbacks; package tests now access real implementation internals (#935).
- Add
SpriteSettingsApplierAPI.TryApplyProfilesto batch-apply and save sprite importer profiles from scripts without opening the window (#821). - Add
TextureSettingsApplierAPI.TryApplyTextureSettingsto apply and save texture import settings from scripts without opening the window (#821). - Add
AnalyzerPolicyAPI.TrySetEnabledso editor scripts can enable or disable Unity Helpers analyzer policies without opening the window (#821). - Add
ScriptableSpriteAtlasGenerator.TryCreateConfigandTrySetSourceTexturesUncompressedto create atlas configs and manage source sprite import settings without opening the window (#821). - Add
DisposableScopefor allocation-freeusingcleanup with copy-safe, reverse-order disposal (#875). - Add
SpriteSettingsApplierAPI.TrySaveProfilesandTryLoadProfilesso editor scripts can persist sprite settings without opening the window, with explicit overwrite and independent profile copies (#862). - Add
UnityMethodAnalyzerReportExportAPIso editor scripts can save Markdown and JSON findings with compiler coverage status without opening the window (#861). - Add
ValidationReportExportAPIso editor scripts can write Sentinel JSON and JUnit reports from a validation run without opening the workspace (#821). - Add
SpriteSheetAnimationAPI.TryCreateso editor scripts can preview or create sprite sheet animation clips from ordered frames without opening the window (#821). - Add
SpriteSheetAnimationAPI.TryDiscoverFramesso editor scripts can load ordered sprite frames and choose whether to update import settings without opening the window (#821). - Add
AnimationClipFrameSaveAPI.TrySaveFramesso editor scripts can save sprite frames and FPS to an existing clip without opening the window. See Sprite Animation Editor (#851). - Add
PrefabChecker.ScanFoldersso editor scripts can inspect prefab findings from explicit folders and options without opening the window (#821). - Add
PrefabChecker.TryRemoveMissingScriptsso editor scripts can preview or repair missing script slots in prefabs without opening the window (#821). - Add
TextureResizerAPI.TryResizeTexturesso editor scripts can resize or preview PNGs from explicit textures and asset folders without opening the window. Textures process in natural name order (#821). - Add an encoding-aware
DurableFile.TryWriteAllTextoverload that preserves the encoding's byte order mark for staged writes.
- Fix sprite atlas generation losing unsaved config sprite entries while creating an atlas asset (#821).
- Fix the Sprite Atlas Generator's Force Uncompressed action so its default-platform compression setting stays uncompressed after reimport (#821).
- Fix editor folder discovery and atlas generation so paths with Windows separators work from scripts and batch mode (#821).
- Fix
DurableFileownership so append, create, and delete operations cannot change a destination during a staged write; competing operations also report failure without crashing Windows IL2CPP players (#871). - Fix Image Blur and Sprite Sheet Extractor output publication so a competing staged-file writer cannot change the destination during publication (#871).
- Fix Sprite Sheet Animation Creator so declining an import-settings prompt leaves the texture importer unchanged (#821).
- Fix Texture Resizer batch calls so whitespace-only source folder entries are ignored like empty entries (#867).
- Fix cached package path conversion so blank package IDs return no path (#867).
- Fix Sentinel validation runs so targets with whitespace-only GUIDs or asset paths are skipped (#867).
- Ignore whitespace-only file paths in Unity Method Analyzer source selection, compiler messages, navigation, and report export (#867).
- Fix
SpriteRendererMetadataso material copies are released after replacement, pop, or destruction, and moving the top material to the back restores the prior top (#855). - Fix Sprite Cropper padding so borderless sprites stay borderless and larger outputs retain their full resolution (#857).
- Fix competing Sentinel suppression edits and Undo so a stale action reports a conflict instead of replacing another action's decisions (#817).
- Fix prefab validation Undo and stale-key repair rollback so they reject observed byte changes before restoring a snapshot; competing staged-replacement
DurableFilewriters share the check's lock (#817, #827). - Fix animation creation APIs to return the created asset path when saving fails after Unity creates the clip (#821).
- Fix Image Blur and Sprite Sheet Extractor new image publication so readers see complete outputs and competing publishers preserve the first file (#827).
- Fix the Sprite Animation Editor's Preview FPS field on Unity 2021.3 so its window opens correctly (#851).
- Fix Sprite Animation Editor saves for clips with multiple sprite bindings so frames from other bindings are not written into the selected curve (#851).
- Fix Odin Migration menu placement under Tools > Wallstop Studios > Unity Helpers (#849).
- Fix JSON
Typevalues and pool type settings to recover constructed generics after unique component assembly moves (#846). - Fix
SerializableTypereferences to constructed generic types after their component types move between assemblies (#845). - Fix Texture Resizer so it rejects invalid sizing settings and colliding output names before changing source, importer, or destination files (#821).
- Fix Test Run Reporter summary claims so one run owns a marker and a stale callback cannot replace or delete a newer run (#827).
- Fix concurrent processes publishing another
DurableFilewriter's staged bytes. A competing writer now fails while the first owns the stage (#838). - Stage subtype manifests, exported proto schemas, and sprite or animation configs before replacement so failed staging leaves the previous file intact (#827).
- Stage original asset bytes before stale-key repair rollback and prefab validation undo, so failed staging does not truncate the asset (#827).
- Keep issue template version choices stable across local hooks and release preparation by rendering both forms from a tracked version manifest.
- Fix asset change detection warnings caused by deserializing imported assets while checking for matching sub-assets. Sprite and nested asset watchers still receive matching paths (#834).
- Fix
FileHelper.InitializePathto returnfalsefor invalid paths and directory failures while preserving existing files. See File Operations (#827).
- Improve tuple-to-coroutine adapters by removing extra callback allocations (#781).
- Improve Sprite Cropper scan speed for images of at least one million pixels (#782).
- Improve
JesseSorton large, sustained random-looking inputs with a direct-sort route while retaining pile merging for structured runs (#747). - Improve subset sampling from queues and hash sets by copying each source once (#781).
- Improve loading of missing
SerializableTypereferences without building the type-picker catalog (#783).
- Add
AnimationEventSaveAPIto save clip events and optional frame rates from editor scripts without opening the window. See Animation Event Editor (#821). - Add
SpritePivotAdjusterAPIto find sprite textures and preview or apply center-of-mass pivots from editor scripts without opening the window. See Sprite Pivot Adjuster (#821). - Add
SpriteCropperAPIto discover and crop sprite assets, build mappings for separate cropped outputs, and preview or replace references from scripts without opening the window. Failed encoding or staging writes keep the previous PNG bytes. See Sprite Cropper (#821, #827). - Add
AnimationCreatorAPIto build sprite clips and write uniquely named animation assets from editor scripts without opening the window. See Animation Creator (#821). - Add
ImageBlurAPIto blur textures or write blurred project assets from editor scripts without opening the window. Occupied output names are preserved. See Image Blur Tool (#821, #817). - Batch sprite reference replacement asset edits and finalize one Undo record per changed object (#821).
- Add
SpriteSheetExtractionAPIto find sprite sheets and extract selected rectangles from scripts or batch mode without opening the window. Outputs appearing during extraction are skipped unless overwrite is requested. See Sprite Sheet Extractor (#821, #817). - Add
SpriteSheetReferenceReplacementAPIto preview and replace sprite references in explicit project assets from scripts. See Sprite Sheet Extractor (#821). - Add
AnimationCopierAPIto analyze, preview, copy, and clean up standalone animation clips from scripts or batch mode without opening its window. See Animation Copier (#821). - Add
FitTextureSizeAPIto preview and apply texture import sizes from scripts or batch mode without opening its window. See Fit Texture Size (#821). - Add
ProtoSchemaExporterto write selected or discovered project contracts from Editor code without opening a window. See Serialization (#821). - Add a public sprite atlas API to scan and sync folders, generate and pack atlases, and detect config drift from code or batch mode. Matching atlases are left untouched. See Sprite Atlas Generator (#821).
- Add
DurableFile.WriteAllBytesAsyncfor staged, cancellable binary saves with the same durability guarantees as synchronous byte writes. See Durable Writes for Player Data (#816). - Add an Odin migration preview/apply tool for proven-equivalent
ReadOnlyandEnumToggleButtonsfields. Preview reports possible Odin-owned data, including quoted keys, in scenes, prefabs, and assets; source-only edits leave that data untouched. See the Odin Migration Guide (#794). - Add opt-in
WUH018warnings for string==and!=comparisons whose ordinal comparison policy is implicit. Null checks and explicit comparison APIs remain quiet. Every package-owned .NET project enforces allWUH###policies, including the three consumer opt-ins. See Analyzers (#807). - Add Analyzer Policies under
Tools > Wallstop Studios > Unity Helpers. It explains everyWUH###diagnostic, enables or disables the full policy set inAssets/Default.ruleset, preserves unrelated analyzer groups, and reports missing, mixed, duplicate, or unknown-rule drift. - Add
WallMath.TryFisherExactTestfor exact two-sided comparisons of two binary groups with bounded work and fail-soft validation. See Descriptive Statistics (#742). - Add
WallMath.TryExactSignTestfor exact two-sided paired-result comparisons that exclude ties and fail soft on invalid counts. See Descriptive Statistics (#742). - Add
WallMath.TryClopperPearsonIntervalfor exact equal-tailed binomial confidence bounds with fail-soft validation. See Descriptive Statistics (#742). - Add stable exponential-race weighted index and no-replacement subset selection over spans, with allocation-free caller scratch and overflow-safe cumulative double selection. See Random Generators (#792, #781).
- Add reusable named command-line parsing, RectTransform-to-BoxCollider2D synchronization, and DPI-aware EventSystem drag thresholds. See Helper Utilities and Math and Extensions (#742).
- Add
RandomSeeds.Mix64and ordered numeric or UTF-8 domain seed derivation for reproducible, schedule-independent gameplay streams. See Keyed Seed Derivation (#791). - Add
PointerEventData.TryGetWorldPointandTryGetLocalPoint, which prefer raycast hits and convert clamped screen positions across overlay, camera, and world-space canvases. See Pointer Coordinates (#742). - Add
Line2D.TryGetIntersectionPoint(Circle, out Vector2), which returns the first circle-boundary hit along a segment. See Math and Extensions (#742). - Add
ShapeHelpercircular curves, upper-semicircle fractions, and angle-centered circular fractions with reusable output buffers and fail-soft validation. See Helper Utilities (#742). - Add
NextIntSkewed, which averages uniform draws with a preferred target and clamps the result to the requested bounds. See Random Generators (#742). - Add
AuthoredAssetYaml.TryResolveObjectReference, which resolves both external and same-file Unity references without loading the asset. See Authored Asset Validation (#779). - Add
ColorContrast.Composite, which produces the opaque visible color for a translucent foreground before a contrast measurement. See Readable Text on Any Background (#777). - Add optional report context and exception handler overloads to
StartFunctionAsCoroutine, so hosted jobs can attribute or program against every failure (#778).
-
Speed up
[DetectAssetChanged]watcher initialization by finding handler methods through Unity's type cache instead of inspecting every method in loaded assemblies (#783, #818). -
Avoid a staging copy for
ICollection<T>sources inOrderBy,Ordered, andShuffled, while keeping compact returned lists for other sources (#781). -
Correct random reference documentation: identify
WyRandom's wyhash v1 stream, clarifyXoroShiroRandom's high-half output, and distinguish the SplitMix design from Vigna's 2015 reference implementation without attributing an unsupported published PractRand result (#757). -
Describe
PatternDefeatingQuickSortas the pdqsort-inspired introsort it implements; it does not include upstream pattern-breaking heuristics (#756). -
Avoid an iterator allocation when the Animation Viewer opens or restores its multi-file selector (#781).
-
Improve editor reloads by removing additional query stages from attribute metadata discovery (#648, #781).
-
Improve Sprite Atlas Generator refresh and source-sprite processing by removing temporary query pipelines while preserving configuration and sprite order (#648, #781).
-
Improve
LayeredImageconstruction by keeping measured parallel blending for multi-row sprites while avoiding slower scheduling for single-row sprites; overflow-safe work sizing preserves the parallel path for very large dimensions (#781, #782). -
Avoid Sprite Pivot parallel scheduling below 65,536 pixels and for one-row sprites, while retaining measured parallel speedups for larger work, and reduce overhead when Pivot Adjuster and Sprite Cropper search large asset folders (#648, #781, #782).
-
Avoid parallel scheduling for Image Blur passes with fewer than 256 pixels or only one partition, reducing small-texture overhead while retaining parallel execution for larger work (#782).
-
Resolve already-loadable
SerializableTypenames before constructing the full descriptor catalog, and avoid parallel sprite-crop scans below a conservative measured crossover (#781, #782). -
Reduce the slowest EditMode, PlayMode, and standalone test paths without reducing case, sample, or parameter counts or covered production behavior (#783).
-
Format Sentinel JUnit reports sequentially, removing worker scheduling and two temporary arrays while preserving report order (#781, #782).
-
Release destroyed Unity object targets from Sentinel's central result store on the next results read without discarding diagnostic details (#669).
-
Defer SerializableType ignore-pattern match counts until the settings window displays them, avoiding a scan of every loaded type on each domain reload (#786).
-
Improve Sprite Cropper and Sprite Sheet Extractor memory reuse for dynamically sized pixel output, with measured size-aware parallel row copies for large images (#754).
-
Remove avoidable allocations from asset watcher discovery and WallstopProto source generation while preserving handler and diagnostic ordering (#764, #781).
- Fix Sentinel suppression saves and undo leaving a partial decision file if a write fails (#827).
- Fix Texture Resizer dry runs changing the loaded image and importer settings. A preview now calculates the target size without resizing or reimporting (#821).
- Fix editor report exports leaving a partial file when writing fails. Existing Prefab Checker, Animation Copier, Method Analyzer, validation, and failed-test reports stay intact until the new report is ready (#827).
- Fix
FileHelper.CopyFileAsyncerasing an existing destination when reading or staging fails, or the copy is cancelled. See File Operations (#827). - Fix Texture Resizer and Analyzer Policies saves when the destination changes during a write (#817).
- Fix Animation Copier dry runs creating destination subfolders during copy previews.
- Fix Animation Copier's Include Unchanged in Copy All option so selected unchanged clips are actually replaced while preserving destination GUIDs.
- Fix
DurableFile.TryDeletereporting success for a directory or inaccessible target (#817). - Fix Editor startup errors when Unity Helpers loads saved project settings, including custom test output paths and pool purging options (#814).
- Fix
NextSubsetreading a lazy source when asked for zero items. It now returns empty without enumerating the source. See Random Generators (#781). - Fix plain .NET source builds of random generators and WallstopProto without the 3.6.0 upgrade workarounds. Unity builds keep their existing behavior (#815).
- Add
WallMath.Median,Percentile,MeanandStandardDeviationoverIReadOnlyList<float>,<double>,<int>and<long>, sorting pooled copies so the caller's list is never reordered. See Descriptive Statistics (#742). - Add
SpriteHelpers.RotateTexture90,RotateTexture180andExtractSpriteRect, which produce new textures and leave the source untouched, returning null with a logged reason instead of throwing on unreadable or write-protected textures. See Helper Utilities (#742). - Add
Objects.Sha256Hexfor text (UTF-8) and byte payloads, andObjects.TrySha256HexOfFile, which reports a missing or unreadable file withfalseinstead of throwing. The sprite sheet tooling's private file hash now calls it. See SHA-256 Digests (#742). - Add
EditorUtilities.IsInvokedByTestRunner(), so an editor window can skip fixture-consumingOnEnablework during a test run instead of re-deriving it. Answersfalserather than throwing where the command line is unreadable, and replaces seven private copies. See Editor Utilities (#742). - Fix
ColorExtensionsaveraging (GetAverageColor, dominant and weighted variants) reading channel-swapped pixels fromARGB32andBGRA32textures through the raw fast path; those formats now take the sameGetPixels32path as every other format (#749). - Enforce one member ordering across the codebase: const, events, delegates, static properties, static fields, properties, fields, constructors, static methods, methods, nested types last, each tier public → protected → internal → private including const.
npm run lint:nested-type-placementenforces it (#672). - Add a JesseSort adaptation with linear monotone-input handling and contiguous pile storage, credited to Jesse Lew. See JesseSort (#747).
- Add runtime-length exclusion arrays to
NextEnumExceptfor interface and concrete generators. See Random Generators (#742). - Add
BitOps, a public home for popcount, trailing zero count, floor log2, highest-bit isolation, power-of-two detection and power-of-two ceiling; the private copies inBitSet,ImmutableBitSet, the sorters and the sprite/enum tools now call it. See Helper Utilities (#742). - Add reusable protobuf read limits for encoded bytes, length-delimited fields, tag counts, nesting and cumulative packed-element counts across nested messages. See Serialization (#647).
- Add reusable JSON read limits for nesting, per-container element counts and string or property-name lengths, with a parameterless constructor for generic factories. New
Serializer.JsonDeserializeoverloads refuse an over-budget document before decoding it. See Serialization (#647). - Add
WUH017warnings for a same-objectGetComponentcompared against null, whereTryGetComponentanswers the same question without Unity's Editor allocation. See Analyzers (#741). - Add opt-in exact sprite-art colliders, reusable alpha-mask reading and pixel-boundary tracing, with alpha and minimum-area controls. See Sprite Colliders (#715).
- Add editor-only sprite keyframe times and threshold-based last-motion detection, with renderer-path filtering and selectable bounds edges. See Sprite Animation Motion (#717).
- Add cross-assembly WallstopProto subtypes with static dispatch, inherited private-member support, and collision checks at compilation, project assembly and startup (#612).
- Add malformed-state checks to WallstopProto raw remainder reads; failed reads leave an empty span and preserve the reader position (#647).
- Add unbounded retention, custom key comparers and opt-in removal ownership transfer to
Cache<TKey, TValue>. Shared cache limits now resize existing entries immediately (#703). - Add
WUH015warnings for invalid Unity callback signatures andWUH016warnings for hidden inherited callbacks, using resolved types and inheritance (#654). - Add
Helpers.ClearTagCache(), which drops every cachedHelpers.Find(tag)lookup. A miss costs oneGameObject.FindGameObjectWithTag(#643). - Add
Buffers.ComparerPoolMaxDistinctEntries, which bounds how many distinct comparers the set and dictionary pool caches retain; the least recently used is evicted past it. See Comparer-Keyed Pools (#689). - Add
StringWrapper.MaxCachedWrappersandStringWrapper.CachedCount, which bound and report how many distinct strings the wrapper cache retains; the least recently requested is evicted past it (#694). - Add
SerializableTypeCatalog.MaxCachedFilterResults, which bounds how many distinct type-picker search terms are cached; the least recently used is evicted past it (#694). - Add
PoolTypeResolver.MaxCachedTypeNames, which bounds how many distinct type-name spellings the pool type resolver retains (#643). - Add
PoolStatistics.ApproximatelyEquals(other, tolerance), matching the overloadPoolFrequencyStatisticsalready carries now thatEqualsis exact (#639). - Add
RestorableGlobal<T>, which lends a global for ausingblock and takes it back exactly once, at any nesting depth and in any disposal order, so a copied scope can no longer restore a stale value.WUH014reports the disposable structs that still assign inDispose. See Restorable Globals (#627). - Add Tools > Wallstop Studios > Unity Helpers > Run EditMode/PlayMode Tests With Summary, which starts a test run and writes a pollable summary file to
Temp/for a script driving the editor from outside. See Test Run Reporter (#625). - Ship four validation rules with the package -- unfilled
[WNotNull]slots, broken serializable dictionaries, empty animation keyframes, and script files that misname what they bind -- so the Asset Validation window and CI batch mode have something to check out of the box. See Asset Validation (#675). - Extend
WUH003tois nullandis not nullon aUnityEngine.Object. Both are CLR null tests, so a destroyed object reads as alive exactly as it does through?.. A type pattern is not reported, because it is not a null test at all. See Analyzers (#621). - Add Tools > Wallstop Studios > Unity Helpers > Authored Assets, reports for a type nothing can author, an unfilled
[WNotNull]slot, aSerializableDictionarythat lost its pairing, and an empty keyframe. Reading is never destructive. A file a scan cannot read is named. See Authored Asset Validation (#618, #619, #620, #624, #631, #670). - Add Report Stale Serialized Keys and Repair Stale Serialized Keys In Selection, for keys an asset still records that no field claims. The repair rewrites one asset at a time, undoes any rewrite that loses objects or throws, and names which. See Authored Asset Validation (#617, #679).
- Add
WUH012, which warns when a walk of a serializedList<T>orT[]of references dereferences a row without testing it. Deleting the asset a row names leaves the row behind, empty. Compaction counts as the guard. See Analyzers (#628). - Add
WUH011, which warns whenSerializedStringComparer.compareModechanges after the comparer has been handed to a collection; freeze the comparer to keep existing keys reachable. See Performance Analyzers (#663). - Add
SerializedStringComparer.Freeze(), which pins the comparison rule so a later write tocompareModecannot re-bucket a dictionary already built with it -- keys sorted under the old rule become unreachable under the new one, with nothing thrown (#646). - Add
RTree2D.GetElementsWithCentersInBoundsandRTree3D.GetElementsWithCentersInBounds, which return only elements whoseBounds.centeris inside the query box, so a sweep over a tiling visits each element once. See Spatial Tree Semantics (#658). - Add
Objects.StableHash32V1(bytes, seed), an FNV-1a hash whose answer depends on nothing but its arguments, so it is safe to persist or compare across machines. See Stable Hashing (#639). - Add
ApproximatelyEquals(other, tolerance)toCircle,Sphere,Line2D,Line3DandPoolFrequencyStatistics, so a computed shape can still be compared loosely now thatEqualsis exact. The tolerance is the whole permitted per-component difference at every magnitude (#639). - A negative, infinite or NaN tolerance makes
ApproximatelyEqualsreturn false, and a non-finite component compares exactly, so a shape with an infinite radius is still approximately equal to itself (#639). - Add
FastVector2Int.HasSameXYandFastVector3Int.HasSameXY, which say what the cross-dimensionalEqualsoverloads did. Those overloads are now[Obsolete]and go away in 4.0 (#639). - Add
Helpers.NameHashCode, the hash code that agrees withHelpers.NameEquals(#639). - Add
TryNextUint,TryNextUlong,TryNextDoubleandTryNextGaussiantoAbstractRandom, reporting exhausted sampling, including nested integer rejection, withfalseand a default output. Existing methods retain degraded sampling (#638). - Add
SpatialHash2D.TryInsertandSpatialHash3D.TryInsert, which report whether a position was stored.Insertdrops a non-finite position silently: one that wentNaNin physics is data, not a call the caller got wrong, so it never aborts the frame. See Query Contract (#642). - Add
WUH010, which warns when a dictionary is read through its key indexer -- it throws for an absent key whereTryGetValuereports, andmatch.Groups["name"]quietly hands back a group that never matched. Off by default, unlike the rest of the family. See Analyzers (#652). - Add
WUH003andWUH004, which warn when?.,?[],??or??=is applied to aUnityEngine.Object-- those operators test CLR null, so a destroyed object walks straight through the guard -- and when an NUnit null assertion passes over one. See Analyzers (#621). - Add
WUH005, which warns onUnityEngine.Random, whose state no test can set or read without disturbing every other caller. It points atPRNG.Instanceand the seedable generators behindIRandom. See Analyzers (#622). - Add
WUH006andWUH007, which warn when anEffectHandleor a coroutine handle is discarded -- each is the only thing that can undo the call it came from. The coroutine rule matches the return type, so this package's delay helpers count too. See Analyzers (#623, #626). - Add
WUH008, which warns when aTryXxxoutvalue is read on a path where the call's result was never tested. Only the BCL promises to writedefaulton a miss. See Analyzers (#629). - Add
WUH009, which warns when a teardown override callsbasebefore statements that still need what it released. Setup chains base-first; teardown chains base-last. See Analyzers (#630). - Add
Span<T>overloads of theIListhelpers -- shuffling,Shift/rotate,TrySwap,Fill, predicate search,TryFindAllandTryPartition-- so astackallocbuffer reaches them with no allocation. They share theIListbodies, so seeded output is unchanged. See Span Operations (#632, #651). - Add
WPROTO047, a warning when a type inherits its WallstopProto contract and declares[WProtoMember]of its own without carrying[WProtoContract]. The code works; the attribute is what tells a reader those members are on the wire (#613). - Add a time-sliced validation engine with per-profile change/save triggers, build gates and JSON/JUnit reports. Failed, cancelled and incomplete runs retain the last complete result. See Asset Validation (#288, #648, #669).
- Add the themed Sentinel workspace with rules, profiles, safe fixes, suppressions and editor status. Preferences can disable all Sentinel editor work and UI. See Asset Validation (#288, #634, #655, #669).
- Add
[WProtoSubtype(typeof(Base))], so a subtype joins a WallstopProto hierarchy without picking a field number. The editor assigns and commits the number on the next reload, and Assign WallstopProto Subtype Tags retires a removed one -- hand-numbered or not -- so it is never reused. See Polymorphism (#587, #601, #606). - Add
Sfc64Random, the Small Fast Chaotic generator: a published-pedigree 64-bit generator with a very small hot path that answersNextUlongin one state advance. See Random Generators (#516). - Add
[WProtoReserved], which records a field number or name a removed[WProtoMember]held -- or, on an enum, a removed member's value. Taking one again is a build error, not a save that reads back as the wrong thing, and the exported schema carries the proto3reservedlines. See Retiring a member (#608, #609). - Subclass a
[WProtoContract]and it serializes -- no attribute needed, the editor commits its field number. Write[WProtoNotSerialized]on one that never reaches the serializer. See Polymorphism (#613). - Add a proto schema exporter: Tools > Wallstop Studios > Unity Helpers > Proto Schema Exporter writes
proto3for your[WProtoContract]types, so anything downstream can read your saves. Search and tick the exact types, name a package, and write one file or one per assembly, namespace or type (#424, #595). - Reading a repeated WallstopProto field allocates from the elements a payload delivers, not the count it states, so splitting one across many runs cannot amplify (#647).
- Add strict UTF-8 validation to WallstopProto strings and Uri: wire bytes that are not valid UTF-8 refuse the payload as malformed instead of decoding to replacement characters, as proto3 requires (#580).
- Add
IntMap<TValue>, an int-keyed open-addressing map measured at 1.26x–2.19xDictionary<int,int>on hit-heavy lookups, with no comparer indirection on the lookup path. See Data Structures (#578). - Add
charandUrisupport to WallstopProto, byte-compatible with both protobuf-net majors, so links and code units inside a save no longer need a surrogate.DateTimeOffset,IntPtr,UIntPtrandTypestay refused, with the reasons in the serialization guide (#399). - Add
stackTrace: falsetoLog,LogWarnandLogError, for a diagnostic that repeats once per object or once per frame. Unity captures a stack trace for every log by default, measured at 178.4 us against 13.3 us without one. See Logging Extensions (#564). - Add
RandomGeneratorMetadata.Period, so every generator states its period where a caller can read it, and the Random Generators table now carries a Period column that cannot drift from it. A published period is quoted; where none exists the value states the measured live state width instead (#516, #285). - Add
SerializedMemberNames, which converts between a property's source name and the<Name>k__BackingFieldUnity serializes it under, for anyone writing a drawer that resolves a member by name (#550). - Add
WUH002, which warns when a Unity-serialized field resolves onto a collection of collections -- Unity drops every inner value and reports nothing. Covers public fields of nested[Serializable]types too. A warning at most, and switchable off. See Performance Analyzers (#548). - Add a non-throwing sibling for all eight ranged draws,
NextIntInRangethroughNextDoubleInRange. They answer the low bound where the strict overloads reject an empty range, which is what an authored min/max pair set equal produces. See Ranges a Designer Authored (#546). - Add
WUH001, which warns when a lookup factory is passed as a method group -- C# rebuilds that delegate on every call, hits included. CoversConcurrentDictionary,ConditionalWeakTableand this package'sIDictionaryextensions. A warning at most, and switchable off. See Performance Analyzers (#538). - Add
Serializer.ProtobufSurrogatesReady(), which reports whether protobuf will write the byte layout this package documents, and names the types it will not. A game can check it before its first save instead of reading a startup log. See Checking the surrogates took effect (#531). - Add
ReflectionHelpers.CreateTypedArray(), which builds an array whose element type is known only at run time about twice as fast asArray.CreateInstanceplusArray.SetValue(#529). - Add
string.Slugify(), which produces a URL- and filename-safe key: lowercase ASCII, single hyphens, accents folded so"Café"is"cafe".ToKebabCasekeeps punctuation and accents and is not a substitute. See Slugs (#386). - Add
TrackedObjectPool<T>, a pool forUnityEngine.Objectitems whose lifetime ends in a callback rather than a scope.Disposedestroys what is still checked out instead of stranding it in the scene, and a destroyed item is never pooled or handed out. See Pooling Unity Objects (#523). - Add
VisualElement.IsShown(),IsShownResolved(),IsWithin(),FocusedElement()andTryFocus().Focus()is silent when it does nothing, and Unity's ownContainsexcludes the element itself, so "did focus move" and "is the focused element mine" both had to be written by hand. See UI Toolkit Extensions (#513). - Add
Xoshiro128StarStarandXoshiro256StarStar, the first generators here whose every output bit is strong enough forNextBooland power-of-two masks.Xoshiro256StarStaralso answersNextUlongin one state advance instead of two. See Random Generators (#510, #285). - Add release notes to the Unity Package Manager: the package now carries its changelog section and a Changelog link, so Version History shows what changed instead of nothing (#421).
- Add AOT protobuf support for
ValueTuple:Serializer.ProtoSerialize((7, 1.5f))threwExecutionEngineExceptionon an IL2CPP player and now goes through a generated formatter. JSON of a tuple stays editor-only. DefineWALLSTOP_DISABLE_VALUE_TUPLE_SERIALIZATIONto opt out. See Tuples serialize on IL2CPP (#289). - Add
SerializableValueTuple<T1, T2>andSerializableValueTuple<T1, T2, T3>, so a tuple survives Unity serialization -- a(int, float)field is silently dropped, taking any authored contents with it. Byte-identical toValueTuplein protobuf and JSON. See SerializableValueTuple (#289). - Add a comparer constructor to the
Dictionary<TKey, TValue>nested inSerializableDictionaryBase, sobase(new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase))-- the documented way to seed a comparer -- compiles inside a subclass, where that type shadows the framework one (#476). - Add
long,floatanddoubleoverloads ofWrappedAdd, and alongWrappedIncrement, so an angle or a normalized phase wraps with the same call a ring-buffer cursor does. See Numeric Helpers. - Add
WDoomRandom, an index-into-array generator inspired by id's original DOOM technique: one array read per draw, and one index of state, so a save file records the whole generator. It repeats every 1024 draws -- for retro feel and replays, never sampling. See Random Generators (#281, #516, #524). - Add
SortAlgorithm.YamandIList<T>.YamSort(), a stable sort that approaches O(n) on sequential and reverse-sequential data. Adapted from YamSort by Gary Gende. See IList Sorting Performance (#461). - Add
[SingletonCreation(SingletonCreationPolicy.NeverCreate)], which stopsRuntimeSingleton<T>.Instancecreating a bare stand-in when no instance exists. It returnsnulland names the type instead. See Controlling on-demand creation (#321). - Add
AnimationClip.GetSpriteFramesFromClip(), which pairs every sprite a clip references with the binding that supplies it, aGetSpritesFromClip(path, propertyName, type)overload that filters on that binding, andUnityExtensions.SpriteBindingProperty. Editor-only. See Sprites from an AnimationClip (#451). - Add
AssetChangeDetectionUtility.Enabled,ResetEnabledToDefault()andEnabledScope(bool)to turn the[DetectAssetChanged]watcher on and off. The returnedAssetChangeDetectionEnabledScoperestores the previous setting on dispose (#327). - Add
SingleThreadedThreadPool.DrainAsync(), which closes the pool and waits for queued work to finish instead of dropping it, plusIsAcceptingWork(#318). - Add
SerializableList<T>, a list that survives Unity serialization inside another serialized collection (#314). - Add the Inspector drawer for
SerializableDictionary<TKey, TValue, TValueCache>, which previously had none (#314). - Add
WGuid.TryCreate(Guid, out WGuid), which wraps an existing GUID without throwing when it is not version 4 (#437). - Add
Enum.TryConvertToUInt64()andTryConvertToInt64(), which convert any enum value to its 64-bit bit pattern without overflowing on negative or very large members. - Add
ColorQuantization, the one place an 8-bit channel becomes a normalized float and back.ToNormalizeddivides by 255, the same bits Unity'sColor32conversion gives;ToByterounds to the nearest channel;ToThresholdBytebounds a float cutoff. See 8-bit Channels and Normalized Floats (#466, #565). - Add
ColorQuantization.AreSameColor(), the one definition of "the same color" in the package: two colors match when they encode to the same 8-bit channels, which is the only rule an equality comparer can hash (#472). - Add
ColorContrast, which answers whether text is readable on a background the way WCAG defines it:RelativeLuminance,ContrastRatioandReadableTextColor. See Readable Text on Any Background (#471). - Add
TextureResampling, the one place a resampler picks a source pixel and mixes colors of unequal opacity:BilinearSourceCoordinate,NearestSourceIndex,PremultiplyandUnpremultiply. See Resampling a Texture (#470). - Add
SemaphoreSlim.Acquire(),TryAcquire()andAcquireAsync(), so a permit can be taken withusinginstead of afinally. The lease releases its permit exactly once, however many copies of it exist. See Semaphore Leases (#358). - Add
DurableFile, whose writes cannot leave a truncated file behind:TryWriteAllText,TryWriteAllBytes,TryAppendAllText,TryCopy,TryDeleteand text/copy async equivalents. See Durable Writes for Player Data (#319). - Add
WallstopProto(preview), an IL2CPP-safe, protobuf-net-compatible serializer. Hand-written formatters must consume valid complete payloads; enum registrations must match the actual enum type, width and signedness. See WallstopProto (#343, #647, #722). - Add steady-state allocation-free
IndentLevelScopeand internal label-width scopes that remain copy-idempotent and restore the newest active editor-global value even when nested copies are disposed out of order (#627). - Add facade-owned nested-message size plans to WallstopProto. Measured prefix widths avoid repeated large-payload moves, direct writers keep canonical back-patching, and a formatter that underwrites its measurement is refused (#383, #504).
- Add the WallstopProto source generator (preview): annotate a type with
[WProtoContract]and its formatter is generated into your assembly and registered for you. A contract it cannot serialize is a build error naming the fix. See The generator (#343). - Add
WProtoFormatterProvider, which resolvesIWProtoFormatter<T>without reflection. Built-ins coverFastVector2Int,FastVector3Int,WGuid,RandomState,DateTime,TimeSpan,Guidanddecimal; later registrations replace them (#343, #379, #399). - Add support for a
[WProtoMember]that is another[WProtoContract], including the same type again, so a tree or linked list serializes without a hand-written formatter. A sub-message merges into the value your constructor gave the member. See Contracts that hold other contracts (#380, #475, #485). - Add WallstopProto support for arrays and any
ICollection<T>with a public parameterless constructor andAdd, includingstructcollections, which are not boxed on the write path.OverwriteList = truereplaces the constructor's collection instead of appending. See Collections (#343). - Add WallstopProto support for
LinkedList<T>,Queue<T>,Stack<T>,ReadOnlyCollection<T>,ReadOnlyDictionary<K,V>and members declared as the matching interfaces. AStack<T>round-trips in its original order. See Collections (#395). - Add WallstopProto support for nested and jagged collections —
int[][],List<List<int>>,Dictionary<string, List<int>>and deeper — which protobuf-net refuses. See Collections (#399). - Add WallstopProto support for rectangular arrays —
int[,],int[,,], and one anywhere a collection can go — which protobuf-net refuses. Dimensions travel with the elements, sonew int[0, 5]keeps its shape. See Collections (#434). - Add WallstopProto maps: a
Dictionary<,>,SortedDictionary<,>or your ownIDictionary<,>is written as a protobuf map, byte-compatible with protobuf-net. See Maps (#387). - Add WallstopProto polymorphism:
[WProtoInclude(tag, typeof(Subtype))]round-trips a member typed as a base as its concrete subtype, with no reflection. An unrecognized include tag is skipped, so saves from newer builds still load. See Polymorphism (#390). - Add
[assembly: WProtoSurrogate(typeof(Real), typeof(Surrogate))]for wire shapes on types you cannot annotate, plus open generic pairs that cover each closed construction. Package pairs serve two- and three-componentValueTuplemembers and map keys. See Surrogates (#391, #399). - Add
WPROTO038, a build error when an open surrogate pair has mismatched arity, openness, or stricter generic constraints than its real type (#399). - Add
[assembly: WProtoRootMarshal(typeof(Real), typeof(Formatter))], which gives a type a different wire shape as the root of a serialization than as a member of a contract. See Root marshals (#402). - Add
[assembly: WProtoDeclaredRoot(typeof(IRandom), typeof(AbstractRandom))], which names the contract serving a value held as an interface. It applies at the root only. See Declared roots (#403). - Add generic
[WProtoContract]support: each closed construction gets its own encoding and is registered automatically, so your ownBox<YourStruct>needs no manual registration. A member typed as the contract's own parameter merges a sub-message a payload carries twice. See Generic contracts (#385, #484). - Add
Tools > Wallstop Studios > Unity Helpers > Validate Serialized Fields In Selection, which reports apublicor[SerializeField]field Unity silently drops -- aDictionary, a(int, float), aNullable<T>-- and names the package stand-in to use instead. See Serialized Field Validator (#497). - Add generated JSON converters, so
Deque<int>andSerializableDictionary<string, int>serialize in an IL2CPP player instead of throwingExecutionEngineException. DefineWALLSTOP_DISABLE_GENERATED_JSON_CONVERTERSto opt out. See Generic containers get their converters generated (#501). - Add bounded pooled scratch lists to generated JSON array converters, so a warm read only allocates its returned array and an untrusted large array is not retained by the pool (#504).
- Add a metamorphic battery over WallstopProto: payloads rewritten into other legal spellings -- fields reordered, an undeclared field injected, a sub-message split into two that merge -- must decode unchanged (#462).
- Add a differential fuzz over WallstopProto: random valid values for repeated, map and polymorphic contracts, checked in both directions against protobuf-net 2.4.9 and 3.2.56 (#437).
- Add
WProtoMemberAttribute.DataFormat, which selects a signed integer member's encoding.WProtoDataFormat.ZigZagis protobuf'ssint32/sint64, where a small negative value costs one byte instead of ten. See DataFormat (#527). - Add
WPROTO037, a build error forDataFormat = ZigZagon a member with no such encoding. Onlysbyte,short,intandlonghave one, so anywhere else the annotation would have been dropped and the member written as theint32it declined (#527). - Add
WPROTO035andWPROTO036, warnings for an[assembly: WJsonConverter]pairing that cannot be closed, and for two pairings claiming one type (#501). - Add
WPROTO034, a warning for a lifecycle hook declared on a subtype of a[WProtoInclude]chain. protobuf-net 3 never runs one, and protobuf-net 2 runs it in the opposite order to WallstopProto, so declare it on the root instead. See The generator (#500). - Add
WPROTO033, a warning for aSkipConstructorcontract holding a field that is initialized where it is declared and is not a[WProtoMember]. That instance is allocated uninitialized, so the field arrives at its type's default. See The generator (#494). - Add WallstopProto support for
readonlyfields and get-only properties, previously a build error. The generator emits a private constructor into yourpartialtype, keeps the parameterless one it would otherwise have had, and a member the payload omits keeps the value your constructor gave it (#394, #491). - Add
WProtoContractAttribute.SkipConstructor, which reads a contract without running any constructor its author wrote, matching protobuf-net and still working under IL2CPP. A collection member holds exactly the elements the payload carried, unless a parent's constructor supplied the instance (#394). - Add
WProtoContractAttribute.IgnoreListHandling, which writes a contract that also implementsICollection<T>as a message. Without it such a member is a build error naming both readings (#343). - Add
IWProtoScalarFormatter<T>andWProtoGeneric<T>, which make a value's wire type expressible so a generic contract can encode a member whose type it cannot see.IWProtoFormatter<T>is unchanged. - Add
IWProtoPolymorphicFormatter, which lets a formatter report the runtime types its[WProtoInclude]chain writes, soSerializercan serve a value held as its base (#403). - Add
IWProtoConditionalFormatter, so a formatter reports whether it can encode a closure before anything is written. An element WallstopProto cannot encode now falls back to protobuf-net instead of throwing mid-serialization (#402, #416). - Add
WProtoFacade.TryDeserializeAs(), the read that names a concrete type.Serializer.ProtoDeserialize<T>(byte[], Type)routes through it (#403). - Add
SerializationCapacityLimits, the bound a deserializer applies to a capacity a payload claims, withMaximumRestoredCapacity(default 1,048,576) for games whose saves are legitimately larger. An empty rectangular array whose axis exceeds it does not deserialize; one carrying elements is unaffected (#437). - Add
WProtoReader.CountPackedElements(),WProtoArrayBuilder<T>andWProtoRepeated.Reserve(), so a hand-written formatter can size a repeated field once instead of growing it. Generated formatters already size theirs from the packed run (#398). - Add
WProtoReader.TryReadPackedRun(), which reads a packed repeated field's payload as its own reader without spending a nesting level. - Add
WProtoMessageAccumulatorand the matchingWProtoReader.TryReadMessage(payload, formatter, out value)overload, so a hand-written formatter can merge a sub-message field a payload carries more than once (#475). - Add
IWProtoMergeFormatter<T>and aWProtoReader.TryReadMessage(payload, formatter, seed, out value)overload, so a hand-written formatter can decode into a value the caller already holds.IWProtoFormatter<T>is unchanged; a formatter that skips this reads exactly as before (#485). - Add
WProtoReader.MaxNestingDepth(64), the deepest message nesting a payload may use, so a few kilobytes cannot ask a formatter for thousands of stack frames. A hand-written formatter descends withTryReadMessage(formatter, out value)ornew WProtoReader(payload, in parent), which carry the depth (#343, #377). - Add
WProtoGeneric<T>.CanEncode, which reports whether a closed type argument can be encoded at all,IsMessage, which reports whether it is encoded as a sub-message, and aTryReadValue(ref reader, payload, out value)overload that decodes accumulated occurrences (#484). - Add
WProtoRectangular, the shape check and refusal message a hand-written rectangular-array formatter needs. - Add
WProtoRepeated.NullElement()andNullNestedElement(), which build the exceptions a generated formatter throws for anullrepeated element or inner collection. - Add
WProtoFormatterProvider.UnexpectedSubtype(), which builds the exception thrown for a value whose runtime type its contract does not declare. - Add
WPROTO018, a build error when a[WProtoContract]'s base is one too but is not declared with[WProtoInclude](#394). - Add
WPROTO028, a warning when a closed construction cannot be registered because it closes over aprivatenested type. It is skipped rather than failing the build (#414). - Add
WPROTO030, an informational diagnostic when a protobuf-net contract has no[WProtoContract], so migration is an opt-in worklist. It reads[ProtoContract], a vendored protobuf-net under a renamed namespace, and a[DataContract]with ordered[DataMember]s, and names which matched (#407, #597). - Add
WPROTO031, a warning when assemblies declare different roots for the same type (#419). - Add
WPROTO032, a build error when a member's collections nest more than 64 deep, which is deeper than the reader can read back (#399).
- Ship the runtime assembly with
allowUnsafeCodeoff. Two BMI2 pointer branches guarded by framework symbols no Unity player defines were the only reason it was on, so nothing that ever ran has changed (#637). - Clamp or refuse a capacity a payload claims in
Deque,SparseSet,BitSetandImmutableBitSet. Six bytes claimingint.MaxValuepreviously allocated 8-16 GB and crashed the player. RaiseSerializationCapacityLimits.MaximumRestoredCapacityif your saves exceed 1,048,576 elements (#429).
- Change Editor
MatchColliderToSprite.OnValidateto preserve collider geometry; programmatic callers now useRebuildCollider(). Inspector edits and the match button still rebuild immediately with Undo support. See Sprite Colliders. - Reduce script compilation work for generic protobuf and JSON contracts (#706).
- Change Unity Method Analyzer to read compiler diagnostics, with assembly coverage, explicit recompilation, and navigation to resolved source locations. See Unity Method Analyzer (#654).
- Pool purge sizing now averages every rental sample inside
RollingWindowSecondsrather than the most recent ten thousand, so spike detection reads a longer history on a busy pool (#693). - Restoring a
CyclicBufferfrom JSON whose stated capacity is below the items it carries now keeps every item, matching what both binary paths already did (#637). -
StringWrapper.Disposeno longer removes the wrapper from the cache, and is[Obsolete]. The wrapper is shared with every other holder of the same string, so one borrower'susingblock evicted an entry the rest were still reading. UseStringWrapper.RemoveorClear(#646). - Restrict
Equals(object)onAttribute,WGuid,FastVector2Int,FastVector3Int,SerializableType,SerializableNullable<T>and bothSerializableValueTuplearities to its own type. A boxedfloat,Guid,Type,Vector2IntorValueTuplenever answered true in return; the typed overloads are unchanged (#639). - Change
Attributeto hash itsCurrentValue, so an attribute already sitting in aDictionaryorHashSetbecomes unreachable the moment a modification changes that value. Re-add it after applying one (#639). - Restrict
CompareTo(object)onFastVector2IntandFastVector3Intto its own type, so it can no longer answer 0 for a pairEqualsrefuses. The cross-dimensionalCompareTooverloads are now[Obsolete]and go away in 4.0 (#639). - Say what
Objects.HashCodeactually promises: the mixing is fixed, but each argument contributes an ordinaryGetHashCode, which is process-local for strings, Unity objects and your own types. It was documented as deterministic and offered for save files (#639). - Change
PRNG.Instanceto returnXoshiro256StarStar: anExcellent-rated generator with a published 2^256-1 period and reference, measured even with the previous default. Streams drawn fromPRNG.Instancediffer; construct a generator directly to keep one (#516). - An attribute with only additive modifications recalculates 2.85x faster: 0.4563 us before, 0.1600 us now, measured on
6000.4.6f1. Addition, Multiplication and Override each got a full pass over every modification whether or not any carried that action (#529). - A relational field that finds nothing is ~15x cheaper to assign: 366-431 us before, 25.1 us now, measured on
6000.4.6f1against a control that did not move. Unity captured a stack trace for the error log on every assignment, and for a collection field finding nothing is a normal state (#564, #529). -
RomuDuonow implements published romuDuo, so a given seed produces a different sequence than in 3.5.1. No saved seed orRandomStatecarries a sequence across this change; pin a generator whose stream is unchanged if you need one to (#509). -
DisjointSet.TryGetAllSets()is 2.4x-3.2x faster and allocates no temporary lists. It gathered every element into a per-root scratch list and then copied all of them a second time; elements now go straight into their result list, found through a dense index rather than a hash lookup (#309). -
Serializer.ProtoSerialize(value, ref buffer)no longer allocates a second full payload for theSerializablecollection types. The overload exists so a per-frame serialize allocates nothing (#504). - Serializing a
SerializableDictionaryorSerializableHashSetno longer resolves its protobuf wrapper type by reflection on every call; the type and its constructor are resolved once per element type (#504). - Writing a
WGuidto JSON no longer allocates a 36-character string per value, and reading anAnimationCurvekeyframe no longer boxes itsweightedMode(#504). - Relational fields typed as an interface no longer fetch every component on the object and type-test each one. Unity's own query resolves an interface, so the query is 1.35x-2.49x faster depending on how many components the object carries -- and the child shape pays it once per descendant (#529).
- Relational collection fields typed as a base component --
Collider2D[],Renderer[]-- no longer fetch every component on the object and type-test each one. Unity's own query already resolves a base class, so assignment of a component with three such fields is 9% faster (#529). -
Animator.ResetTriggers()no longer allocates. It readAnimator.parameters, which builds a new array and new element objects on every read -- 53.5 bytes per call for a three-parameter controller. The trigger hashes are now read once per controller (#549). -
GameObject.IsDontDestroyOnLoad()no longer allocates. It readScene.name, which marshals a fresh managed string every call, and the signature reads cheap enough to end up inUpdate. The answer now comes from the scene's handle (#549). -
WGroupandWGroupEndnow accept fields only. They advertised properties, which nothing lays out, so the attribute compiled and drew nothing. Use[field: WGroup(...)]on an auto-property (#550). -
WShowIfnow reads a condition that names a[field: SerializeField]auto-property from the serialized state rather than the live object, so a pending Inspector edit is what it reacts to (#550). - Draw 64-bit values 2.49x faster from
BlastCircuitRandom,RomuDuo,SplitMix64,WyRandomandXoshiro256StarStar-- one state advance instead of two.NextUlong,NextLongandNextDoublereturn a different sequence for a given seed; 32-bit draws are unchanged. See Seeded streams that moved (#509). - Return the strong half of
XoroShiroRandom, raising it fromFairtoGood: itsNextBool()was predictable from 128 draws, and PractRand now runs clean through 8GB where it used to fail at 16MB. Every draw changes for a given seed (#509, #285). - Stop the serializer allocating a delegate on every collection serialize. Four cache lookups built their factory per call rather than reusing one, costing 106-116 bytes each time, cache hit included (#504).
- Assign a hierarchy 30% faster when most of its components have no relational fields:
AssignHierarchytook a lock per component just to answer "does this type have any", costing 59.7 ns per component against 41.5 ns now (#529). - Deserialize a
SerializableDictionary,SerializableSortedDictionary,SerializableHashSetorSerializableSortedSetfrom JSON without reflection: the converters looked their backing fields up on every read, costing 1.5 us of a 17.6 us 16-entry read (#504). - Stop
[ChildComponent]and[ParentComponent]collection fields allocating aComponent[]on every assignment, so a scene full of components no longer builds garbage duringAwake(#534, #529). - Assign relational array fields faster: a
[SiblingComponent]array field costs 22% less, and sibling collection fields no longer allocate per call.ListandHashSetfields are unchanged (#529). - Write
FastVector2IntandFastVector3Intcomponents assint32, so a negative coordinate costs one byte instead of ten. A 1,000-cell tilemap centred on the origin falls from 14,690 to 3,870 bytes. Payloads written by 3.5.1 still read (#527). - Stop
FastVector2IntandFastVector3Intwriting their cached hash, which every reader already recomputed: a 1,000-cell tilemap falls from 14,167 to 5,870 bytes. Payloads written by 3.5.1 still read; a 3.5.1 build cannot read new ones (#519). - Remove an item from a
SpatialHash2D/SpatialHash3Dbucket by swapping the last entry into its place rather than shifting the tail. Query results were never ordered and are not now; the order two items in one cell come back in can differ from before. - Say what has actually been measured about
IllusionFlow,StormDropRandom,PhotonSpinRandom,FlurryBurstRandomandBlastCircuitRandom. All five are now verified clean through 8GB of PractRand 0.95 here, rather than described by an author's claim on a repository that is offline. Ratings are unchanged (#286, #516). - Take every
Bounds,BoundsInt,RectandColorextension receiver byin. Calling one no longer copies the struct at your call site, whichErrorProne.NET.Structsreported as anEPS06warning you could not fix without giving up extension syntax (#512). - Make
PooledArray<T>andPooledResource<T>readonly struct, so readingarray,lengthorresourceand callingDispose()no longer copies the wrapper first (#512). - Lower
XoroShiroRandomtoFairandRomuDuotoGood. Bit 0 ofXoroShiroRandomfollows a linear recurrence of order 128, so itsNextBool()is predictable from 128 draws; preferXoshiro128StarStarwhere single bits matter (#509, #286). - Lower
SquirrelRandomtoFair. It fails PractRand at 1GB, reproducibly across four seeds; it stays a good fit for the table lookups it was designed for (#286). - Read cancellation-aware JSON files through a pooled scratch buffer and deserialize the pooled stream's valid segment directly. Large save files no longer allocate an extra full-payload copy before decoding (#504).
- Speed up every pooled-buffer operation on blittable elements -- the sorts,
Shuffle,Filland the geometry helpers -- by clearing a returned array only when its element type can hold a reference. A reference element is still never left rooted in the pool (#482). - Speed up
ProtoSerialize,ProtoDeserialize,ProtoEqualsandNextEnumby resolving each one's type questions once per closed generic instead of on every call. Measured at 8.5x for that check alone on reference types (#346). - Every
IList<T>sort now runs over an array rather than the list's indexer, so sorting aT[]is 2.5x to 5.4x faster and needs no copy. Any other list is copied through a pooled buffer and back. See Where the Time Actually Goes (#463). - Speed up
IList<T>.Reverse(26x-37x),Shift/RotateLeft/RotateRight(2.7x-36x),Fill(3.6x-38x),Shuffle(1.5x-2x) and predicateIndexOf/LastIndexOf(2.4x-3.1x) with bulk array operations. See Bulk Operations on a List (#480). - Enable WallstopProto by default for the runtime assembly: every
Serializer.ProtoSerializeandProtoDeserializeoverload, includingref bufferandforceRuntimeType: true, uses generated formatters when available and falls back to protobuf-net (#343, #403). - Serialize thirty of this package's own contracts through WallstopProto when
WALLSTOP_PROTOis defined, includingAbstractRandomand all seventeen generators. Saved data is unchanged (#394). - Serialize
SerializableHashSet,SerializableSortedSet,SerializableDictionary,SerializableSortedDictionary,Deque,CyclicBufferandSparseSetthrough WallstopProto whenWALLSTOP_PROTOis defined. Saved data is unchanged (#402). - Serialize the fourteen surrogated Unity and package structs --
Vector2,Bounds,Resolution,Parabola,ImmutableBitSetand the rest -- through WallstopProto when one is the root of aProtoSerialize. An IL2CPP player threw or silently returned a default. Existing saves still read (#696). - Allow a
structbacking set inSerializableSetBase<T, TSet>: thewhere TSet : classconstraint is gone (#388). - Report a read of the write-only
GameObjectandTouchJSON converters asNotSupportedExceptioninstead ofNotImplementedException(#437). - Ship the bundled
System.Text.Jsonand friends only on editors that do not provide them; Unity supplies its own from 6000.5. See Bundled Assembly Conflicts (#331).
- Fix reusable tag and effect query buffers retaining stale entries when a target, handler, or requested tag is unavailable (#633).
- Fix
LineHelper.SimplifyPreciseleaving a supplied buffer stale for null or short paths, and allow both polyline simplifiers to use their input list as the destination (#633). - Fix per-destruction array allocation when cosmetic effects remove tracked targets. Cleanup still visits the original target snapshot when callbacks change the target list (#781).
- Fix package compiler and analyzer warnings across supported Unity versions, including the 2021.3 serialization attribute conflict and Unity 6 obsolete build-target calls (#766, #770).
- Fix the seven UI Toolkit progress controls failing to compile on Unity 6000.6. Their UXML tags and attributes remain available on Unity 2021.3 and newer (#759).
- Fix pooled serialization writes accepting negative or oversized advances and overflowing capacity calculations (#760).
- Fix caught exceptions losing their type, inner exceptions, and stack trace in diagnostic logs (#762).
- Fix global budget enforcement running pool purge and disposal callbacks while holding the registry lock (#752).
- Fix a disposed pooled serialization writer hanging when code tries to use it again (#753).
- Fix the protobuf byte-writer pool abandoning its rented buffer on purge: writers now return their
ArrayPool<byte>rental when they leave the pool, and the pooling guide documents when a pool needsonDisposal(#749). - Fix collection-inspector button textures leaking across script reloads. See Property Drawers (#648).
- Fix unbounded sprite preview retention in Animation Creator and Animation Event Editor while preserving full-resolution previews. See Animation Tools (#648).
- Fix
WyRandom.Copy()losing pending Gaussian, bool, and byte values, so a copied generator continues the same mixed draw sequence (#638). - Fix malformed
SparseSetbinary saves silently losing invalid element IDs or enlarging their universe for duplicates. Both binary readers now refuse invalid elements before restoring the set (#647). - Fix explicit-type protobuf collection reads bypassing wrappers, preserving stored elements and capacity rules (#647).
- Fix infinite-bound random draws returning NaN or excluded endpoints, and stop exhausted sources from multiplying retry budgets (#638).
- Fix exact relational component matching when
AllowInterfacesis disabled, including concrete types that are not sealed (#733). - Fix editor windows and collection drawers retaining owned serialized state and copied previews after refresh, close or failed initialization; shared Unity previews remain available (#734).
- Fix cleared runtime singletons being returned again before frame-end destruction. Immediate lookup selects a live replacement or respects
NeverCreate(#729). - Fix finite extreme coordinates disappearing from spatial queries or choosing farther nearest neighbors. Immutable trees preserve accepted finite entries across overflowing bounds and distance calculations (#720).
- Fix runtime and ScriptableObject singleton reset callbacks interrupting cleanup or recursively invoking themselves. Reset completes after callback failures and rejects worker-thread access before changing state (#723).
- Fix stuck progress bars after failed prefab checks, sprite and texture batches, atlas operations, reference replacement, and animation copy or delete cleanup (#648).
- Fix infinite stored geometry hiding finite spatial-tree entries. Constructors exclude non-finite positions and bounds edges while preserving source identities; infinite query radii remain supported (#718).
- Fix NaN guards in random sampling, sprite extraction, parabola math, spatial queries and cache timing. Nonfinite coroutine intervals use the existing one-frame delay. See Numeric Guards (#716).
- Fix package stylesheet and asset paths for local checkouts outside the Unity project. Cached packages resolve through their package identity, and similarly named sibling projects no longer count as project content (#655).
- Fix child-component binding to select nearer matches first for single fields and capped collections (#709).
- Fix typed reflection invokers for inherited receivers and struct interface methods. Receiver-specific caching and exact return-type validation prevent incompatible delegates (#644).
- Fix
Range<T>.Overlapsfor coincident open intervals, excluded touching endpoints, and empty or inverted ranges (#707). - Fix SLRU cache segment accounting during eviction and preserve protected-segment limits after resizing or clearing (#703).
- Fix Image Blur batches to restore source texture import settings, release temporary textures, and show one completion message per batch (#648).
- Fix
FluxSortoverflowing the stack on a list sorted by a shared key. 100,000 equal values recursed 47,572 frames deep; aStackOverflowExceptionis caught by nothing (#645). - Lower a deserialized
BitSet's capacity to the words the payload actually delivered, so corrupt saves fail reads safely and later writes still grow. An index too large to represent now returns false instead of throwing (#647). - Fix inspector button colours leaking a texture per intermediate shade: dragging a
[WButton]palette colour picker minted a 1x1 texture the editor never released (#701). - Bound the drawer caches that grew once per inspected object -- foldout animations, measured property widths and built foldout keys -- so a long editor session no longer retains every object it ever showed (#701).
- Fix
StringWrapper.Getkeeping every string it was ever given alive for the process, so wrapping a value built from gameplay grew without bound. A wrapper re-created after an eviction still equals the one it replaced (#694). - Fix
SerializableTypeCatalog.GetFilteredDescriptorsretaining a filtered copy of every project type for each distinct search term, so typing a name one character at a time kept an array per prefix (#694). - Fix
TagHandler.RemoveTagthrowing when a tag-removed handler removed another tag through the same buffer, which left the first tag raised. It also returns the handles it removed rather than the nested call's (#640). - Fix a cosmetic component destroyed by a sibling's callback aborting the whole effect, so an instant effect left its tags raised and applied no modifications (#640).
- Fix
AttributesComponent.ForceApplyAttributeModificationsandForceRemoveAttributeModificationsthrowingNullReferenceExceptionfor a handle whose effect is missing (#640). - Fix
PoolStatistics.Equalscomparing three rates within a tolerance, so a value did not equal itself when a rate wasNaNand equality was not transitive. It is exact now, withApproximatelyEquals(other, tolerance)for a loose compare (#639). - Fix
AttributeEffect.EqualsandCosmeticEffectData.GetHashCodethrowingMissingReferenceExceptionfor a destroyed asset, which could throw out of a dictionary lookup (#639). - Fix
AnimatedSpriteLayer.EqualsthrowingNullReferenceExceptionwhen either side was a default value, so comparing two entries of a fresh array threw (#639). - Fix a default
ImmutableBitSetcomparing unequal to an empty one, so a default value did not equal its own round trip (#639). - Fix
SplitMix64comparing by reference throughEquals(object)while hashing by value, so two generators with the same seed landed in twoHashSetentries (#639). - Fix one element whose position or bounds went non-finite hiding every finite element from
QuadTree2D,RTree2DandRTree3D, andRTree3Dthrowing when no element was finite. A non-finite element is skipped (#642). - Fix a query radius above about 1.8e19 returning elements outside it, on all eight trees and both spatial hashes: squaring it saturated to infinity and disabled the exact distance test (#642).
- Fix
OctTree3Dthrowing andQuadTree2Dsilently answering empty for an unusableboundary. Both now compute bounds from the elements, as passing no boundary does (#642). - Fix
ReflectionHelpers.TryResolveTypecaching names no assembly declares, so a save file naming a renamed type grew the cache for the process. A name that only becomes resolvable after a later assembly load now resolves (#643). - Fix eight editor drawer caches growing for the life of the editor, several holding a
SerializedPropertyor dropdown option and so keeping an inspected asset or scene object alive (#643, #694). - Fix
Helpers.Find(tag)keeping an unloaded scene's objects alive: an entry whose object the unload destroyed is dropped, where before only the next lookup of that same tag would notice.Helpers.ClearTagCache()drops them all (#643). - Fix every pooled rental allocating on a growth boundary for its pool's first ten thousand rents, and each pool then retaining 131 KB of usage samples for the process. Rentals are allocation-free once a pool exists (#693, #643).
- Fix
TextureScale.BilinearandPointreturning a partly scaled texture with no error when a worker slice failed. The failure now reaches the caller, as it already did on a single-core machine (#691). - Fix the
[SiblingComponent],[ChildComponent],[ParentComponent]and[ValidateAssignment]field caches being plain dictionaries, so assigning components from more than one thread could corrupt them (#644). - Fix an
ImmutableBitSetwhose serialized capacity exceeded the words stored beside it throwingIndexOutOfRangeExceptionfromTryGetandAll. The capacity is bounded by the words delivered (#637). - Fix a
[SiblingComponent],[ChildComponent]or[ParentComponent]field declaredprivateon a base class never being assigned, and never logging that it was not. Inherited[WNotNull],[ValidateAssignment]andAttributefields are found now too (#688). - Fix
TextureScale.BilinearandPointreturning their pooled buffers while worker threads were still writing into them, so a scaled texture could carry another caller's pixels (#643). - Fix a dropdown drawer keeping its choices list after returning it to the pool, so another editor could be handed the list it was still displaying (#643).
- Fix five pooled rentals being dropped rather than returned when the code between the rent and the release threw (#643).
- Bound the comparer-keyed set and dictionary pool caches, which kept every comparer a game ever handed them -- and any scene object one captured -- alive for the process.
Buffers.ComparerPoolMaxDistinctEntriessets the bound (#689). - Fix
Partitiontaking two pooled leases per partition and releasing one, so a consumer that did not dispose each partition leaked a lease slot per partition (#689). - Fix a
ScriptableObjectSingletonwhose first load threw staying broken for the rest of the session: the failure is now logged andInstanceanswers null, and clearing the instance recovers (#644). - Fix
Instancehanding back aScriptableObjectSingletonasset destroyed by a reimport, whichHasInstancealready reported as absent. It reloads instead (#644). - Fix
[AutoLoadSingleton]running only in the first play session when Enter Play Mode Options skips domain reload (#644). - Fix an effect that a subscriber removes from inside an apply callback leaving an attribute changed, or a tag raised, with no active effect and no way to undo it (#640).
- Fix removing an effect clearing a tag another active effect still owns, when the first effect only got part-way through applying its own (#640).
- Fix a shared
CosmeticEffectDatabeing applied twice and removed once when an effect is re-applied, which is what the defaultRefreshstacking mode does (#640). - Fix an attribute callback that adds or destroys a sibling
AttributesComponentaborting the rest of the effect's removal, leaving its tags raised and its cosmetics orphaned (#640). - Fix
NextSubsetreturning empty or another caller's values for a source that is not a list. Arrays and lists were never affected (#643). - Fix
PolygonCollider2D.Invertlosing its pooled buffers when Unity rejects a path (#643). - Fix a single-valued
[ChildComponent]field binding a disabled component that the same attribute on a collection field excluded (#644). - Fix every relational field in a scene being left unassigned, silently, when an injected
AttributeMetadataCachehad been destroyed (#644). - Fix a singleton first touched from inside another singleton's
OnInstanceClearedaborting startup cleanup for every singleton after it (#644). - Fix a
defaultNativePcgRandom-- an unassigned field, an array element -- being a stuck generator rather than a seeded one: every draw returned the same value, in range and so not obviously wrong. Explicitly seeded streams are unchanged (#638). - Fix editor work that only ran on a tick an unattended or CI editor may never pump: a failing run exported nothing, drawer caches survived a domain reload, and old selector history was never pruned. All three now run as soon as the editor can answer (#684).
- Fix
IList<T>.Sort, which documented "No allocations": any list that is not aT[]is copied through a pooled buffer. Its docs now state that, and everySortAlgorithmmember says whether it is stable. See Sorting Performance (#645). - Fix
RemoveAtSwapBackmutating single-item, out-of-range and fixed-size lists before throwing. Failed removals now leave the list untouched (#645). - Fix
WallstopGenericPool<T>returning an item into a pool that itsonReleasecallback just disposed. The item is retired exactly once instead of being resurrected inside a dead pool, in both thread-safe andSINGLE_THREADEDbuilds (#643). - Fix
RuntimeSingleton<T>accepting a sibling component that inherited the wrong closed generic base. The mismatch is refused and reported instead (#637). - Fix runtime and ScriptableObject singletons creating or loading a new instance while the application is quitting. An instance already found remains available during teardown (#644).
- Fix races in the package logger. Its global flag, per-object disable set, metadata cache and scratch dictionary were unsynchronized, so a worker could miss a toggle, throw from inside a diagnostic, or print one object's fields under another's name (#646).
- Fix
ToCachedNameandToDisplayNamegrowing a process-lifetime cache for undefined and composite enum values. Only declared members are cached now; anything else is formatted fresh (#646). - Fix
UnityMainThreadDispatcher.RunAsyncnaming the caller's token on every cancellation. It now reports the token that actually cancelled the work, so a caller racing its own timeout against an unrelated one can tell them apart. See Which Token Cancelled (#641). - Fix
SceneHelperscene disposal hanging forever when the dispatcher queue was full or the unload failed. Disposal now faults with the reason, and disposing a scope twice unloads the scene once (#641). - Fix eight editor windows -- Image Blur, Sprite Settings Applier, Texture Settings Applier, Animation Copier, Sprite Cropper, Animation Creator, Sprite Pivot Adjuster and Sprite Sheet Extractor -- leaking a
SerializedObjecton every close. Each now releases it and rebinds when reopened (#641). - Fix
RTree3D.GetElementsInBoundsdropping an element that straddles the query box: it filtered by the element's center whereRTree2Dreturns anything the box touches, so a system ported from 2D to 3D silently stopped seeing them. Both now mean "the element's box touches the query box" (#658). - Fix spatial hash queries that never returned: a huge or infinite radius, or a rect spanning the float range, walked every cell in the volume. Each query now walks the cheaper of the cells and the occupied buckets. See Query Contract (#642).
- Fix nearest-neighbor queries dropping equal-valued elements: all six spatial trees staged results in a value-keyed set, so 64 identical points answered one neighbor. They now return
min(count, n), ordered by distance then insertion index (#642). - Fix negative,
NaNand non-finite spatial query inputs returning an arbitrary subset. Every query clears its destination once and returns empty,RTree2Dzero-range returns only elements the point touches, andRTree3Dbounds queries include the max face like their siblings, zero-size elements too (#642). - Fix
RTree2DandRTree3Drange queries measuring with an epsilon.RTree3Ddropped an element sitting exactly on the query radius along an axis, and both admitted one up to 1e-3 world units outside a small radius. Distances are compared exactly now (#642). - Fix a spatial hash's
Disposedestroying the process-wide buffer pool every other consumer of that element type shares; it now releases only its own buckets. ANaNor infinite cell size is also rejected instead of collapsing the grid (#642). - Fix
Circle,Sphere,Line2D,Line3DandPoolFrequencyStatisticscomparing equal while hashing apart, so a value could vanish from the set it had just been added to. Equality is exact now; useApproximatelyEqualsfor a tolerance (#639). - Fix
Attributehashing by reference identity whileEqualscomparedCurrentValue, which put two equal attributes in different dictionary buckets. It hashesCurrentValuenow (#639). - Fix
AttributeEffect.EqualsignoringperiodicEffects,behaviorsand all four stacking fields, and its hash ignoring the name it compares. Equality reads every authored field now, and the hash reads the managed ones (#639). - Fix
CosmeticEffectDatahashing its component count whileEqualscompared a deduplicated type set, so one copy of a component and two compared equal and hashed apart (#639). - Fix an effect's stack key becoming unreachable once the effect asset was destroyed. The key compared managed identity and hashed through Unity's own
GetHashCode, which collapses on destruction (#639). - Fix
default(RandomState)hashing zero while comparing equal tonew RandomState(0). The hash comes from the membersEqualscompares, never from the value a payload carried, and skips a gaussian the state says it does not hold (#639). - Fix
AnimationEventEqualityComparer.GetHashCode(null)throwing while itsEqualsreported two nulls equal, which madeHashSet<AnimationEvent>.Add(null)fail. Null hashes to zero (#639). - Fix
SerializableType.Equals(null)andSerializableNullable<T>.Equals(null)answering true. A boxed value type is a real object, soObject.Equalsrequires false; test emptiness withIsEmptyorHasValue(#639). - Fix effect teardown that ran your callbacks before the handle was detached: one that removed, re-applied or queried its effect saw it still active, recursed, corrupted a pooled buffer, or threw and stranded the tags after it.
ApplyEffectrefuses aStackpastmaximumStacks. See Removal Is Two-Phase (#640). - Fix
IRandom.Next()andNextLong()returningint.MaxValueandlong.MaxValue, one value outside their documented[0, max)range. Values from those two methods change for a given seed; rawNextUint/NextUlongstreams and saved generator state do not (#638). - Fix guards a destroyed object walked straight through:
?.and??compare against CLR null, soTagHandler,EffectHandler,SingletonAutoLoader, two property drawers and the settings asset acted on effects, windows and assets that were already gone (#621). - Fix
RuntimeSingleton<T>.Instancehanding back a live-but-inertGameObjectwhen the singleton component could not be attached. The half-built object is destroyed andInstancereturns null instead. (#629). - Fix relational component assignment falling back to reflection after its metadata cache was destroyed: a destroyed
_metadataCachefield skipped the liveAttributeMetadataCache.Instanceinstead of using it. (#629). - Fix the Animation Creator treating a frame index too large for an
intas index0, where it collided with a real frame0. Such a name is no longer read as an indexed frame. (#629). - Fix reads of
TryXxxoutvalues whose call was never tested, including aRollingHighWaterMarkcleanup that could subtract a sample nobody wrote and then loop forever (#629). - Fix two documentation examples that could not work as printed: the enum display-name sample imported
Core.Attributerather thanCore.Attributes, and thelink.xmlsample preserved an assembly name that does not exist, which strips silently (#441). - Fix fifteen broken documentation links on
AssetDatabaseBatchScope: its<see cref>references toAssetDatabase.Refresh,CreateAssetandImportAssetnamed an ambiguous overload or an unresolvable type, so an IDE linked the wrong overload or nothing (#594). - Fix the IntelliSense tooltip on ten public
ReflectionHelpersdelegate factories, which carried two<summary>tags and showed the vaguer one (#441). - Fix zero-valued
ValueTuplecomponents and fixed-width map keys being omitted by WallstopProto where protobuf-net writes them explicitly, including enum tuple map keys (#399). - Fix
string.FromBase64()inventing replacement-character text when the decoded bytes are not valid UTF-8; corrupt payloads now return an empty string instead (#580). - Fix entering Play Mode destroying scene-authored
RuntimeSingletoncomponents before their scene starts (#582). - Fix double allocation when JSON-deserializing arrays: array growth rents from the shared pool and collection property names match without a throwaway string (#504).
- An
AttributeEffectauthoring mistake --Instantwith periodic or behaviour data, or an unassigned cosmetic entry -- is now reported once per effect and in the Inspector, not on every application. Each report rendered the whole effect to JSON, measured at 20.5 us (#567). - Fix
Attribute.CurrentValuereporting the wrong number in the editor: outside play mode it discarded the cached value, so an attribute deserialized while buffed lost the buff, and in play mode an Inspector edit to the base value left the cache stale (#569). - Fix a single
[SiblingComponent]or[ChildComponent]field binding a disabled component whenIncludeInactive = false. With two candidates on one object and the first disabled, the disabled one was assigned instead of the enabled one behind it (#529). - Fix a second
awaitof the sameAsyncOperationstopping the first one resuming. Two coroutines or tasks awaiting oneSceneManager.LoadSceneAsynchandle now both continue; previously only the last to register did. - Fix one refused protobuf surrogate registration disabling every registration after it, so
Vector3,ColorandBoundssilently encoded with different bytes. Each is now independent and names the type it could not register. - Fix
UnityRandomsaves and copies losing engine position or pending Gaussian draws. Snapshots preserve sampling caches; restoring also restores Unity's shared random state. See Random Generators (#521, #728). - Fix protobuf-net writing a different payload than WallstopProto for a zero-initialized
FastVector2IntorFastVector3Int. The surrogate mirrored the cached hash throughGetHashCode()rather than the stored field, so the two encoders disagreed on the origin (#309). - Fix
default(FastVector2Int)anddefault(FastVector3Int)comparing unequal to the origin they describe. An array element, an unset field or a dictionary miss did not matchnew FastVector2Int(0, 0), so a set held the origin cell twice. Wire format is unchanged (#309). - Fix
default(CacheStatistics)anddefault(PoolStatistics)comparing unequal to an all-zero snapshot, for the same reason (#309). - Fix
PoolStatisticshashing the three rates it compares with a tolerance, so two snapshots that were equal could hash differently and a set held both (#309). - Fix generator metadata that named the wrong algorithm:
LinearCongruentialGeneratoris the Numerical Recipesranqd1LCG, not Park-Miller;RomuDuomatches neither published ROMU duo variant;IllusionFlowis not a PCG or xorshift hybrid (#509). - Fix the license recorded for the ROMU algorithm behind
RomuDuo: it is Apache 2.0, not CC0 (#509). - Fix
SortByName()andScriptableObjectSingleton<T>.Instancethrowing on a name whose trailing digits do not fit anint, such as a timestamp, or are not ASCII digits. Such names now order correctly, and a suffix of any length is compared without being parsed (#386). - Fix
string.Reverse()destroying emoji and other non-BMP characters: it split every surrogate pair, so the result encoded as replacement characters. Reversing twice now returns the original (#386). - Fix
string.Truncate()returning a result longer than the limit it was given when the ellipsis did not fit, and cutting characters in half. The result now always fits and is always valid text (#386). - Fix
SerializedStringComparerthrowing fromEqualsandGetHashCodewhen its serialized mode held an unrecognized value. It falls back to ordinal comparison, and hashes null instead of throwing (#386). - Fix fourteen comparables ordering null last instead of first, disagreeing with every
IComparablein the framework and with each other.StringWrapperalso ordered by hash code rather than by its string (#386). - Fix
XoroShiroRandombeing documented as xoshiro128**. It is xoroshiro128+, whose lowest bits are the weak ones and the half this returns, so its rating is now Good (#285). - Fix
NextGuid()andNextWGuid()throwing on any generator restored from a protobuf payload read by protobuf-net. Twelve generators were affected, so the first GUID drawn after loading a save crashed (#492). - Fix
IllusionFlow,PcgRandom,RomuDuo,XorShiftRandomandXoroShiroRandomreplaying a different sequence than the one saved when their whole serialized state was at its default (#492). - Fix dead or crashing streams when restored shared reservoir, PCG, StormDrop, PhotonSpin or SystemRandom state is invalid. Repair and PhotonSpin warmup now run after construction or deserialization, not on every draw (#492, #503).
- Fix
Helpers.EnumeratePrefabsandEnumerateScriptableObjectssearching folders the caller never named when the asset paths were passed as anything other than astring[](#482). - Fix
[WShowIf]never matching a member of aulong-backed enum abovelong.MaxValue, which hid the field it was meant to reveal (#346). - Fix a chosen text colour on a
[WButton]or[WEnumToggleButtons]palette entry being overwritten whenever its red, green and blue were all zero, so opaque black was replaced by the auto-computed colour and any transparency was discarded. Existing entries keep the colour they were showing (#476). - Fix settings color keys being stored case-sensitively while every reader matched them without regard to case, so
"Save"and"save"were two palette entries on disk and one entry to every reader (#476). - Fix
PositiveModreturning a negative result -- the one thing it promises never to do -- for any maximum above 2^30 (int) or 2^62 (long), and roundingfloatanddoublevalues that were already inside the range.WrappedAddnow wraps the real sum when adding overflows. - Fix
floatanddoublePositiveModreturning0for every input when the maximum is 1, which made the normalized-phase case it documents useless:5.5f.PositiveMod(1f)is0.5f. It could also return the maximum itself when the remainder was too small to survive being added to it. - Fix
SerializableSortedDictionaryskipping the rebuild of its serialized arrays when two stored keys compare equal under its own comparer, which could leave a newly added key unsaved (#476). - Fix
SerializableDictionary,SerializableSortedDictionaryandSerializableHashSetwriting back entries their own comparer holds as one, so a case-insensitive dictionary could save two entries that merged into one on the next load (#476). - Fix
SortAlgorithm.PowerandSortAlgorithm.PowerPlusreordering elements that compare equal, which both are documented as never doing. Any list whose equal elements sit inside a descending stretch was affected (#461). - Fix
SerializableDictionaryandSerializableSortedDictionarydiscarding a comparer they were constructed with, so seeding one withStringComparer.OrdinalIgnoreCase— the documented way to make it case-insensitive — did nothing (#472). - Fix the settings window treating any color within about two and a half 8-bit steps of the factory default as untouched, so a color you had deliberately changed could be overwritten by the suggested palette (#472).
- Fix the inspector repainting on every settings check once a custom color held a
NaNchannel, and missing the removal of a color key present in any snapshot but the first (#472). - Fix a log format naming a color Unity cannot parse —
$"{value:#notacolor}"— emitting a rich text tag the console shows as literal markup instead of the value. The value is now logged undecorated (#473). - Fix
LayeredImagediscarding one alpha level more than itspixelCutoffasks for at some cutoffs and not others, so it kept different pixels than the sprite tools at the same cutoff (#473). - Fix the multi-file selector's list background being white at 15% opacity rather than the opaque dark panel it was written as, and the sprite animation creator's selected-thumbnail border coming back out of gamut and more opaque than its fill.
Color * floatscales alpha too (#473). - Fix
[WButton]and the serialized collection drawers labelling buttons with the less readable of black and white on 22.9% of colors. The shipped green "Add" button was at 2.84:1, below the 3:1 large-text floor; it is now 7.40:1 (#471). - Fix a dark
[WButton]showing no hover or press feedback: darkening a color already near black clamped all three states to the same color (#471). - Fix
Serializer.ProtoDeserialize()refusing to read back whatProtoSerialize()wrote for a value whose fields are all at their defaults —Vector3.zero,Color.clear,Quaternion(0,0,0,0)and any such contract encode to zero bytes, which was rejected as empty input. Anullpayload is still refused (#474). - Fix
TextureScale.Bilinear()andPoint()shifting a scaled texture half a texel toward the origin: an upscale never reached the source's brightest pixel and a symmetric image downscaled asymmetrically (#470). - Fix
TextureScale.Bilinear()and the Image Blur tool pulling a fully transparent pixel's color into its visible neighbors, so red beside transparent green produced a yellow edge. Opaque textures are unchanged (#470). - Fix the sprite sheet extractor's preview thumbnails sampling half a texel toward the origin (#470).
- Fix the
WButtonandWEnumToggleButtonsstyle and texture caches growing without bound as inspector colours change: two colours the caches called equal were stored under different keys, so every repaint could add another entry (#466). - Fix a
WButtonhover or pressed colour derived from an out-of-gamut palette colour coming back with a negative channel (#466). - Fix
IRandom.NextColorInRange()returning the varied colour fully opaque, discarding the alpha of the base colour (#466). - Fix
IRandom.NextColorInRange()throwingArgumentExceptionfor a variance of zero or a negative variance, and returning black for one that is not a finite number (#466). - Fix
Color.ChangeColorBrightness()returning a colour whose channels are allNaNwhen the correction factor isNaN(#466). - Fix a save holding an unset
WGuidfailing to load: JSON wrote the empty GUID and then refused to read it back (#437). - Fix a JSON payload with
mingreater thanmax, or withmaxmissing, crashing aRange<T>load withArgumentExceptioninstead of reporting corrupt data (#437). - Fix a
Gradientwith more than eight colour or alpha keys silently losing the extras and filling the player log with errors. The payload is now refused (#437). - Fix
Serializer.JsonStringifyandJsonSerializefailing on aType, at the root of a graph or behind anobjectmember, despite the shippedTypeconverter (#437). - Fix an unattributed field joining the wrong group when a type declares more than one
[WGroup], and a bare[WGroupEnd]closing a group other than the one it follows. Auto-include now targets the most recently declared group, and a bare end closes every open group (#455). - Fix a
SerializableDictionary's "Add entry" Value field being drawn 8.5px left of its Key field wherever the inspector indents it (#284). - Fix a watcher on a non-
GameObjecttype loading every imported prefab, which is whereSendMessage cannot be called during Awake, CheckConsistency, or OnValidatecame from. A sub-asset nested inside a.prefabno longer matches a watcher on its type (#280). - Fix the asset-change watcher deserializing assets during import to decide whether a path holds a watched type, which produced the same warning. The decision is now made from asset metadata (#439).
- Fix
[DetectAssetChanged]crashing headless editors; it no longer initializes in batch mode (#327). - Fix
SerializableDictionary<TKey, List<TValue>>and its sorted counterpart saving their keys and none of their values. Dictionaries with other value types keep exactly the same saved data (#314, #348). - Report
SerializableHashSet<List<TValue>>in the Inspector instead of drawing a column that persists nothing: a list compares by reference, so such a set already treats equal contents as distinct elements (#314, #354). - Fix the Inspector drawing an empty value column instead of an error for dictionary values no wrapper repairs, such as
List<List<T>>and jagged arrays. Sorted dictionaries are covered too (#357). - Fix
StartFunctionAsCoroutine()andExecuteOverTime()stopping forever the first time their action threw. Both now report the failure against the owning object and keep running (#359). - Fix disabled logging still evaluating its receiver and building its message, so
MySingleton.Instance.Log($"…")created the singleton and allocated in a release build. DefineENABLE_UBERLOGGINGto keep logging — see Logging Extensions (#350). - Fix
GetRandomPointInCircle()andGetRandomPointInSphere()returning points outside the shape far from the origin: at world coordinate 1,000,000 with radius 0.05, half of them were outside. - Fix
NativePcgRandomproducing worse randomness than every other generator:NextFloat()could return exactly 1,NextLong()could return a negative,NextUint(0)threw, and most seeds built a shortened-period stream. Sequences for a given seed have changed (#282). - Fix
ToCachedName()andToDisplayName()falling back to a dictionary for any signed enum with a negative member, andsbyteenums allocating a 256-entry cache (#339). - Fix
EditorCacheHelper.GetEnumDisplayName()rebuilding the enum's value array on every call. Display names are unchanged. - Fix
[WEnumToggleButtons]throwingOverflowExceptionon an enum with a negative member and taking the Inspector down. The Odin drawer rendered such members as "None" and wrote that back (#339). - Fix an interrupted
Serializer.WriteToJsonFile()destroying the previous save. Every JSON file write now stages and swaps, and creates missing directories (#319). - Fix a copied
PooledResource<T>orPooledArray<T>returning the same instance to the pool twice, so two live rentals shared one buffer. Every disposable scope in the package is now disposed at most once, at no allocation cost (#358). - Fix a double-disposed
AssetDatabaseBatchScopeending an outer scope's batch early, leaving the rest of its asset writes unbatched (#358). - Fix
WallstopArrayPool<T>andWallstopFastArrayPool<T>allocating 32 bytes on every rent (#367). - Fix
Color.ToHex()truncating each channel instead of rounding it, so it disagreed with Unity's ownColorUtility.ToHtmlStringRGBA()on half of all colors and could not returnFFfor any channel below exactly 1 (#466). - Fix the sprite cropper and pivot adjuster selecting different pixels than the sheet extractor at the same alpha cutoff, and two transparency scorers disagreeing with their twenty siblings by one alpha level (#466).
- Fix the Inspector's solid-texture cache holding two entries for one color: colors a hair either side of a channel boundary compared equal and hashed apart (#466).
- Fix
GetAverageColor(ColorAveragingMethod.Dominant)returning a channel above 1 for a saturated color - a dominant white came back as 1.0039 (#466). - Fix
GetAverageColor(ColorAveragingMethod.Weighted)returning a fully transparent color for opaque black pixels, which weigh nothing under luminance weighting (#466).
- Unity license lock handoff after CI cleanup: final Unity license returns now emit fail-closed cleanup proof to the organization build lock. Cleanup is bounded and non-masking, failed or cancelled acquisitions still release queued/owned lock state, and only a successful return command or exact allowlisted Unity responses permit the activation slot to enter cooldown instead of quarantine.
-
Asset change detection loop reset utility: Added
AssetChangeDetectionUtility.ResetLoopProtection()so editor tooling can resume[DetectAssetChanged]dispatch after a recursive callback has tripped loop protection, without requiring a domain reload. -
Feel-good randomness helpers: Added
ExactAveragePrd,BadLuckProtection, andWeightedShuffleBag<T>for player-facing randomness that reduces streak frustration while keeping deterministic, testable behavior.ExactAveragePrdsolves the pseudo-random distribution coefficient for a configured long-run average,BadLuckProtectionimplements pity-timer chance ramps and optional hard guarantees, andWeightedShuffleBag<T>emits exact weighted ticket counts before repeating. The helpers expose explicit restore APIs so save/load systems can persist pity/deck state.
-
Optional Odin Inspector integration without unguarded Odin assemblies:
RuntimeSingleton<T>,ScriptableObjectSingleton<T>, andAttributeEffectnow use Odin serialized base classes only when theodininspectorpackage is installed and the package-ownedWALLSTOP_UNITY_HELPERS_ODIN_INSPECTORdefine is active; otherwise they fall back to Unity base classes. Projects that still define the globalODIN_INSPECTORsymbol without shipping Sirenix assemblies no longer fail package compilation, while Odin projects keep the serialized base behavior automatically.
See the roadmap for details
-
AssetDatabaseBatchHelper.EnsureAssetFolder/EnsureAssetParentFolder: batch-safe helpers that register every missing folder segment with theAssetDatabase(never via raw disk, which can leave the database out of sync and spawn numbered duplicate folders) and pause any openStartAssetEditingbatch first, so a subsequentAssetDatabase.CreateAssetcannot fail with "Parent directory must exist".
-
[WValueDropDown]provider-misconfiguration is now logged as a Warning, not an Error: a null/empty/missing/throwing or wrong-return-type dropdown value provider is a handled condition — the dropdown falls back to an empty option list — soWValueDropDownAttribute/DropDownValueProvidernow log these diagnostics atWarningseverity instead ofError. The message text is unchanged. This better reflects that the feature degrades gracefully, and (as a side effect) stops these editor-tooling diagnostics from failing PlayMode tests that incidentally exercise the dropdown path. -
DirectoryHelper.EnsureDirectoryExistsno longer logs an error before throwing: passing a path outsideAssets/in the editor previously emitted aDebug.LogErrorand threw anArgumentException(a redundant double signal that polluted the console and could fail unrelated tests that did not expect the log). It now throws theArgumentExceptiononly, and the exception message names the offending path (for example,Cannot create directory 'SomeFolder/SubFolder' outside the Assets folder: AssetDatabase only manages paths under 'Assets/'.). Callers that already catch the exception are unaffected; callers that relied on the console log should read the thrown exception instead.
-
Unity 6000.3 compatibility for runtime helpers:
Object.GetUnityObjectId,UnityObjectExtensions.FindObjectsOfTypeShim,SceneHelper.EnsureOneComponent, and theRigidbody2Dvelocity helpers now gate Unity 6 APIs by the specific version that introduced them instead of treating every Unity 6000.x editor as equivalent. Unity 6000.3 and earlier supported matrix versions compile through the older overloads while Unity 6000.4+ continues to use the newer no-sort object lookup andEntityId.ToULongpaths. -
EffectHandlerperiodic catch-up after a long frame: missed periodic ticks now advance on their scheduled interval instead of sliding from the current frame, and unlimited periodic effects are capped per update so a large frame hitch cannot monopolize the player loop. Remaining due ticks continue catching up on subsequent updates. -
Protobuf abstract-root deserialization reporting corrupt data instead of a registration error: abstract
[ProtoContract]roots without[ProtoInclude]metadata now require an explicitRegisterProtobufRootmapping before deserialize, so ambiguous abstract roots fail with the documented type-resolution exception instead of falling through to protobuf-net's version-specific corrupt-data / constructor errors. Explicit registrations also take precedence over abstract root heuristics. -
Background logging using a stale Unity main-thread capture:
WallstopStudiosLoggernow sharesUnityMainThreadGuard's captured main-thread state, preventing background log calls from being misclassified after test/player lifecycle resets. -
Protobuf serialization of an EMPTY
SerializableHashSet/SerializableSortedSet/SerializableDictionary/SerializableSortedDictionarythrowingSerializationInputException("data is empty") on deserialize: an empty collection serializes to a zero-byte payload (the wrapper carries only repeated fields), butProtoDeserializeran its generic empty-payload guard before the collection-wrapper interception, so a valid empty collection could not round-trip. The collection interception now runs first, so an empty collection round-trips to an empty collection; the empty-payload guard still rejects empty input for ordinary (non-collection) message types. Wire format for non-empty payloads is unchanged. -
Serialization crashing or silently corrupting data on IL2CPP/AOT standalone players:
Serializernow serializes the affected types through AOT-safe paths instead of protobuf-net's reflection /Reflection.Emitmodel, which under IL2CPP hit the unsupportedGetTypeModifiersicall or bound immutable structs to empty values.Deque<T>,CyclicBuffer<T>, andSparseSetroute through plain array/scalar protobuf wrappers (also fixing a spurious[ProtoAfterDeserialization]ArgumentOutOfRangeException);FastVector2Int,FastVector3Int,ImmutableBitSet, andParabolaroute through mutable protobuf surrogates.Serializer.JsonStringify/JsonSerializeof anonymous types (and other types lacking a public parameterless constructor) now use a reflection-light writer instead of System.Text.Json's parameterized-constructor converter, which has no AOT code under IL2CPP. A packagelink.xmlpreserves the serialization assemblies. JSON output and protobuf round-trip values are unchanged on mono / editor. The package pre-registers the common element specializations (int,long,float,double,bool,string,Vector2,Vector3,Vector2Int,Vector3Int,FastVector2Int,FastVector3Int); consumers that store a custom value-type element in aDeque<T>orCyclicBuffer<T>must register thatTfor AOT themselves (reference-type elements share a single generic specialization and need no registration). -
Relational component attributes (
[SiblingComponent]/[ChildComponent]/[ParentComponent]) crashing or resolving the wrong components on IL2CPP/AOT players: the collection fast-path built a runtime generic accessor viaExpression.Compile(), which IL2CPP cannot service (it threw at runtime). It now uses the AOT-safe non-genericGetComponents/GetComponentsInChildren/GetComponentsInParent(Type, …)overloads. Include-inactive filtering also wrongly treated every component as enabled on IL2CPP (a reflection delegate path that silently failed), so disabled components were not filtered out; the enabled state is now read AOT-safely. Behavior on mono / editor is unchanged. -
[WNotNull]validation (this.CheckForNulls()) silently doing nothing in player builds: the null check was compiled only into the editor, so it never threwArgumentNullExceptionfor a null[WNotNull]field in a built player. It now runs in every build (AOT-safe) exactly as the API documents, so missing required references fail fast at runtime instead of being silently ignored. -
[ValidateAssignment]validation (this.ValidateAssignments()) silently doing nothing in player builds: likeCheckForNulls, the warning pass was compiled only into the editor (while the siblingAreAnyAssignmentsInvalidalready ran everywhere), so unassigned fields were never reported in a built player. It now runs in every build (AOT-safe), consistent withAreAnyAssignmentsInvalid. -
ScriptableObjectSingletonassets not regenerating on Unity 6 after the asset file was deleted: when a singleton's.assetwas removed but its.metaremained, Unity 6000.3+ retains the path-to-GUID mapping, whichScriptableObjectSingletonCreatortreated as "path already occupied" and skipped recreation. Creation decisions now key on the asset body file, so the singleton is recreated on every Unity version. -
AssetDatabase.CreateAssetfailing with "Parent directory must exist" / "Creating asset at path … failed": editor utilities that create assets —ScriptableObjectSingletonCreator,ScriptableObjectSingletonMetadataUtility,AttributeMetadataCacheGenerator,PersistentDirectorySettings, and the sprite/atlas/animation creator windows — now ensure the parent folder is registered with theAssetDatabasebefore creating the asset, including while anAssetDatabasebatch is open (common under-batchmode). Previously, creating an asset in a not-yet-registered folder could fail or be skipped. -
Unity 6 deprecation warnings from
Object.FindObjectsOfType/FindObjectOfType: runtime DI-integration code (Reflex / VContainer / Zenject) now routes object lookups through a version-gated shim that usesFindObjectsByType/FindFirstObjectByTypeon Unity 2022.2+ (including Unity 6) and the legacy API below it, eliminating the deprecation warnings while preserving behavior. -
MonoBehaviour.ExecuteFunctionAfterFramecallback never firing in headless/batch mode: the helper yieldedWaitForEndOfFrame, which never resumes under-batchmode -nographics(no end-of-frame render signal), so the queued callback was silently dropped on headless players, dedicated servers, and CI. It now advances a single frame in batch mode (the headless-safe equivalent) and continues to useWaitForEndOfFramein interactive/graphical sessions, so the callback runs in every environment.
-
Failed Tests Exporter: New editor utility that hooks into the Unity Test Runner to automatically capture test failures and export them to timestamped text files
- Automatically records test name, failure message, and stack trace for each failed test
- Configurable output directory with a visual folder picker — defaults to the project root if not set
- Path validation on every use: falls back to the project root if the configured directory is missing, invalid, or outside the project
- Menu items under
Tools > Wallstop Studios > Unity Helpersto export or clear captured failures - Disabled by default — enable via
Project Settings > Wallstop Studios > Unity Helpers
-
Editor asset import-loop pressure from unchanged metadata saves:
ScriptableObjectSingletonMetadataandAttributeMetadataCachenow skipSetDirty/SaveAssetswork when regenerated metadata is unchanged. This prevents initialize-on-load and explicit singleton ensure passes from repeatedly re-saving identical assets during Unity test runs or editor refreshes, reducing the chance of Unity's "infinite import loop" detector tripping on native runs. -
AssetPostprocessorDeferraldedup regressing structurally-equal-but-distinct drains:AssetPostprocessorDeferral.Schedulenow dedups pending drains viaReferenceEqualsinstead ofList<Action>.Contains(which invokesDelegate.Equals). The prior structural-equality behavior silently coalesced two distinct delegates that shared the sameMethod+Target-- common when a local helper returns a lambda that captures only outer-method variables -- so a self-rescheduling drain could be dropped entirely. Matches the documented intent in the AssetPostprocessor safety skill and is pinned by the newScheduleStructurallyEqualButDistinctDelegatesAreNotDeduplicatedregression test - WButton conflict warnings hidden in collapsed foldouts: Fixed group conflict warnings (placement, priority, and draw order) not rendering when foldout groups are collapsed. Conflict warnings now render in the group header area regardless of foldout expansion state, and warning cache population is validated across collapsed, expanded, and always-open foldout behavior.
-
Spurious "SendMessage cannot be called..." warnings from asset processors: Eliminated spurious "SendMessage cannot be called during Awake, CheckConsistency, or OnValidate" warnings from
DetectAssetChangeProcessorand related editor processors (LlmArtifactCleaner,SpriteLabelProcessor) by deferring callback invocation out of Unity's asset-import phase via a sharedAssetPostprocessorDeferralhelper backed byEditorApplication.delayCall. The prior synchronous path calledAssetDatabase.LoadAllAssetsAtPath/GetComponentsInChildren/ user callbacks during the import phase, which triggered Unity's internal sprite/renderer lifecycle relays and produced per-import warning storms. A new opt-out setting (Project Settings > Wallstop Studios > Unity Helpers > Detect Asset Changes, option Defer Post-process Callbacks, default on) restores the old synchronous behavior for users who require it. (#234) -
Pool auto-purge throttle dropping real purges: Fixed
WallstopGenericPool<T>.PurgeInternalCoreletting the 1-secondMinAutoPurgeIntervalSecondsthrottle blockMaxPoolSizeenforcement. A Rent/Return that advanced_lastAutoPurgeTimeon a healthy-pool scan would block a subsequent same-tick call that pushed the pool pastMaxPoolSize, silently skipping theCapacityExceededpurge and dropping the matchingOnPurgenotification. The throttle now has two orthogonal rules: (1) when the pool is observably overMaxPoolSize, the throttle is bypassed entirely so a burst of returns cannot accumulate beyond capacity within a single clock tick; (2) otherwise the throttle still rate-limits scans (preserving the O(1) amortized fast path for healthy pools under contention). The multithreaded variant also guards the timestamp advance with CAS max-semantics so concurrent out-of-order writes cannot regress the throttle clock. Applies to both the multithreaded and SINGLE_THREADED pool variants -
Pool performance regression from O(n) usage tracking: Fixed
RollingHighWaterMark(used by pool purge system) performing O(n) operations on every pool rent/return, causing 100x slowdowns under sustained load. ReplacedList<Sample>withCyclicBuffer<Sample>for O(1) add/remove, added incremental running sum for O(1) average computation, and added a monotonic deque for O(1) amortized peak tracking. Previously, 100K rent/return cycles took 20+ seconds; now completes within 200ms budget -
TexturePlatformOverrideEntryDrawer render-phase mutations: Fixed
OnGUIwriting toSerializedPropertyvalues every frame withoutBeginChangeCheck/EndChangeCheckguards. Direct assignments likeapply.boolValue = EditorGUI.ToggleLeft(...)andnameProp.stringValue = EditorGUI.TextField(...)dirtied theSerializedObjecton every repaint, corrupting undo history. Also removed a redundant render-phase writeback of the computed display label to the platform name property -
TexturePlatformOverrideEntryDrawer GenericMenu undo and Custom handling: Fixed
GenericMenucallback not callingUndo.RecordObjectsbefore mutation and not handling the "Custom" menu option. Selecting "Custom" from the dropdown now correctly sets the platform name tostring.Empty(triggering custom mode), and all selections are undoable -
SourceFolderEntryDrawer render-phase mutation: Fixed
EnumFlagsFieldfor selection mode writing tomodeProp.intValueevery frame without aBeginChangeCheck/EndChangeCheckguard -
AttributeMetadataCache generator path mismatch: Fixed
AttributeMetadataCacheGenerator.GetOrCreateCache()using hardcoded asset paths that did not match the[ScriptableSingletonPath("Wallstop Studios/Unity Helpers")]attribute onAttributeMetadataCache. The generator was creating and loading the cache asset fromAssets/Resources/Wallstop Studios/instead of the correctAssets/Resources/Wallstop Studios/Unity Helpers/path, causing cache generation to silently fail when the singleton was already loaded at the correct path -
IntDropDown invalid value handling: Fixed
IntDropDownDrawerto properly handle property values that fall outside the configured options. Invalid values (values not in the options array) are now preserved without modification during render and clearly displayed with an "(Invalid)" suffix. Previously, invalid values were displayed as-is without any visual indication. The UI ToolkitIntDropDownSelector.GetDefaultValue()now returns the first option instead of 0 -
Linux dropdown rendering phantom rows: Replaced all
EditorGUI.Popupusage withGenericMenu-based dropdowns to eliminate phantom empty rows when selected index is -1 on Linux. Affected drawers:WValueDropDownDrawer,IntDropDownDrawer,StringInListDrawer,TexturePlatformOverrideEntryDrawer(standard variants), andWValueDropDownOdinDrawer,IntDropDownOdinDrawer,StringInListOdinDrawer(Odin Inspector variants). Odin drawers now always useGenericMenuregardless of the page limit setting, sinceGenericMenuhandles all list sizes correctly without the rendering issues that required the threshold (#209) -
Multi-object editing for WValueDropDown: Added typed
SerializedPropertysetters for Unity-native types (Vector2,Vector3,Vector4,Color,Rect,Bounds,Quaternion,AnimationCurve,Hash128, and theirIntvariants) inWValueDropDownDrawer.ApplyOptionto avoid the reflection fallback for known property types. The generic reflection path now iterates over allserializedObject.targetObjectsfor proper multi-object editing support instead of only updating the first selected object -
SerializableSet undo not working for add, clear, sort, and commit operations: Fixed
TryClearSet,TryAddNewElement,TryCommitPendingEntry,AppendNullPlaceholderEntry, andTrySortElementsinSerializableSetPropertyDrawernot callingUndo.FlushUndoRecordObjects()after direct object mutation. These methods usedUndo.RecordObjectsto snapshot pre-change state but never finalized the undo record, causingUndo.PerformUndo()to silently do nothing -
GUIContent GC pressure in drawer OnGUI: Fixed per-frame
GUIContentallocations inIntDropDownDrawer.DrawGenericMenuDropDownandPoolTypeConfigurationDrawer.OnGUIthat created avoidable garbage collection pressure in the Inspector. Both drawers now reuse staticGUIContentinstances (consistent withWValueDropDownDrawerandStringInListDrawerwhich already followed this pattern)
-
WValueDropDown empty rows in dropdown: Fixed
WValueDropDownandStringInListdropdowns showing empty/blank rows at the top of the dropdown list, particularly on Linux. Clamped invalid-1selected indices to0inWValueDropDownDrawerandStringInListDrawer, and hardened dropdown display logic to replace empty labels with descriptive fallback text (#209) -
Dropdown display label normalization: Fixed search, filter, suggestion, and selected-value display in
WValueDropDown,StringInList, and popup dropdown windows not applying the(Option N)fallback label consistently, causing items with empty display labels to be unsearchable and the wrong option to appear selected (#213)
- "Destroying assets is not permitted to avoid data loss" on Asset Domain Reload issue
-
Unity 6.3 unsigned package warning: Added
"signature": "unsigned"field to package.json to explicitly mark the package as unsigned for Unity 6.3+. This prevents Unity from showing a warning that the package is missing a signature. The change is backwards compatible with older Unity versions and works with OpenUPM, npm, and git URL installations. -
WGroup not working in Unity 6000.x: Fixed WGroup attributes not rendering in Unity 6 by using named parameter syntax for
CustomEditorattribute'seditorForChildClassesparameter. This change is backward compatible with Unity 2022 and earlier versions.
-
DetectAssetChanged scene file crash: Fixed Unity crash ("Do not use ReadObjectThreaded on scene objects!") when
.unityor.scenetemplatefiles were processed by the asset change detection system
- Banner SVG Issues: Various issues relating to Unity Helpers banner SVG rendering
- Documentation nested list rendering: Fixed GitHub Pages rendering where nested bullet lists appeared flat without proper indentation (#175)
- Documentation too self-congratulatory: Toned down the documentation to be more realistic and less LLM-speak
-
Breaking: Relational component attributes (
[SiblingComponent],[ParentComponent],[ChildComponent]) now assignnullto single-component fields when no matching component is found andSkipIfAssigned=false(the default). Previously, fields retained their existing values when no component was found. This change makes single-field behavior consistent with collection-field behavior, which already assigned empty collections.
-
ScriptableObjectSingletonMetadata Sync Button: Added a
Syncbutton toScriptableObjectSingletonMetadatainspector that re-scans all assemblies forScriptableObjectSingleton<T>types and updates their metadata entries. This allows manually refreshing singleton metadata when assets are added, moved, or renamed.
-
ScriptableObjectSingletonCreator race condition: Fixed issue where newly created singleton assets were immediately deleted because
LoadAssetAtPathreturned null before Unity's AssetDatabase had indexed the file. The fix adds a synchronous import afterCreateAssetand avoids deleting on-disk files when the file exists but isn't visible to the AssetDatabase yet.
-
AnimationCreator Configuration Persistence: Save and load AnimationCreator settings to JSON files alongside sprite source folders.
- Configurations are automatically saved as
.animation-creator.jsonin sprite source directories - Auto-loads existing configurations when source folders are selected
- Save individual or all configurations with dedicated UI buttons
- Reset to defaults with optional config file deletion
- Preserves all settings including animation data, framerate curves, regex patterns, and grouping options
- Configurations are automatically saved as
- AnimationCreator Pagination: Animation data list now uses pagination (20 items per page) for better performance with large animation sets
- AnimationCreator editor performance: Significantly improved scrolling and editing responsiveness when working with animation data.
- ScriptableObjectSingletonCreator retry exhaustion: Fixed issue where new singleton assets would fail to create with "Maximum automatic retry attempts reached" even when specifying paths.
- Animation Copier diff detection: Fixed issue where copied animations were incorrectly detected as "changed" instead of "unchanged" after copy operations.
- Updated npmignore to align more closely with gitignore. The
scripts/testsmeta file error when sourcing from npm should be gone.
- WInLineEditorOdinDrawer now compiles.
- Pool Access Frequency Tracking: Intelligent purge decisions based on pool usage patterns
- Memory Pressure Detection: Proactive memory monitoring for intelligent pool purging
- Cross-Pool Global Memory Budget: Prevents aggregate memory bloat across all pools
-
Size-Aware Purge Policies: Large objects (above LOH threshold) get stricter purge policies
-
WallstopGenericPool<T>automatically uses size-aware options during construction
-
-
SpriteSheetExtractor: New editor tool for extracting individual sprites from sprite sheet textures.
- This is an ALPHA feature, much functionality is currently broken.
-
Cache Data Structure: New high-performance, configurable
Cache<TKey, TValue>with fluent builder API- Multiple eviction policies: LRU, Segmented LRU (SLRU), LFU, FIFO, and Random
- Time-based expiration with
ExpireAfterWriteandExpireAfterAccess - Weight-based sizing for entries of varying cost
- Dynamic growth with configurable thrash detection
- Loading cache support with
GetOrAddand custom loader functions - Thread-safe by default (single-threaded mode via
SINGLE_THREADEDdefine) - Eviction, get, and set callbacks for monitoring cache behavior
- Statistics tracking with hit/miss counts
- CachePresets: Factory methods for creating pre-configured caches optimized for common gamedev scenarios
-
AnimationCreator Variable Framerate: AnimationCreatorWindow now supports variable framerate animations using AnimationCurve
- New
FramerateModeenum (ConstantorCurve) for choosing timing mode - Per-animation
framesPerSecondCurveallows custom timing across animation progress - Curve presets: Flat, Ease In, Ease Out, and Sync with constant FPS
- Frame timing preview shows per-frame durations before generation
- New
-
AnimationCreator Live Preview: Real-time animation preview panel
- Play/pause/stop transport controls for preview playback
- Frame scrubber for manual frame navigation
- Respects variable framerate curves during preview
- Shows current frame index and FPS in preview panel
-
AnimationData Cycle Offset: New
cycleOffsetproperty (0-1) sets animation loop start point -
Pool Auto-Purging:
WallstopGenericPool<T>now supports configurable auto-purging and eviction- New
PoolOptions<T>class for configuring pool behavior at construction -
MaxPoolSizelimits pool capacity with automatic eviction of excess items -
IdleTimeoutSecondspurges items that have been idle too long -
PurgeTriggerflags control when purging occurs:OnRent,OnReturn,Periodic, orExplicit -
OnPurgecallback withPurgeReason(IdleTimeout, CapacityExceeded, Explicit) for monitoring - Intelligent purging mode tracks usage patterns to avoid purge-allocate cycles
-
MinRetainCountensures a minimum number of items are always kept
- New
-
Application Lifecycle Hooks for Pool Purging: Automatic pool purging in response to system events
-
Application.lowMemorytriggers emergency purge (ignores hysteresis, purges toMinRetainCount) -
Application.focusChangedtriggers purge when app backgrounds (mobile platforms) - New
PurgeReasonvalues:MemoryPressure,AppBackgrounded,SceneUnloaded(reserved) - Configurable via
PoolPurgeSettings.PurgeOnLowMemoryandPoolPurgeSettings.PurgeOnAppBackground -
GlobalPoolRegistrytracks all pool instances for cross-pool operations -
PoolPurgeSettings.PurgeAllPools()method for manual global purge - Lifecycle hooks automatically registered via
RuntimeInitializeOnLoadMethod
-
-
RandomExtensions
NextOfExcept: New extension methods for selecting random elements with exclusions-
NextOfExcept(values)- no exclusions (convenience overload) -
NextOfExcept(values, exception1...)- exclude values - Zero-allocation using pooled collections internally
-
-
BREAKING: Pool purging now enabled by default with conservative settings
-
GlobalEnableddefaults totrue(wasfalse) -
DefaultBufferMultiplierdefaults to2.0(was1.5) -
DefaultHysteresisSecondsdefaults to120(was60) -
DefaultSpikeThresholdMultiplierdefaults to2.5(was2.0) - Use
PoolPurgeSettings.DisableGlobally()to restore previous behavior -
UnityMainThreadDispatcherauto-load behavior has changed from auto-loading to not auto-loading. -
UnityMainThreadDispatcherhide flags have been changed toNone.
-
-
DictionaryExtensions
ToDictionary: Now uses last-wins semantics for duplicate keys instead of throwingArgumentException- Aligns with common dictionary initialization patterns
- Applies to both
KeyValuePair<K,V>and tuple(K, V)overloads
-
IEnumerableExtensions return types:
OrderBy,Ordered, andShuffledmethods now returnList<T>instead ofIEnumerable<T>for improved usability (indexable, known count)- Note: These methods now use eager evaluation (execute immediately) instead of deferred evaluation
- Source code remains compatible—
List<T>is assignable toIEnumerable<T>
-
LRU cache eviction: Bounded editor caches now use LRU (Least Recently Used) eviction instead of FIFO
- Frequently-accessed cache entries are retained longer, improving cache hit rates
- Both reads and writes update an item's "recency", preventing hot items from being evicted
- Affects
EditorCacheHelper.AddToBoundedCacheand newTryGetFromBoundedLRUCachemethod - Applied to
InLineEditorShared,WShowIfPropertyDrawer, and other bounded editor caches
-
Shuffled performance:
IEnumerableExtensions.Shufflednow uses O(n) Fisher-Yates shuffle instead of O(n log n) sort-based approach -
LINQ elimination: Removed LINQ usage across runtime code for reduced allocations and improved performance
- Affects
Trie,Geometry,Serializer,ValidateAssignmentAttribute,WShowIfAttribute, relational component attributes, and more - Uses pooled collections and explicit loops instead of LINQ methods
- Zero-allocation patterns applied throughout
- Affects
-
GlobalPoolRegistry.EnforceBudget() zero-allocation: Replaced per-call
List<IPoolStatistics>allocation with static reusable list protected by existing lock
-
Cache pre-allocation OutOfMemoryException: Fixed production bug where
Cache<TKey, TValue>would pre-allocate internal storage toMaximumSizeinstead of using a small initial capacity- Creating a cache with
MaximumSize = int.MaxValuenow works correctly instead of throwingOutOfMemoryException - New
InitialCapacityoption allows explicit control over starting allocation size (default 16) - Cache grows dynamically from
InitialCapacitytowardMaximumSizeas items are added -
CacheBuilder<TKey, TValue>.InitialCapacity(int)method for fluent configuration -
Cache<TKey, TValue>.MaximumSizeproperty added to expose configured maximum (distinct fromCapacity) - Large
InitialCapacityvalues are clamped toMaxReasonableInitialCapacity(65536) to prevent excessive allocations
- Creating a cache with
-
Pool MinRetainCount not respected during gradual explicit purges: Fixed
MinRetainCountbeing ignored when usingMaxPurgesPerOperationwith explicit purges- Gradual purges now correctly stop when pool size reaches
MinRetainCount - Added
_pool.Count > effectiveMinRetaincheck to the purge loop condition in both thread-safe and non-thread-safe pool implementations
- Gradual purges now correctly stop when pool size reaches
-
Pool idle timeout purges blocked by comfortable size: Fixed idle timeout purges not occurring when pool size was at or below comfortable size
- Idle timeout purges now proceed regardless of comfortable size, as they represent essential pool hygiene
- Added
hasIdleTimeoutto loop entry condition to allow idle timeout evaluation independent of size
-
Pool hysteresis incorrectly blocking idle timeout purges: Fixed hysteresis protection blocking all purge types including idle timeout
- Idle timeout purges now proceed during hysteresis since they only remove items unused for extended periods
- Capacity and explicit purges remain blocked during hysteresis to prevent thrashing
-
ScriptableObjectSingletonCreator race condition creating numbered duplicate folders: Fixed race condition where parallel operations could cause Unity to create numbered duplicate folders like "Resources 1", "Resources 2", etc.
- Added detection for Unity's numbered duplicate folder creation pattern
- Automatically deletes duplicate folders and uses the intended folder path
- Logs warning if duplicate folder deletion fails, alerting user to manual cleanup needed
- GitHub Pages Support: All documentation is now available via a pretty GitHub Pages
- GitHub Wiki Support: All documentation is now available via a less pretty GitHub Wiki
-
Comprehensive Odin Inspector Attribute Support: All Unity Helpers inspector attributes now work seamlessly with Odin Inspector's
SerializedMonoBehaviourandSerializedScriptableObjecttypes-
[WButton]: Full support including grouping, placement, history, async methods, and parameters -
[WShowIf]: Conditional property display based on field values, methods, or comparisons -
[WReadOnly]: Disables editing while preserving display in Odin inspectors -
[WEnumToggleButtons]: Toggle button UI for enum selection with flags support -
[WValueDropDown]: Dropdown selection from custom value lists -
[WInLineEditor]: Inline editing of referenced ScriptableObjects and components -
[WNotNull]: Null reference validation with HelpBox warnings/errors -
[ValidateAssignment]: Field validation for null, empty strings, and empty collections -
[StringInList]: String selection from predefined lists or method providers -
[IntDropDown]: Integer selection from predefined value lists - No setup required — attributes work identically whether Odin Inspector is installed or not
- Custom Odin drawers registered when
ODIN_INSPECTORsymbol is defined
-
-
WButton Custom Editor Integration: New
WButtonEditorHelperclass for integrating WButton functionality into custom editors- Only needed when creating custom
OdinEditorsubclasses for specific types - Provides simple API for any custom editor to draw WButton methods
- Methods:
DrawButtonsAtTop(),DrawButtonsAtBottom(),ProcessInvocations(), and convenience methods - Documented integration patterns for both Odin Inspector and standard Unity custom editors
- Only needed when creating custom
-
Sprite Sheet Auto-Detection Preferring Non-Transparent Boundaries: Fixed an issue where the "Auto Best" algorithm and other detection methods could select grid boundaries that pass through non-transparent pixels when transparent alternatives existed
- Changed scoring system from linear to non-linear, heavily favoring fully transparent grid lines (10x higher score) over partially transparent ones
- Adjusted boundary comparison to only prefer alternatives when transparency score differs by more than 5%, preventing minor variations from overriding better transparent boundaries
- When scores are similar, the algorithm now prefers divisors closer to the originally detected cell size
- This fix affects
ScoreDivisorByTransparency,ScoreCellSizeForDimension, andFindBestTransparencyAlignedDivisormethods
-
Manual Recompile Silent Failure After Build: Fixed an issue where the "Request Script Recompilation" menu item and shortcut would stop responding after building a project (particularly on Linux)
- Added defensive null check in compilation pending evaluator to prevent silent
NullReferenceException - The null evaluator scenario could occur when static field initialization failed or was corrupted during build operations without a domain reload
- Added defensive null check in compilation pending evaluator to prevent silent
- Documentation only (
WGroupEndexamples)
- Fix packaging issue related to rsp files
- Fix packaging issue related to Styles/Elements/Progress.meta file
- Updated
package.jsonto be OpenUPM-compatible
-
llms.txt: Added
llms.txtfile following the llmstxt.org specification for LLM-friendly documentation- Provides a structured overview of package features, APIs, and documentation links for AI assistants
- Enables third-party LLMs to quickly understand and work with the Unity Helpers codebase
-
Auto-Load Singleton System: New singleton pattern with configurable lifetimes and thread-safe execution
-
UnityMainThreadGuardfor ensuring operations run on the main thread -
UnityMainThreadDispatcherwith configurable lifecycle management -
AutoLoadSingletonAttributefor automatic singleton instantiation during Unity start-up phases - Reworked the autoload singleton architecture for better scene persistence
-
-
Asset Change Detection: Monitor asset changes with
DetectAssetChangedAttribute- Annotate methods to automatically execute when specific asset types are created or deleted
- Support for inheritance with
IncludeAssignableTypesoption - Automatic registration and callback execution via asset processor
-
Inspector Attributes & Drawers: Comprehensive custom inspector tooling
-
WGroupattribute for visual grouping of inspector properties, including collapsible sections and palette-driven styling -
WButtonattribute with support for async/Task methods and custom styling -
WEnumToggleButtonsattribute for toggle-based enum selection in inspector -
WShowIfconditional display attribute improvements - Enhanced dropdown attributes for better property selection
-
-
Inspector Validation Attributes: Enhanced inspector feedback for null/invalid field detection
-
WNotNullAttributenow displays a warning or error HelpBox in the inspector when the field is null -
WNotNullAttributenew properties:MessageType(Warning/Error enum) andCustomMessage(string) for customizable feedback -
WNotNullAttributenew constructor overloads for easy customization of message type and custom messages - New
WNotNullPropertyDrawerfor rendering validation feedback in the inspector -
ValidateAssignmentAttributenow displays a warning or error HelpBox in the inspector when the field is invalid (null, empty string, or empty collection) -
ValidateAssignmentAttributenew properties:MessageType(Warning/Error enum) andCustomMessage(string) for customizable feedback -
ValidateAssignmentAttributenew constructor overloads for easy customization of message type and custom messages - New
ValidateAssignmentPropertyDrawerfor rendering validation feedback in the inspector - Both attributes maintain full backward compatibility—existing code works unchanged with default warning messages
-
StringInListAttributenow supports[StringInList(nameof(Method))]to call parameterless instance or static methods on the decorated object, and the drawer exposes the same experience in both IMGUI and UI Toolkit inspectors -
WButtonnow supportsgroupPriorityandgroupPlacementparameters for fine-grained control over button group ordering and positioning
-
-
Serialization Data Structures: Production-ready serializable collections
-
SerializableDictionary<TKey, TValue>with custom inspector drawer -
SerializableSortedDictionary<TKey, TValue>with ordered iteration -
SerializableHashSet<T>with custom set drawer and duplicate detection -
SerializableSortedSet<T>for sorted sets withIComparable<T>elements -
SerializableNullable<T>for nullable value types in inspector -
SerializableTypefor type references in inspector - Pagination support for large collections in the Editor
- Inline nested editor support for complex types
- Undo/Redo support for all serializable collection modifications
- Confirmation dialog when clearing collections to prevent accidental data loss
-
-
Editor Tooling Enhancements:
- Enhanced
StringInListDrawerfor validated string input with suggestions - UI Toolkit-based editors for modern Unity editor integration
- Configurable settings windows with improved layout and styling
- Move up/down buttons for reordering collection elements
- Add/remove buttons with improved visual styling
- Added Request Script Recompilation menu item (
Tools ▸ Wallstop Studios ▸ Unity Helpers) to manually trigger script recompilation - The "Request Script Compilation" utility includes a Unity Shortcut Manager binding (default Ctrl/Cmd + Alt + R) for quick access. The shortcut appears under Wallstop Studios / Request Script Compilation and can be remapped like any other Unity shortcut.
- Coroutine wait buffer defaults can now be configured under Project Settings ▸ Wallstop Studios ▸ Unity Helpers. The generated
Resources/WallstopStudios/UnityHelpers/UnityHelpersBufferSettings.assetapplies the selected quantization, entry caps, and LRU mode automatically on domain reload or when the player starts (unless your code overrides the values at runtime). - Added Unity Method Analyzer (
Tools ▸ Wallstop Studios ▸ Unity Helpers ▸ Unity Method Analyzer) for detecting inheritance issues and Unity lifecycle method errors across C# codebases
- Enhanced
-
Random Number Generation: Extended PRNG capabilities
- Added
BlastCircuitRandomandWaveSplatRandomgenerators with improved performance characteristics - New
RandomGeneratorMetadatasystem for inspecting generator properties - Extended random sampling methods with improved statistical distribution
- Added
-
Array Pooling: New
SystemArrayPool<T>and unifiedPooledArray<T>return type- Added
SystemArrayPool<T>wrappingSystem.Buffers.ArrayPool<T>.Sharedfor variable-sized allocations - Added
PooledArray<T>struct as unified return type for all array pools with properLengthtracking -
WallstopArrayPool<T>andWallstopFastArrayPool<T>now returnPooledArray<T>instead ofPooledResource<T[]> - Critical for
SystemArrayPool<T>: returned arrays may be larger than requested; always usepooled.Length, notarray.Length
- Added
-
Grid Concave Hull Reliability:
- Edge-split and grid KNN hull builders now insert missing axis-aligned corners after the initial pass, guaranteeing concave stair, horseshoe, and serpentine inputs retain their interior vertices even when only sparse samples exist.
- Improved handling of staircase patterns, axis-corner preservation, and diagonal-only rejection for more robust hull generation.
-
Random Number Generation: Critical edge case handling
- Fixed poor handling of
NextFloat()andNextDouble()potentially returning exactly0.0or1.0in extensions and helpers - Fixed sampling bias in
NextUlong()for more uniform distribution - Ensured proper range handling for all random generation methods
- Fixed poor handling of
-
IllusionFlow Random: Serialization and performance issues
- Fixed deserialization bugs in
IllusionFlowcomponents - Optimized to reduce GC churn during effect processing
- Fixed deserialization bugs in
-
Editor & Inspector: Multiple rendering and caching bugs
- Fixed stale label caching causing incorrect inspector display
- Fixed scene loading edge cases in editor workflows
-
Component System: Runtime component query issues
- Fixed
GetComponentsreturning null arrays in some cases - Fixed jitter-related bugs in component updates
- Fixed
-
Extension Methods: Mathematical edge cases
- Fixed calculations with zero or negative areas (bounds, rectangles, circles)
- Fixed color averaging bugs in color extension methods
-
Geometry & Spatial: Convex hull computation
- Fixed convex hull behavior for edge cases (collinear points, degenerate cases)
- Improved hull computation accuracy and performance
-
GUID Generation: Specification compliance
- Fixed GUID v4 generation to properly set version and variant bits per RFC 4122
-
Editor Settings: Project settings and drawer issues
- Fixed obsolete API usage in editor code
- Fixed project settings panel rendering issues
- Fixed reflection-based property access for better performance
-
Scriptable Object Singletons: Duplicate folders should no longer be created
- Fixed a "should-never-happen" bug where, if a singleton was accessed for the first time off the main thread, it would never be able to be accessed for the lifetime of the process
- Fixed a bug where auto-creation would happen concurrently with AssetDatabase importing, resulting in Unity crashing with no error message
-
Performance Optimizations:
- Reduced reflection usage in custom property drawers (10-100x faster in some cases)
- Optimized list navigation and caching for large collections
- Faster indexing and lookup in serializable data structures
- Improved drawer update performance for complex inspector hierarchies
- Data structure conversion optimizations
- Minor relational component performance improvements, specifically for children components
- Reduced GC allocations across property drawers, editor tools, and various helpers
-
EnhancedImage Visual Component:
- Improved material instance management with proper cleanup OnDestroy
- Better domain reload handling for HDR color and material state persistence
- Enhanced editor inspector with automatic material fix suggestions
-
Animation Editor Tools:
- Fixed FPS field handling in Animation Viewer and Sprite Sheet Animation Creator
- Improved frame reordering and preview responsiveness
-
Documentation:
- Major documentation refactor for clarity
- Added GUID generation documentation
- Improved inline code documentation
- Better attribute usage examples
-
Breaking Changes:
- Removed
KVector2(deprecated, use Unity's built-in Vector2) - Renamed
KGuid->WGuid, changed data layout - Forced
WallstopFastArrayPoolto forceunmanagedtypes. This pool does not clear arrays and can leak references. -
WallstopArrayPool<T>andWallstopFastArrayPool<T>now returnPooledArray<T>instead ofPooledResource<T[]>. Update usages frompooled.resourcetopooled.Arrayand consider usingpooled.Lengthfor iteration bounds. - The legacy line-division concave hull overload
BuildConcaveHull(IEnumerable<FastVector3Int>, Grid, float scaleFactor, float concavity)has been marked[Obsolete]and now throwsNotSupportedException. UseConcaveHullStrategy.KnnorConcaveHullStrategy.EdgeSplit(and their dedicated helpers) instead; the docs now call out this retirement explicitly. -
StringInListinspectors now keep the property row single-line and open a dedicated popup that contains search, pagination, and keyboard navigation for large catalogs (applies to both IMGUI and UI Toolkit drawers, includingSerializableType).
- Removed
-
API Improvements:
- Simplified
TryAddmethods for collections - Enforced
IComparableconstraint where appropriate for sorting - Better handling of null additions in collections
- Updated editor tooling for better integration with Unity 2021.3+
- Default IList.Sort to Grail sort for stability and improved performance
- Simplified
-
Documentation:
- Updated documentation to reflect new features and API changes
- Re-organized documentation into a more logical structure
- Consolidated documentation naming around kebab-case
- Deprecate BinaryFormatter with
[Obsolete], keep functional for trusted/legacy scenarios. - Make GameObject JSON converter output structured JSON with
name,type, andinstanceId. - Fix stray
UnityEditorimports in Runtime to ensure clean player builds.
- See commit history for incremental features (random engines, spatial trees, serialization converters, editor tools).