KybernetikGames / animancer

Documentation for the Animancer Unity Plugin.
65 stars 8 forks source link

Mixertransistion2D: The PlayableGraph is invalid. It has either been Disposed or was never created #267

Closed Flemming-DC closed 1 year ago

Flemming-DC commented 1 year ago

Hi Kybernetics

Environment

Description

I have a bug with Mixertransistion2D. When I set the mixer's State.Parameter to a new value, then it sometimes throws the error "The PlayableGraph is invalid. It has either been Disposed or was never created." Do you know what could be causing this?

ps: I don't know how to dispose or create PlayableGraphs, since I am not familiar with the implementation details of Animancer.

Code with error

public Vector2 moveRelativeToLookDirection { get => strafingMixer.State.Parameter; set => strafingMixer.State.Parameter = value; }

Vector2 newMoveDirection = Vector2.Lerp(moveRelativeToLookDirection, inputDirection, strafingRotationSpeed * Time.deltaTime); try { moveRelativeToLookDirection = newMoveDirection; // THIS LINE IS GIVING ERRORS } catch (Exception e) { actMachine.SetNextState(typeof(Idle)); Debug.LogWarning($"failed to set moveRelativeToLookDirection in strafing on {transform.parent.Path()}"); print($"Time.timeSinceLevelLoad = {Time.timeSinceLevelLoad}"); print(e.Message); print(e.StackTrace); print($"updating moveRelativeToLookDirection to {newMoveDirection}"); }

Console output

failed to set moveRelativeToLookDirection in strafing on Functionality\Enemies\Long Right Path\Plaza\Tower\Wall\Zombie - Axe (1)

Time.timeSinceLevelLoad = 551.0944

The PlayableGraph is invalid. It has either been Disposed or was never created.

at (wrapper managed-to-native) UnityEngine.Playables.PlayableGraph.DisconnectInternal_Injected(UnityEngine.Playables.PlayableGraph&,UnityEngine.Playables.PlayableHandle&,int) at UnityEngine.Playables.PlayableGraph.DisconnectInternal (UnityEngine.Playables.PlayableHandle playable, System.Int32 inputPort) [0x00000] in <86acb61e0d2b4b36bc20af11093be9a5>:0 at UnityEngine.Playables.PlayableGraph.Disconnect[U] (U input, System.Int32 inputPort) [0x0000f] in <86acb61e0d2b4b36bc20af11093be9a5>:0 at Animancer.MixerState.OnRemoveChild (Animancer.AnimancerState state) [0x00030] in C:\Mine\Unity\RPG 2\Assets\Externals\Animancer\Internal\Mixer States\MixerState.cs:369 at Animancer.AnimancerState.Destroy () [0x00008] in C:\Mine\Unity\RPG 2\Assets\Externals\Animancer\Internal\Core\AnimancerState.cs:787 at Animancer.ClipState.Destroy () [0x00007] in C:\Mine\Unity\RPG 2\Assets\Externals\Animancer\Internal\Core\ClipState.cs:131 at Animancer.MixerState.DestroyChildren () [0x0001d] in C:\Mine\Unity\RPG 2\Assets\Externals\Animancer\Internal\Mixer States\MixerState.cs:401 at Animancer.MixerState.Destroy () [0x00000] in C:\Mine\Unity\RPG 2\Assets\Externals\Animancer\Internal\Mixer States\MixerState.cs:385 at Animancer.Validate.AssertPlayable (Animancer.AnimancerNode node) [0x0001f] in C:\Mine\Unity\RPG 2\Assets\Externals\Animancer\Internal\Editor\Validate.cs:66 at Animancer.AnimancerPlayable.RequirePreUpdate (Animancer.IUpdatable updatable) [0x0000a] in C:\Mine\Unity\RPG 2\Assets\Externals\Animancer\Internal\Core\AnimancerPlayable.cs:1101 at Animancer.AnimancerNode.RequireUpdate () [0x00000] in C:\Mine\Unity\RPG 2\Assets\Externals\Animancer\Internal\Core\AnimancerNode.cs:273 at Animancer.MixerState`1[TParameter].set_Parameter (TParameter value) [0x0000e] in C:\Mine\Unity\RPG 2\Assets\Externals\Animancer\Internal\Mixer States\MixerStateT.cs:43 at Strafing.set_moveRelativeToLookDirection (UnityEngine.Vector2 value) [0x00000] in C:\Mine\Unity\RPG 2\Assets\Scripts\Character System\Meta States\Strafing.cs:23 at Strafing.Tick () [0x0009a] in C:\Mine\Unity\RPG 2\Assets\Scripts\Character System\Meta States\Strafing.cs:68 UnityEngine.MonoBehaviour:print (object) Strafing:Tick () (at Assets/Scripts/Character System/Meta States/Strafing.cs:76) Statemachine:Tick () (at Assets/Scripts/Character System/statemachine/Statemachine.cs:36) StatemachineBehavior:Update () (at Assets/Scripts/Character System/statemachine/StatemachineBehavior.cs:34) ActMachine:Update () (at Assets/Scripts/Character System/statemachine/ActMachine.cs:22)

updating moveRelativeToLookDirection to (0.00, 1.00)

KybernetikGames commented 1 year ago

How are you creating/playing the state? Are you destroying any objects or playing the transition on multiple characters before the error occurs? I can see what's causing the error, but can't figure out how you got it into that situation.

Flemming-DC commented 1 year ago

The MixerTransition2D is defined in a scriptable object. At startup each character will assign the mixer to a local variable. When the character's statemachine enters the strafing state, then he plays the mixer. The mixer's State.Parameter then get's updated each frame until he leaves the strafing state.

Yes, the transition is being played on multiple characters, since they share the same scriptable object. Furthermore, a lot of those characters are getting destroyed.

Code

public class AnimationsGivenWeaponClass : ScriptableObject
{
    [Header("Passive")]
    public ClipTransition idle;
    public ClipTransition hurt;
    public ClipTransition veryHurt;
    [Header("Movement")]
    public ClipTransition run; 
    public ClipTransition sprint;
    public MixerTransition2D strafingMixer; // THIS MIXER IS DEFINED HERE
}

private void Start()
{
    mover = this.GetComponentInSiblings<ScivoloMover>();
    animancer = this.GetComponentInSiblings<AnimancerComponent>();
    actMachine = this.GetComponentInSiblings<ActMachine>();
    animationUpdater = this.GetComponentInSiblings<AnimationUpdater>(false);
    SetAnimation(animationUpdater.animations);
    strafingMixer = animationUpdater.animations.strafingMixer; // HERE THE MIXER IS ASSIGNED TO A LOCAL VARIABLE
    interruptable = true;
    if (animationUpdater != null)
        animationUpdater.OnAnimationsChanged += SetAnimation;
}

public override void OnEnter(object input)
{
    if (mover == null)
        Start();
    animancer.Play(strafingMixer); // NOW WE PLAY THE MIXER
}
KybernetikGames commented 1 year ago

You're assigning the same transition to every character so the strafingMixer.State will be the same for all of them, meaning they're all controlling the parameter on the same state (instead of their own state) which is owned by whichever character last played the transition and will be destroyed by that character.

The UnShared section explains the issue in more detail and how to solve it. Unfortunately, since you're storing the transition in a custom ScriptableObject you won't be able to use the UnShared classes so you'll need to directly cache the state after playing it as shown on the left side of the table.

Flemming-DC commented 1 year ago

I just had my first two 100% error free playthroughs, since I moved the combat system from a dummy level to a real level.

Thanks a lot :)