Animancer v8.0 is coming

Animancer v8.0

This page was last updated 2024-05-10

Estimated Release 2024-??-??

Upgrade Guide

See the Animancer v7.0 Upgrade Guide if you're upgrading from a version older than that, then follow the new Upgrade Guide for this version.

For the first time ever, Animancer v8.0 will be a paid upgrade.

Purchases after 2024-05-01 can upgrade for free.

Older purchasers will receive a 50% discount.

See the Upgrade Guide page for details.

Major Features

Transition Libraries

This feature is still under development.

Transition Libraries allow you to look up transitions by name and modify what specific transitions look like depending on the character's previous state.

They somewhat resemble Animator Controllers in that they allow you to set up an interconnected web of animations and customise the transitions between them. The main difference is that they do not contain any logic, i.e. no parameters or conditions on the transitions, they simply modify a transition when you tell it to play. Transition Libraries are also fully open and modifiable at runtime.

The Transition Libraries thread has more details about my thought process and features planned for the system. Progress updates will be posted there and feedback is welcome.

Weighted Mask Layers

The experimental Weighted Mask Layers system has been turned into a proper feature. It allows layers to be masked so they control some bones but not others just like the standard AvatarMask system, but with more detail because you can control the Weight of each individual bone instead of only turning them on or off. It also supports fading between different sets of Weights over time.

  1. Add a WeightedMaskLayers Component:

  1. Click its Edit button to open a window where you can configure it.

  1. Use the ? column to select the objects you want to include in the mask.
  2. Use the Add Group button down the bottom to add as many groups as you want.
  3. For each object and group, enter the Weight you want the object to have when that group is active.
  • 1 is the same as including the object in an AvatarMask, i.e. Layer 1 will fully control the object.
  • 0 is the same as excluding the object from an AvatarMask, i.e. Layer 0 will fully control the object.
  • Values in between will interpolate between both layers.
  1. In code if you want to change the current Weight group you can get a reference to the WeightedMaskLayers component and call SetWeights(groupIndex) or FadeWeights(groupIndex, fadeDuration).

#154 and #328

Known Limitations:

  • It only supports 2 layers (Base + 1).
  • It only supports override blending (not additive).
  • These limitations could be removed with further development if enough users request it.

String References and Assets

strings are often a very convenient type which can make systems easy to use, but also have notable downsides such as being inefficient to compare and hard to refactor.

  • The Event Names system used to be based on strings and the new Event Binding and Parameter Binding systems would have been as well, so a new system has been implemented to avoid those downsides.
  • StringReference is a simple class which contains a string and is kept in a dictionary so that everything which calls StringReference.Get for a given string will be given the exact same instance.
    • This allows the system to check if two references are equal much faster by using a single equality instruction instead of potentially needing to check if every individual character is equal in two strings.
    • StringReference has an implicit conversion from string, meaning that any method which takes a StringReference parameter can also be given a regular string. That's less efficient though, because it needs to look up the reference in the dictionary every time, so it's much more efficient to cache the StringReference yourself beforehand, for example:
// This is an implicit conversion from string to StringReference:
public static readonly StringReference EventName = "Fire";

// It actually does this internally:
public static readonly StringReference EventName = StringReference.Get("Fire");

// Or you can reference a StringAsset in a serialized field:
[SerializeField] private StringAsset _Name;
  • StringAsset is a ScriptableObject which contains a StringReference initialized from its asset name.
    • This allows values to be managed more reliably and consistently referenced in multiple places without risking spelling mistakes each time or making it hard to rename things.
    • StringAssets can be created via Assets/Create/Animancer/String Asset or by using the New button in the Inspector (see below).
    • StringAsset fields normally show up in the Inspector like regular string fields, but if you move your mouse over them they change to let you pick an asset or use several functions:
      • New - If the field is empty, this button will create a new StringAsset and ask where you want to save it.
      • C - If the field has a reference, this button will copy its name to the clipboard.
      • X - If the field has a reference, this button will remove it.
Normal Appearance Mouse Hover

Animancer Events

