Hi, I'd like my character to blink when it's idle, walking, and during other animations. I found some posts about mixing tracks, and it seems like there's no way to do this without scripting. Is this correct?
There's an "Animation Tracks" option in the Skeleton Root Motion component, but I couldn't figure out how to assign animations to each track. I also tried using the "Preview" feature in Spine 2D, but mixing two animations there doesn't seem to work in Unity.

For reference, I'm using Adventure Creator, and I export Spine 2D in binary format with .skel.bytes and .atlas.txt extensions. Could someone please help me understand how to mix blinking or lip movements with other animations? Thank you!

  • Harald hat auf diesen Beitrag geantwortet.
    Related Discussions
    ...

    To achieve the effect of your character blinking while performing other animations like idle or walking, you can use multiple animation tracks in Spine. This allows you to layer animations on top of each other. Here’s a step-by-step guide to help you set this up:

    1. Set Up Animations in Spine Editor:

      • Create a separate animation for blinking.
      • Ensure that the blinking animation only affects the eye bones or slots and does not interfere with other parts of the skeleton.
    2. Export Animations:

      • Export your animations in binary format as you have been doing.
    3. Using Multiple Tracks in Unity:

      • In Unity, you can use the SkeletonAnimation component to manage multiple tracks. Here’s an example script to help you set this up:
    using UnityEngine;
    using Spine.Unity;
    
    public class CharacterAnimationController : MonoBehaviour
    {
        public SkeletonAnimation skeletonAnimation;
        public AnimationReferenceAsset idleAnimation;
        public AnimationReferenceAsset walkAnimation;
        public AnimationReferenceAsset blinkAnimation;
    
        void Start()
        {
            skeletonAnimation = GetComponent<SkeletonAnimation>();
            PlayIdleAnimation();
            PlayBlinkAnimation();
        }
    
        void PlayIdleAnimation()
        {
            skeletonAnimation.AnimationState.SetAnimation(0, idleAnimation, true);
        }
    
        void PlayWalkAnimation()
        {
            skeletonAnimation.AnimationState.SetAnimation(0, walkAnimation, true);
        }
    
        void PlayBlinkAnimation()
        {
            var trackEntry = skeletonAnimation.AnimationState.SetAnimation(1, blinkAnimation, true);
            trackEntry.MixBlend = MixBlend.Add;
        }
    }
    1. Mixing Animations:

      • The SetAnimation method is used to set animations on different tracks. Track 0 is typically used for the main animation (idle, walk, etc.), and track 1 can be used for the blinking animation.
      • The MixBlend.Add ensures that the blinking animation is added on top of the base animation without overriding it.
    2. Preview in Spine Editor:

      • Use the Preview feature in Spine to see how the animations blend together. This can help you fine-tune the animations before exporting them to Unity.
    3. Adventure Creator Integration:

      • Ensure that Adventure Creator is set up to use the SkeletonAnimation component and that it can trigger the appropriate animations based on game events.

    By following these steps, you should be able to mix blinking or other small animations with your main animations seamlessly.

    acai I also tried using the "Preview" feature in Spine 2D, but mixing two animations there doesn't seem to work in Unity.

    That's just a preview, it does not export anything differently if you preview your animation there or not.

    acai Hi, I'd like my character to blink when it's idle, walking, and during other animations. I found some posts about mixing tracks, and it seems like there's no way to do this without scripting. Is this correct?

    That's incorrect. If you're using SkeletonMecanim, which uses Unity's Mecanim API to set animations, you can create additional animation layers using the Mecanim Animator window as you normally would. Each animation layer corresponds to an animation track (like when using AnimationState.SetAnimation(trackIndex, animation,..))

    If you are using SkeletonAnimation, which uses Spine's AnimationState API to set animations, you need to use a script to set animations at higher tracks. The SkeletonAnimation component just exposes a parameter to set the initial animation at the first track.

    In general you will always sooner or later want to use scripting to set animations, you will hardly get by just setting a single initial animation. You might want to check out both SkeletonAnimation and SkeletonMecanim first before choosing which way of setting animations suits your use cases better.

    See the documentation here:
    https://esotericsoftware.com/spine-unity-main-components#Main-Components
    https://esotericsoftware.com/spine-unity-main-components#SkeletonMecanim-Component

    Also please be sure to check out the example scenes that come with the spine-unity package on how to set Animations with SkeletonAnimation and AnimationState:
    https://esotericsoftware.com/spine-unity-examples

    Got it thank you! These are very helpful!

    12 Tage später

    I have blink, idle, walk, and back animations all in the same file and tried mixing blink with idle and walk. However, the back animation flickers when I do this. When I call the back animation using Adventure Creator's Action, the Mesh Renderer’s materials have no elements, except when blinking, which only shows the closed eyelid element.

    If I don't use my AnimationMixingSpine2D script, the back animation works fine. I'm a novice coder, I think the issue lies in the script.

    I also found the SpineBlinkPlayer script from the Runtime Examples, but I'm unsure which Scene/Project it belongs to. If the SpineBlinkPlayer script is suitable, could you point me to the correct Scene/Project? Thank you.
    `

    using UnityEngine;
    using Spine.Unity;
    using System.Collections;

    public class AnimationMixingSpine2D : MonoBehaviour
    {
    public SkeletonAnimation skeletonAnimation;

    // Public animation names
    public string idleAnimation = "idle";
    public string walkAnimation = "walk";
    public string runAnimation = "run";
    public string talkAnimation = "talk";
    public string blinkAnimation = "blink";
    
    // Public variables to control animation speeds
    public float blinkSpeed = 1.0f;
    public float talkSpeed = 1.0f;
    
    // Public variables to control blink frequency
    public float minBlinkDelay = 2f;
    public float maxBlinkDelay = 5f;
    
    private Coroutine blinkCoroutine;
    private Coroutine talkCoroutine;
    
    void Start()
    {
        // Ensure SkeletonAnimation is assigned
        if (skeletonAnimation == null)
            skeletonAnimation = GetComponent<SkeletonAnimation>();
    
        // Play idle animation on track 0 as default
        SetBaseAnimation(idleAnimation);
    
        // Start the blinking and talking animations
        StartBlinking();
        StartTalking();
    }
    
    void StartBlinking()
    {
        if (blinkCoroutine != null)
        {
            StopCoroutine(blinkCoroutine);
        }
        blinkCoroutine = StartCoroutine(BlinkingCoroutine());
    }
    
    void StartTalking()
    {
        if (talkCoroutine != null)
        {
            StopCoroutine(talkCoroutine);
        }
        talkCoroutine = StartCoroutine(TalkingCoroutine());
    }
    
    IEnumerator BlinkingCoroutine()
    {
        while (true)
        {
            float delay = UnityEngine.Random.Range(minBlinkDelay, maxBlinkDelay);
            yield return new WaitForSeconds(delay);
    
            var trackEntry = skeletonAnimation.AnimationState.SetAnimation(1, blinkAnimation, false);
            trackEntry.TimeScale = blinkSpeed;
            trackEntry.MixBlend = MixBlend.Add; // Ensure blending
            trackEntry.Complete += (entry) =>
            {
                Debug.Log("Blink animation complete.");
            };
        }
    }
    
    IEnumerator TalkingCoroutine()
    {
        while (true)
        {
            float delay = UnityEngine.Random.Range(0.1f, 0.5f);
            yield return new WaitForSeconds(delay);
    
            var trackEntry = skeletonAnimation.AnimationState.SetAnimation(2, talkAnimation, false);
            trackEntry.TimeScale = talkSpeed;
            trackEntry.MixBlend = MixBlend.Add; // Ensure blending
            trackEntry.Complete += (entry) =>
            {
                Debug.Log("Talk animation complete.");
            };
        }
    }
    
    // Public method to change the base animation (idle, walk, run, or talk)
    public void SetBaseAnimation(string animationName)
    {
        skeletonAnimation.AnimationState.SetAnimation(0, animationName, true);
    }
    
    public void SetIdleAnimation()
    {
        SetBaseAnimation(idleAnimation);
    }
    
    public void SetWalkAnimation()
    {
        SetBaseAnimation(walkAnimation);
    }
    
    public void SetRunAnimation()
    {
        SetBaseAnimation(runAnimation);
    }
    
    public void SetTalkAnimation()
    {
        SetBaseAnimation(talkAnimation);
    }
    
    public void StopWalking()
    {
        SetIdleAnimation();
    }

    }

    `

    Hi, just to update, here's the code based on SpineBlinkPlayer. It works, but if I trigger the 'back-opening-locker' animation, the character vanishes. Its Mesh Renderer's material is set to none.

    `using Spine.Unity;
    using System.Collections;
    using UnityEngine;

    namespace Spine.Unity.Examples
    {
    public class AnimationMixingSpine2D : MonoBehaviour
    {
    const int BlinkTrack = 1;
    const int MainTrack = 0;

        public AnimationReferenceAsset blinkAnimation;
        public AnimationReferenceAsset backOpeningLockerAnimation;  
        public float minimumDelay = 0.15f;
        public float maximumDelay = 3f;
    
        IEnumerator Start()
        {
            SkeletonAnimation skeletonAnimation = GetComponent<SkeletonAnimation>();
            if (skeletonAnimation == null) yield break;
            while (true)
            {
                var currentAnimation = skeletonAnimation.AnimationState.GetCurrent(MainTrack)?.Animation;
    
                if (currentAnimation != backOpeningLockerAnimation.Animation)
                {
                    skeletonAnimation.AnimationState.SetAnimation(BlinkTrack, blinkAnimation, false);
                }
    
                yield return new WaitForSeconds(Random.Range(minimumDelay, maximumDelay));
            }
        }
    }

    }`

    In Spine, I've set up my skeleton with one root bone and two hip bones. Hip bone 1 is used for walk, idle, and blink animations, while hip bone 2 is for the back animation. Is this setup going to cause any issues?

    Sorry for all the updates. I managed to fix the issue in Spine. My setup was incorrect because I didn't realize that keyframes shouldn't overlap.

      acai Sorry for the late reply. I'm glad you were able to solve the problem! I looked at the code you posted, but I thought it would be difficult to give detailed advice without knowing what the animations you are playing actually looks like, so if the animation looks strange next time, create a minimal project that can reproduce the problem and send it to us via email: contact@esotericsoftware.com

      Thank you so much! I'll make sure to do that next time. What I want is for my character to blink only when it's idle, walking, or running, and not blink when it's playing the 'back' animation. Right now, my character blinks all the time, but I've managed to hide the blink with its hair during the 'back' animation. 😅

      • Misaki hat auf diesen Beitrag geantwortet.
      • Misaki gefällt das.

        That's a great idea. Thank you! I'll give it a try!