• Unity
  • Play an animation on editor mode

  • تم التحرير
Related Discussions
...

Hello, i'm trying to play the animation on a scene, while we are on editor mode. is this possible ?

The little "preview " windows on the inspector is not enough for me. (for various reason)

شهر واحد لاحقا

Sorry to have missed your post. Do you mean to have a preview of animation frames in the scene view while not in play mode? I see your point, but what frame of an animation would you like to see previewed in the scene view, or would you expect to have a control-panel to select frames of an animation? Then you could still not easily test any transitions. Overall it seems to work a bit against the way Unity is designed.
I would either recommend
a) to drag the borders of the inspector window larger, then the preview window can get quite large - you can also preview transitions this way, or
b) to enter play-mode to see animations with all transitions as they are appearing.
If you have a better idea how things should be previewed in the editor, please let us know.

Harald, he's talking about in Unity.

Currently, we don't have animation preview in scene view in Unity.
But it could be added. We'll look into how it might be done, 'cause we don't actually use Unity's own windows. We would have to come up with a new interface for it.

شهر واحد لاحقا

Yas i was talking about unity. thanks for your answers

2 أشهر لاحقا

We would also like to have this feature. This would be extremely useful when positioning UI related Spines that have animations. In our case option to show first and/or last frame of selected animation would be enough.

4 أعوام لاحقا
using Spine.Unity;
using UnityEngine;
using Sirenix.OdinInspector;

[ExecuteInEditMode]
public class SpineAnimationScrubber : MonoBehaviour
{
    [ShowInInspector]
    private SkeletonAnimation skeletonAnimation;
    [ShowInInspector]
    [SpineAnimation] 
    public string animationName;
    private float previousTime = 0.0f;
    private float maxRange = 1.0f;
    [ShowInInspector, PropertyRange(0.0, 1.0f, MaxMember = "@maxRange")]
    private float currentTime = 0.0f;
    private string previousAnimationName;
    private Spine.TrackEntry anim;

private void OnValidate()
{
    if( skeletonAnimation == null )
    {
        skeletonAnimation = GetComponent<SkeletonAnimation>();
    }

    if( skeletonAnimation == null ) return;

    if( animationName != previousAnimationName )
    {
        anim = skeletonAnimation.AnimationState.SetAnimation(0, animationName, false);
        maxRange = anim.Animation.Duration * 0.5f;
        currentTime = 0.0f;
        previousTime = -1.0f;
        previousAnimationName = animationName;
    } else if( anim == null && !string.IsNullOrEmpty( animationName ) )
    {
        anim = skeletonAnimation.AnimationState.SetAnimation( 0, animationName, false );
    }

    if( anim != null && currentTime != previousTime )
    {
        if( skeletonAnimation.state == null ) return;
        anim.TrackTime = currentTime;
        skeletonAnimation.Update( currentTime );
        skeletonAnimation.state.Update( currentTime );
        skeletonAnimation.LateUpdate();
        skeletonAnimation.skeleton.Update(currentTime);
        previousTime = currentTime;
    }
}
}

I just needed something like this, so I made a simple animation scrubber. Could easily just update the current time OnInspectorGUI if you make custom inspector for it, but I had no need for that part.
I am sure the code can be optimized, but I didn't bother tbh.