Animancer Events have been reworked in several different ways, some of which would result in data loss if you don't run the Serialized Data Migration tool explained in the Upgrade Guide.

Shared Events

When you play a Transition, it creates an AnimancerState (or reuses the existing one if it was already created) and assigns its events to that state. The state has a direct reference to the transition's event sequance so any modifications to the state.Events would affect the transition.Events and vice versa. This is efficient, but often problematic in cases where a transition is shared by multiple characters (particularly when using Transition Assets).

That problem is now solved in the same way that Unity's Renderers handle their material and sharedMaterial properties:

  • AnimancerState.Events now ensures that the state owns the event sequence it gives you.
    • A transition will still give the state a direct reference to its events at first.
    • But the first time you access the state.Events it will create a new event sequence and copy the transition's events into it. Now the state owns its event sequence and will return the same one each time you access that property.
    • If a state has no events assigned, accessing this property will create an empty event sequence so it never returns null.
    • It doesn't technically create a new event sequence every time, it gets spare ones from the ObjectPool to minimise the performance cost of memory allocation and garbage collection.
    • This would have previously been very inefficient when events were automatically cleared from states whenever you played anything because all that copying and configuration would need to be repeated every time, however the Persistent Events change avoids that issue.
  • AnimancerState.SharedEvents doesn't do any of that, it simply returns whatever event sequence the state has, including null.
  • There are also two bool properties for checking the event details:

This change avoids the need for the UnShared transition system, so it has been removed because it was confusing to many users and didn't cover many common use-cases.

#261

Persistent Events

Animancer Events were originally designed to be automatically cleared from a state whenever you played something else in order to avoid conflicts if multiple scripts happened to use the same animation and therefore share the same state. That system was good for safety, but bad for efficiency because you had to either spend processing time re-configuring all the events every time you played an animation or spend development time configuring and storing the events separately so they could be assigned to the state every time you played it.

  • Transitions solve this by storing their own events which you could configure on startup so they could simply assign the events to the state when played.
  • But any transitions shared by multiple characters (particularly Transition Assets) couldn't be used in that way because all characters would be trying to configure the same event sequence.
  • The UnShared transition system was an attempt to solve that, but it was only usable in limited situations and has now been removed as noted above.
  • The old system was also confusing to many new users who did not expect their events to be cleared.

The new system is simple: allow users to configure their events when a state is first created, don't automatically clear them, and discourage sharing states between scripts.

  • AnimancerState.TryInitializeEvents has been added for easily configuring events when a state is first created. Its example usage looks like this:
var state = animancerComponent.Play(animationClipOrTransition);
if (state.TryInitializeEvents(out var events))
{
    events.SetCallback("Event Name", OnAnimationEvent);
    events.OnEnd = OnAnimationEnded;
}
  • When the state is first created, it won't own its events. If created by an AnimationClip it won't have any events and if created by a Transition it will have the transition's events which the state doesn't own.
  • Since it doesn't own its events, TryInitializeEvents will return true and do the same thing as the Events property described above which it gives you in the events parameter for you to configure.
  • Every time you play the same state after that, it will already own its events so TryInitializeEvents will return false.
  • In order to reduce the chance of multiple scripts sharing the same state, each ClipTransition will now create its own state instead of having them share a state if they have the same AnimationClip.
  • In order to avoid overwriting the configured events on subsequent plays, Transitions will now only assign their Events when they first create a state instead of re-assigning them every time they are played (which was necessary when the events would have been automatically cleared from the state).
  • ExitEvents have also been reworked to be based around keeping the same instance in a field instead of clearing and object pooling instances.
  • One major difference between Animancer Events and Unity's inbuilt Animation Events is that Animancer Events aren't triggered on a state which is fading out.
    • The old system enforced this inherently because starting a fade out would have cleared the state's events.
    • The new system has the same behaviour by default, which can be disabled by setting the static AnimancerState.RaiseEventsDuringFadeOut = true;.

Event Names

Animancer Events can optionally be given names which can then be used to identify them in code. They previously used regular strings which made the system simple to use, but had several downsides such as being inefficient and hard to refactor.

