3.4.0
See the roadmap for details
Added
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".
Changed
[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.
Fixed
- 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.
Install
- Import the attached
.unitypackageinto a Unity project, or - install
com.wallstop-studios.unity-helpers@3.4.0from npm / OpenUPM through Unity Package Manager.
The release includes the npm tarball and the .unitypackage, each with a .sha256 checksum.