Note: I am using OdinInspector for the PropertyRange attribute, but you can achieve the same dynamic range with a custom inspector.
Let me know if it helps 🙂

    Thanks for sharing your code, always much appreciated! 🙂

    6 أيام لاحقا

    Thank you!

    5 أشهر لاحقا

    Hey, does anyone know why WiseKodama had to multiply the animation duration by 0.5f here?

    I find that if i do not do this, the animation scrubs through twice as fast as it should and its on its final frame by t = trackEntry.Animation.Duration / 2.

    But i don't understand why that would be the case? Is Animation.Duration reporting a duration that's twice as long as the actual duration? What's going on there?

    • تم التحرير

    @SpiralCircus Good catch! Actually this is a bug in the source code provided by @WiseKodama, since the same delta time is applied twice to the AnimationState here:

    skeletonAnimation.Update(currentTime); // this calls animationState.Update(currentTime)
    skeletonAnimation.state.Update(currentTime); // this line should be removed

    So to correct the code, remove the second line, then you no longer need to halve the duration.
    Change this line:
    maxRange = anim.Animation.Duration * 0.5f;
    To the following:
    maxRange = anim.Animation.Duration;

      Awesome, thanks Harald!

      You're welcome, thanks for reporting.

      5 أشهر لاحقا

      Harald Thanks for the fix 🙂

      18 أيام لاحقا

      Inspired by these articles, I created a script 'EditorSkeletonPlayer' that supports SkeletonAnimation and SkeletonGraphic. I hope you find this useful.
      (If possible, I think it would be great if this feature could be officially adopted into the Spine Unity Runtime 😊)

      #if UNITY_EDITOR
      using Spine;
      using Spine.Unity;
      using UnityEditor;
      using UnityEditor.Callbacks;
      using UnityEngine;
      
      [ExecuteInEditMode]
      public class EditorSkeletonPlayer : MonoBehaviour
      {
          private IEditorSkeletonWrapper _skeletonWrapper;
          private TrackEntry _trackEntry;
          private string _oldAnimationName;
          private bool _oldLoop;
          private double _oldTime;
      
          [DidReloadScripts]
          private static void OnReloaded()
          {
              //force awake when scripts are reloaded
              var editorSpineAnimations = FindObjectsOfType<EditorSkeletonPlayer>();
      
              foreach (var editorSpineAnimation in editorSpineAnimations)
              {
                  editorSpineAnimation.Awake();
              }
          }
      
          private void Awake()
          {
              if (Application.isPlaying) return;
      
              if (_skeletonWrapper == null)
              {
                  if (TryGetComponent<SkeletonAnimation>(out var skeletonAnimation))
                  {
                      _skeletonWrapper = new SkeletonAnimationWrapper(skeletonAnimation);
                  }
                  else if (TryGetComponent<SkeletonGraphic>(out var skeletonGraphic))
                  {
                      _skeletonWrapper = new SkeletonGraphicWrapper(skeletonGraphic);
                  }
              }
      
              _oldTime = EditorApplication.timeSinceStartup;
              EditorApplication.update += EditorUpdate;
          }
      
          private void OnDestroy()
          {
              EditorApplication.update -= EditorUpdate;
          }
      
          private void EditorUpdate()
          {
              if (Application.isPlaying) return;
              if (_skeletonWrapper == null) return;
              if (_skeletonWrapper.State == null) return;
      
              if (_oldAnimationName != _skeletonWrapper.AnimationName || _oldLoop != _skeletonWrapper.Loop)
              {
                  _trackEntry = _skeletonWrapper.State.SetAnimation(0, _skeletonWrapper.AnimationName, _skeletonWrapper.Loop);
                  _oldAnimationName = _skeletonWrapper.AnimationName;
                  _oldLoop = _skeletonWrapper.Loop;
              }
      
              if (_trackEntry != null)
              {
                  _trackEntry.TimeScale = _skeletonWrapper.Speed;
              }
      
              float deltaTime = (float)(EditorApplication.timeSinceStartup - _oldTime);
              _skeletonWrapper.Update(deltaTime);
              _oldTime = EditorApplication.timeSinceStartup;
      
              //force repaint to update animation
              EditorApplication.QueuePlayerLoopUpdate();
          }
      
          private class SkeletonAnimationWrapper : IEditorSkeletonWrapper
          {
              private SkeletonAnimation _skeletonAnimation;
      
              public SkeletonAnimationWrapper(SkeletonAnimation skeletonAnimation)
              {
                  _skeletonAnimation = skeletonAnimation;
              }
      
              public string AnimationName
              {
                  get { return _skeletonAnimation.AnimationName; }
              }
      
              public bool Loop
              {
                  get { return _skeletonAnimation.loop; }
              }
      
              public float Speed
              {
                  get { return _skeletonAnimation.timeScale; }
              }
      
              public Spine.AnimationState State
              {
                  get { return _skeletonAnimation.state; }
              }
      
              public void Update(float deltaTime)
              {
                  _skeletonAnimation.Update(deltaTime);
      
              }
          }
      
          private class SkeletonGraphicWrapper : IEditorSkeletonWrapper
          {
              private SkeletonGraphic _skeletonGraphic;
      
              public SkeletonGraphicWrapper(SkeletonGraphic skeletonGraphic)
              {
                  _skeletonGraphic = skeletonGraphic;
              }
      
              public string AnimationName
              {
                  get { return _skeletonGraphic.startingAnimation; }
              }
      
              public bool Loop
              {
                  get { return _skeletonGraphic.startingLoop; }
              }
      
              public float Speed
              {
                  get { return _skeletonGraphic.timeScale; }
              }
      
              public Spine.AnimationState State
              {
                  get { return _skeletonGraphic.AnimationState; }
              }
      
              public void Update(float deltaTime)
              {
                  _skeletonGraphic.Update(deltaTime);
              }
          }
      
          private interface IEditorSkeletonWrapper
          {
              string AnimationName { get; }
      
              bool Loop { get; }
      
              float Speed { get; }
      
              Spine.AnimationState State { get; }
      
              void Update(float deltaTime);
          }
      }
      #endif

        @Sekomu Awesome, thanks very much for sharing!

        Sekomu (If possible, I think it would be great if this feature could be officially adopted into the Spine Unity Runtime 😊)

        We would be happy to include such a component in the spine-runtimes officially (potentially with some slight modifications)! Could you please send us a signed contributor license agreement (CLA) to contact@esotericsoftware.com as listed in the Contributing section here:
        https://github.com/EsotericSoftware/spine-runtimes/tree/4.1/#contributing

          No need to resend the CLA! @Harald will have a look at the pull request.

          @Sekomu Thanks very much for the pull request, sorry that I missed that you already sent a CLA in the past!

          We have merged the pull request and added some improvements on top of it, e.g. added single frame preview as well via a Fixed Track Time parameter. This has been requested in this issue ticket.

          From the changelog:

          Added experimental EditorSkeletonPlayer component to allow Editor playback of the initial animation set at SkeletonAnimation or SkeletonGraphic components. Add this component to your skeleton GameObject to enable the in-editor animation preview. Allows configurations for continuous playback when selected, deselected, and alternative single-frame preview by setting Fixed Track Time to any value other than 0. Limitations: At skeletons with variable material count the Inspector preview may be too unresponsive. It is then recommended to disable the EditorSkeletonPlayer component (at the top of the Inspector) to make it responsive again, then you can disable Play When Selected and re-enable the component to preview playback only when deselected.

          New spine-unity 4.1 and 4.2-beta unitypackages are available here as usual:
          https://esotericsoftware.com/spine-unity-download