Those strings have been replaced by String References and Assets (StringAssets in the serialized data and StringReferences in the runtime data).

Using the [EventNames] attribute now shows an info icon next to the name field with a tooltip listing all the expected names. If an unexpected name is selected, it will change to a warning icon.

This is one of the changes that would result in data loss if you don't run the Serialized Data Migration tool explained in the Upgrade Guide.

#264

Event Binding

In many cases, being able to set up event timings in the Inspector while previewing the animation and then assign callbacks to those events in code based on their names is a great workflow (and that's not changing). But in some cases that workflow isn't ideal or even viable, such as if you're playing an animation in a Transition Library by name which means you don't even know what's going to be played so you can't be sure what events you need to look for.

The solution to this is the new AnimancerComponent.Events property, which is a dictionary of String Reference names mapped to callbacks. Whenever an event with a name but no callback is triggered, it will look in the dictionary for a callback bound to its name. If an event has no callback in either location, it will still trigger OptionalWarning.UselessEvent to let you know that something is likely configured incorrectly. This is closer to the way Unity's inbuilt Animation Events work, except that it's still using delegates for flexibility instead of requiring a method name to match the event name in a component attached to the same GameObject as the Animator.

For example, here's a simple script that expects its transition to have events with the name "Footstep" set up in the Inspector:

public class FootstepWalkExample : MonoBehaviour
{
    public static readonly StringReference FootstepEvent = "Footstep";
    
    [SerializeField] private AnimancerComponent _Animancer;
    [SerializeField, EventNames] private ClipTransition _Walk;
    
    private void OnEnable()
    {
        _Walk.Events.SetCallbacks(FootstepEvent, PlayFootstepSound);
    
        _Animancer.Play(_Walk);
    }
    
    private void PlayFootstepSound() { }
}

Note the [EventNames] attribute. Since it isn't being given any names explicitly, it will look for static fields in the class it's in.

That's a nice simple script which would work perfectly fine on its own, but what if you have other animations in other scripts that also want to have footstep events? Obviously you could connect all of them to a central script which manages the footsteps, but that sort of thing can start turning your code base into a tangled mess of interconnected dependencies. Instead, the new Event Binding system would let you split those responsibilities into two scripts like this:

public class FootstepManager : MonoBehaviour
{
    public static readonly StringReference FootstepEvent = "Footstep";
    
    [SerializeField] private AnimancerComponent _Animancer;
    
    private void OnEnable()
    {
        _Animancer.Events.Set(FootstepEvent, PlayFootstepSound);
    }
    
    private void PlayFootstepSound() { }
}

public class WalkExample : MonoBehaviour
{
    [SerializeField] private AnimancerComponent _Animancer;
    [SerializeField] private ClipTransition _Walk;
    
    private void OnEnable()
    {
        _Animancer.Play(_Walk);
    }
}

The FootstepManager script registers its PlayFootstepSound method to be called by any event with the name "Footstep" in any animation and the WalkExample script doesn't need to know anything about footsteps, it's simply responsible for playing its animation and managing the mechanics of walking. It took a bit more code, but the system is now more flexible and modular so it should be easier to extend and maintain as your systems grow in complexity.

The event binding dictionary is shown in the Live Inspector (unless you leave it empty):

Event Callbacks

On its own, the new Event Binding system would have had one major drawback compared to Unity's inbuilt Animation Events: the inability to specify parameters in the Inspector. In the above example, the "Footstep" events have no way to indicate which foot they're referring to. You could give each foot a different event name, but that's not very convenient and still doesn't allow other parameter types like floats or asset references.

Instead of solving that issue by adding extra fields for several specific parameter types (like Animation Events do), it has been addressed by changing the serialized event callback field from a hard coded UnityEvent to a [SerializeReference] field which can hold anything that implements IInvokable (a simple interface with an Invoke() method).

  • Animancer includes its own UnityEvent class which simply inherits from Unity's regular one and implements IInvokable so it can be used for event callbacks exactly the same as before.
    • Since regular serialized fields store their data differently to [SerializeReference] fields, this is one of the changes that would result in data loss if you don't run the Serialized Data Migration tool explained in the Upgrade Guide.
    • It also supports UltEvents in the same way, which is much better than the old system where you could only use UnityEvents or UltEvents and swapping between them would lose all your event data.
  • The parameter issue is handled by AnimancerEvent.Parameter<T> which is an IInvokable that sets the static AnimancerEvent.CurrentParameter property then invokes the bound callback.
    • Any method can access the AnimancerEvent.CurrentParameter during an event (it's cleared afterwards).
    • There are also several methods in the Event Binding dictionary for binding callbacks with a parameter which can be used like this:
public static readonly StringReference FootstepEvent = "Footstep";

[SerializeField] private AnimancerComponent _Animancer;

private void OnEnable()
{
    _Animancer.Events.AddNew<AudioSource>(FootstepEvent, PlaySound);
}

public void PlaySound(AudioSource audioSource) { }
  • That method would be automatically given its value from the event's callback parameter set in the Inspector (or an error if it doesn't have a parameter).
  • Animancer includes several common parameter types: bool, double, float, int, long, Object, string.
  • You can easily add a new parameter type by creating a serializable class like this:
[Serializable] public class ParameterThing : Parameter<Thing> { }
// Inherit from ParameterBoxed<T> instead if Thing is a struct or enum.
  • Simply having that class in your project would make it selectable as a callback type.
  • You could also create a classs that implements IInvokable to direcly use it as the callback.

End Event Time

  • Animancer Lite now allows you to change the time of End Events. Most of the new features are Pro-Only so this restriction has been removed.

Parameters

Like with the Event Binding system, being able to directly control the parameters of a Mixer State or Controller State is often great, but can be really inconvenient if you don't have a strongly typed reference to the state such as if you're using Transition Libraries or even just Nested Mixers.

The solution is also quite similar: a new AnimancerComponent.Parameters property containing a dictionary of parameter values registered by name which other systems can get, set, and listen for changes to.

Mixer Parameters

  • The Inspector shows the Parameters dictionary below all the States:

  • You can then control the parameters in the dictionary like this (instead of directly on the state):
animancerComponent.Parameters.SetValue(SpeedParameter, value);
  • This is a one-way link. Directly changing the Mixer parameters will not affect the Animancer Parameter.
  • This is all completely optional. You can still directly set the mixerState.Parameter as before.

#257

Controller Parameters

Controller States and ControllerTransitions can also have their parameters bound to Animancer's parameters, though it's a bit more complicated than with Mixers.

  • When bound, any changes to the Animancer Parameter will update the corresponding parameter in the Animator Controller.
    • This is a one-way link. Changes to parameters inside an Animator Controller will not affect Animancer Parameters.
  • The simplest usage is to simply enable Bind All Parameters to have it bind every parameter in the Animator Controller to an Animancer Parameter with the same name and type.

  • Otherwise, if you put anything in the Bindings array the Bind All Parameters toggle becomes Rebind Names.
Rebind Names Disabled Rebind Names Enabled
Each name in the array will bind the Animator Controller parameter with that name to an Animancer Parameter with the same name. The array is split into two columns with the left specifying the name of a parameter in the Animator Controller and the right specifying the Animancer Parameter name you want to bind it to.
  • Note that the Bindings array contains String Assets (not regular strings).

Linear Blending Example

The Linear Blending Example previously contained two very similar scripts (LinearBlendTreeLocomotion and LinearMixerLocomotion) to demonstrate the same thing with both a Blend Tree inside an Animator Controller and with a Linear Mixer.

Being able to bind parameters in both types of states to the same Animancer Parameters means that they could now be handled by just one script, though it has still been split into two with separate responsibilities:

The example still consists of two scripts, but they're now much more useful because they only have one responsibility each so they could potentially be used in other situations too.

Fade Groups

Previously when transitioning between states, every state would fade independently. You could have one state fading out and another fading in but they weren't explicitly linked to each other. The old CustomFade system had to be built around that by cancelling regular fades and managing them on its own as a group.

Those systems have been unified so all fades are managed as a FadeGroup (even if it only has one thing in it such as when fading out a layer).

  • This is slightly more efficient in most cases because it calculates and applies the weights of multiple states in one go instead of independently.
  • It also makes it easier to do things like check what animation is fading out, though that isn't a particularly common use case.
  • The FadeGroup.Easing property allows you to apply a custom curve to the fade progression like the old CustomFade system.
state = animancerClone.Play(animation, 0.25f);
state.FadeGroup.SetEasing(Easing.Function.SineInOut);
  • SetEasing is an extension method which will do nothing if the FadeGroup is null (such as if the animation was already faded in so it didn't start another fade) and it also accepts the Easing.Function enum as a parameter instead of only a Func<float, float> delegate.
  • Active FadeGroups are shown in the Inspector if you have the Show Internal Details Display Option enabled.

Live Inspector

The AnimancerComponent Inspector in Play Mode has been given quite a few improvements to its visuals and rendering performance.

Old Inspector New Inspector
  • The header object field of each state is now just a flat box instead of an actual object field to reduce the unnecessary visual noise.
  • While a state is fading, its time bar shows a diagonal line indicating where its Weight will go over time.
  • Changed the Weight labels and time lines to be coloured based on their effective weight instead of just the raw weight, meaning that a - Mixers no longer show their children unless the mixer's details are expanded.
  • Added the ability to Alt + Click a state to expand/collapse all of its siblings as well.
  • Added event time indicators to states in the Inspector for Animancer Events and Animation Events.
  • Added the ability to ping states in the Inspector by clicking on fields that reference them.
  • Changed states to not show their Key in the header unless the same MainObject is used by multiple states.
  • Added a hover highlight to the animation binding warning icon so it looks more like a button.
  • Changed Mixers to ignore the "Auto Normalize Weights" Display Option since they're usually used for things that don't require the weights to add up to 1.
  • Added a "Repaint Every Frame" toggle to the AnimancerComponent Inspector when there are 10 or more Components on the GameObject because Unity won't do it by default to save performance.
  • Added null reference detection to the Animation Type warning system to identify animations with missing Sprites.

Transition Inspector

Many new users have trouble figuring out how to get animations to restart when you play them instead of continuing from their current time (despite the topic being covered many times in the documentation and code comments and tooltips). So the Inspector for Transitions has been changed to make it clearer. When the Speed or Start Time toggle is disabled, the regular fields will be replaced with a text box saying that it will continue at the current value which should hopefully make it obvious that you can tell it not to do that using the toggle.

Shared References

The Polymorphic Drawer system now lets you properly view and manage references that are shared by multiple fields.

For example, say you have 3 [SerializeReference] ClipTransition fields A, B, and C:

If A already has a value and you go to create an object for B, the menu will have a Shared Reference sub-menu where you can give it the same reference as A:

While A and B are referencing the same object, they will show a link toggle:

  • It has a randomly generated colour for the object so if other fields have shared references to other objects they will hopefully have a different colour.
  • It has a tooltip listing all the fields sharing that object.
  • Clicking it will show connection lines between the fields sharing that object.

Tools

Several of the Animancer Tools have been improved:

  • Pack Textures:
    • Fixed compile errors if Unity's Built-in Image Conversion module isn't present. Now the tool will simply display an error message.
  • Rename Sprites:
    • Merged the Sprites and Names into a single column.
    • Added thumbnails so you can see what you're working with.
    • Added a First Index field so you can choose to start numbering at 0 or 1 or whatever number you want. #316
    • Removed the New Names field.
    • Instead, anything you enter in a Sprite's name field will apply to it and all following Sprites until the next name you enter. For example:
Walk Down Walk Down and Left

First you type Mage-Walk-Down- for the first Sprite's name.

The First Index number is automatically added to the end and every Sprite after that is given the same name with the next number.

Because there are more than 10 Sprites (see the scroll bar) it uses 2 digits for all the numbers (i.e. starting at "01" instead of just "1").

Then you go to the 5th Sprite and call it Mage-Walk-Left-.

Now everything after it is using that name while the first 4 continue using Mage-Walk-Down- with a 1 digit suffix because there's only 4 of them now (and the Minimum Digits is 1).

The two names you've entered are shown in bold while the others which are following your naming convention are in italics.

  • Generate Sprite Animations:
    • Added fields to customize the animation being generated (if you want to animate something other than a SpriteRenderer): #283
      • Hierarchy Path: the name of each object in the Hierarchy below the Animator down to the object with the component you want to animate.
      • Target Type: the Assembly Qualified Name of the Component type on that object.
      • Property Name: the name of the field on that Component.
    • Added thumbnails and animation previews.

Examples

The Examples have mostly been ignored so far and will be worked on during the Alpha Testing phase as explained in the Upgrade Guide

Minor Features

  • Added AnimancerLayer.ActiveStates to efficiently track states that are playing or have any Weight or TargetWeight.
    • Changed AnimancerState.IsActive to include the current Weight so it corresponds to the state's presence in the ActiveStates list.
    • Added AnimancerState.IsCurrent with the old behaviour of IsActive (just IsPlaying and TargetWeight > 0).
    • Optimized various operations which previously iterated through all states in a layer to now only go through the active states. For example, playing a new animation only needs to stop or fade out the other animations which were actually playing instead of wasting time stopping states which were alreay stopped.
  • Added a reflection based system for associating CustomGUI drawers with their target objects.
    • Added support for custom CustomGUI on internal details (visible in the AnimancerComponent Inspector when the "Show Internal Details" display setting is on).
    • Added FadeGroup.Drawer.
    • Added EventDispatcherDrawer for drawing event dispatchers in the Post-Updatables list.
  • Added AnimancerEvent.Sequence.SetCallbacks, AddCallbacks, and RemoveCallbacks for controlling all events with the given name instead of only the first one. #263
  • Added AnimancerEvent.Dispatcher.MaximumFullLoopCount to prevent a short and fast looping animation from invoking all its events too many times every frame. It's still unlikely that you'll get useful behaviour from invoking so many events, but this should help limit how badly it can lock up the app at least.
  • Added MixerState.NormalizedParameter for treating the parameter as a value between 0 and 1 regardless of the actual thresholds assigned.
  • Added IParametizedState to simplify parameter drawing in the Inspector for Mixers and ControllerStates.
  • Added AnimancerGraph.DestroyPlayable in case you want to destroy it without destroying the whole graph (in case the graph was owned by another system).
  • Added [AnimancerHelpUrl] attribute which uses a type reference to generate the URL instead of needing to hard code the string.
  • Added ClipTransitionSequence.TryGetCumulativeTime.
  • Added oldValue as a parameter to IPolymorphicReset.Reset so it can potentially copy over things based on specifically known previous types.
  • Added Normalized Start Time field to SoloAnimation.
  • Added OptionalWarning.EventPlayMismatch to help detect event registration issues with shared Transitions.
  • Added OptionalWarning.DynamicAnimation which warns if you try to play an animation that isn't saved as an asset because animations can't be created dynamically at runtime (except Legacy animations which Animancer can't use).
  • Added CloneContext system for cloning complex object trees and translating their internal references to the corresponding clones.
  • Added TimeSynchronizer to replace the old TimeSynchronizationGroup. #250

Improvements

  • Improved Polymorphic Drawer to be able to properly draw objects with custom drawers for their type.
  • Improved Object Pool logging to log the contents of all pools when the Play Mode state changes.
  • Improved ManualMixerState.ToString to put the mixer type first and be cached separately from the DebugName so it doesn't appear as a string label in the Inspector.
  • Improved SimpleTimer:
    • Changed it to track long ticks instead of double seconds.
    • Added an optional format string in case more or less than 3 decimal places of precision is needed. Can also set it to null for it to use the ticks in its strings instead of seconds.
  • Improved the animation bindings warning icons:
    • Optimized their drawing to be more efficient.
    • Removed the loading icons while the bindings are being checked since they will usually not need to show a warning anyway.
    • Removed the ITransition interfaces nested inside each state type because they're no longer necessary.
  • Improved the AppendDescription methods:
    • Added IHasDescription with standard extension methods.
    • Added IUpdatable and IDisposable descriptions to the text if they implement IHasDescription. This is used by FadeGroups.
    • Added AnimancerUtilities.AppendField for easily appending a named value.
    • Pulled AnimancerState.GetPath up to AnimancerNode.
    • Changed various inter-node references to use GetPath to make it easier to tell what it's referring to.
  • Optimized ClipState.Length and IsLooping to be retrieved from the AnimationClip in the constructor and cached because getting them from the AnimationClip allocaes some garbage every time.
  • Optimized Animancer Events by adding AnimancerState.GetEventDispatchInfo to get all the required details from the state in one call, which is much more efficient (especially for Mixers).
  • Optimized Mixers to cache their IsLooping property instead of checking if any child is looping every frame (which would have been recursive for nested Mixers).
  • Optimized the AnimancerState.NormalizedTimeD getter to only get the Length once.
  • Optimized Object.name usage by adding ObjectNameCache since it allocates garbage every time it's accessed.
    • You can define ANIMANCER_DISABLE_NAME_CACHE as a compilation symbol to disable this system.

Changes

  • Changed the "total Weight of all states in this layer does not equal 1" Inspector warning to appear on all layers instead of only the Base Layer. If you want to smoothly stop a layer you need to fade out the layer itself, not its states.
  • Changed all states to stay at Time = 0 after their first frame so that the first time they're played is consistent with subsequent re-plays.
  • Changed InputBuffer to replace the state machine's ForceSetDefaultState delegate to try to enter the buffered state first.
    • Changed the Weapons example's AttackState to use this feature for attack combos so it doesn't need to return to Idle for one frame between attacks.
  • Changed AnimancerLayers to use a list-like Capacity which doubles whenever it needs to increase instead of simply incrementing.
  • Changed the AnimancerLayer.MaxCloneCount setter to public.
  • Changed ITransition<T> to support covariance.
  • Changed ManagedReference handling to use the managedReferenceValue property.
  • Changed AnimancerEvent.Sequence.CopyFrom to not resize the Names array if it's already larger than necessary.
  • Changed AnimancerSettings to use dynamic [SerializeReference] fields so it's more modular.
  • Changed ICloneable to be generic.
  • Changed ControllerState.ActionsOnStop to not call GatherDefaultStates so it only gets called by CreatePlayable.
  • Changed AnimancerTransition.Events to virtual.
  • Changed AnimancerGraph.KeepChildrenConnected to always default to false instead of true for Generic Rigs because it no longer seems to give them better performance.
  • Changed OptionalWarning.UnusedNode to not log for nodes with a parent.
  • Changed AnimancerNode.CopyFrom and all inherited overloads to public.
  • Renamed ValidateAssertNotLegacy to AssertAnimationClip and added a null check to give a better exception.
  • Moved the Speed field from child transition classes up to the base AnimancerTransition since all states now support it (ControllerState didn't previously).
    • Removed OptionalWarning.UnsupportedSpeed for the same reason.
  • Simplified the AnimancerState parenting system.
    • AnimancerState.SetParent no longer takes a manually specified index. Both Layers and Mixers will add new states to the end of their list.
    • Removed AnimancerNode.AddChild since it's now unnecessary. Just use child.SetParent.
  • Split AnimancerReflection out of AnimancerUtilities and AnimancerEditorUtilities.
  • Split AnimancerLayerMixerList out of AnimancerLayerList.

Removals

  • Removed FastEnumerator members which modified the list because they exposed unexpected vulnerabilities since enumerators aren't expected to allow modifications.
  • Removed the Inspector warning "A state is playing at weight 0" because that's actually supported now.
  • Removed AnimancerState.DelayedPause and integrated its functionality into ControllerState directly since that's the only place it was used.
  • Removed AnimancerGUI.GetNarrowText since it wasn't really useful and added unnecessary complexity.
  • Removed FastComparer and FastEqualityComperer because they weren't notably faster and were sometimes slower.
  • Removed OptionalWarning.LockedEvents since you would need to explicitly access state.SharedEvents to run into the issues it warns about.

Fixes

  • Fixed the ReadMe to only log that a new version is available once per Unity Editor session instead of logging again every domain reload.
  • Fixed fading to include the root AnimancerGraph.Speed in the fade time progression.
  • Fixed AnimancerEditorUtilities.PlayModeState to be properly initialized in Edit Mode.
  • Fixed AnimancerStateDictionary.GetEquivalentNode to connect a newly cloned state to the correct layer.
  • Fixed incorrect comment on AnimancerState.MoveTime and added more detail. It previously claimed that Animancer Events would be skipped, but that isn't true.
  • Fixed several incorrect usages of the component name in HybridAnimancerComponent. #297
  • Fixed Animancer Events to not start triggering every frame if you used one to re-play the animation that triggered it. #298
  • Fixed AnimancerStateDrawer to not break GUI control IDs after it.
  • Fixed the Live Inspector state foldout to properly clear the hot control when clicked.
  • Fixed TransitionDrawer indentation in Unity 2022.
  • Fixed TransitionDrawer to still show its foldout arrow in array fields even if thats the only field in the declaring type. #307
  • Fixed TransitionDrawer to still show its foldout arrow if the target Transition is in a serializable class.
  • Fixed TimeScale to do nothing when disabled.
  • Fixed AnimancerLayer.Mask to allow null to be assigned without allocating any garbage in case you want to set it from an unassigned serialized field or copy from another layer which has no mask. #303
  • Fixed IUpdatable usage to continue enumerating properly if an item is removed during an update before it has been updated.
  • Fixed AnimancerNode.MarkAsUsed to be properly Assert-Conditional.
  • Fixed ExampleInput.WASD to work properly with the Play Mode Domain Reload disabled.
  • Fixed AnimancerGraph.InsertOutputPlayable to connect the correct playable.
  • Fixed AnimancerState.RecreatePlayable to retain its IK flags and connect the Playable to the parent if necessary.
  • Fixed AnimancerNodeDrawer to allow fades to be manually started in the Inspector.
  • Fixed the SpriteDataEditor.SpriteCount setter to initialize all the sprites instead of leaving them null.
  • Fixed the dummy event callback drawer to automatically expand a newly created item.
  • Moved the IPolymorphic implementation from ITransition to AnimancerTransition so it doesn't interfere with custom Object Field drawers (from Inspector Gadgets or any other system) on Transition Asset references.
  • Fixed PolymorphicDrawer to not draw the header line twice if it can't find another drawer for the target object type.
  • Fixed Mixer Time Synchronization to calculate the speed properly for nested Mixers.
  • Fixed Ctrl + Click on a Mixer child in the Inspector causing it to re-parent under the layer and play directly. Instead it will now just play the Mixer.
  • Fixed the Mixer Transition Inspector to not show a toggles on the Speed fields to disable them. #258
  • Fixed LinearMixerTransitionDrawer to not lose the text field being edited when a sorting warning appears or disappears. #343
  • Fixed ControllerState.GetStateInfo to return a default uninitialized state instead of giving an error if called before the Playable is initialized.
  • Fixed the Inspector for states with 0 length to show the time highlight bar across their whole area.
  • Fixed AnimancerGraph.CopyFrom to properly copy custom fades and do nothing if called on itself.
  • Fixed AnimancerLayer.CopyStatesFrom to reuse existing states instead of destroying and recreating them all.
  • Fixed AnimancerState cloning into a new graph uses the same Key.
  • Fixed AnimancerUtilities.TryGetIsLooping and TryGetLength to not cause exceptions if the given object is a destroyed AnimationClip. #342
  • Fixed Real Speed field in the Inspector to not cause drag events to be lost when it appears or disappears.
  • Fixed AnimancerGraphCleanup to work properly when the Play Mode Domain Reload is disabled.
    • Replaced OptionalWarning.CreateGraphWhileDisabled with a warning when AnimancerGraphCleanup is forced to clean up a graph that wasn't destroyed in Play Mode.
  • Fixed scripts to support API changes in Unity 6.
    • Rigidbody.velocity renamed to linearVelocity.