• RuntimesUnity
  • 通过FGUI点击切换动画资源文件时的问题

  • Editado

使用FGUI按钮事件时,会有一帧的骨骼设置状态。使用其他代码方式没有该问题
以下是示例代码:其中Update中的方法可以做到正确切换,start中的ui.GetChild("Sword").onClick.Set切换动画文件时会有默认的设置姿态

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Spine;
using Spine.Unity;
using FairyGUI;
public class Demo : MonoBehaviour
{
    public SkeletonDataAsset Sword;
    public SkeletonDataAsset Wand;
    public SkeletonAnimation curAnimation;
    public GameObject body;

    public GComponent ui;
    // Start is called before the first frame update
    void Start()
    {
        curAnimation = GetComponent<SkeletonAnimation>();
        ui = GameObject.Find("UIPanel").GetComponent<UIPanel>().ui;

        ui.GetChild("Sword").onClick.Set(() =>
        {
            ReloadSkeletonAsset(1);
        });

        ui.GetChild("Wand").onClick.Set(() =>
        {
            ReloadSkeletonAsset(2);
        });
    }

    // Update is called once per frame
    void Update()
    {
        if(Input.GetKeyDown(KeyCode.P))
        {
            ReloadSkeletonAsset(2);
        }
        if (Input.GetKeyDown(KeyCode.O))
        {
            ReloadSkeletonAsset(1);
        }
    }
    public void ReloadSkeletonAsset(int id)
    {


        SkeletonDataAsset skeleton;
        string animation;
        if(id == 1)
        {
            skeleton = Sword;
            animation = "n-b0029-防守-姿势循环";
        }
        else
        {
            skeleton = Wand;
            animation = "h-b0029-防守-姿势循环";
        }

        if (skeleton != null)
        {

            body.GetComponent<SkeletonAnimation>().ClearState();
            body.GetComponent<SkeletonAnimation>().skeletonDataAsset = skeleton;
            body.GetComponent<SkeletonAnimation>().Initialize(true);
            body.GetComponent<SkeletonAnimation>().Skeleton.SetSkin("No1");
            PlayAnimation(animation);
        }

    }

    public void PlayAnimation(string animationName)
    {
        curAnimation.AnimationState.SetAnimation(0, animationName, true);
    }
}

我可以提供一个14MB的示例项目

Related Discussions
...

在使用FGUI按钮事件切换动画资源文件时出现一帧的骨骼设置状态问题,可能是因为在切换SkeletonDataAsset时,SkeletonAnimation组件的状态没有及时更新。根据提供的代码,您可以尝试在切换SkeletonDataAsset后立即调用Initialize(true)方法来重新初始化SkeletonAnimation组件,这样可以确保所有引用和状态都被正确更新。

ReloadSkeletonAsset方法中,您已经调用了Initialize(true),这应该是正确的做法。然而,您可能需要确保在调用Initialize(true)之前,所有相关的状态和引用都被清除,例如使用ClearState()方法清除当前的动画状态。

如果问题仍然存在,您可以尝试在Initialize(true)之后立即设置动画,以确保动画状态在初始化后立即被应用。

public void ReloadSkeletonAsset(int id)
{
    SkeletonDataAsset skeleton;
    string animation;
    if(id == 1)
    {
        skeleton = Sword;
        animation = "n-b0029-防守-姿势循环";
    }
    else
    {
        skeleton = Wand;
        animation = "h-b0029-防守-姿势循环";
    }

    if (skeleton != null)
    {
        var skeletonAnimation = body.GetComponent<SkeletonAnimation>();
        skeletonAnimation.ClearState();
        skeletonAnimation.skeletonDataAsset = skeleton;
        skeletonAnimation.Initialize(true);
        skeletonAnimation.Skeleton.SetSkin("No1");
        PlayAnimation(animation);
    }
}

确保在Initialize(true)之后立即调用PlayAnimation方法,以便在初始化后立即应用动画。

Thanks for sending us a Unity project that can reproduce the problem, the code returned by Spinebot doesn't seem to have changed much, in fact it seemed to work just fine. As far as I could tell, modifying Demo.cs as follows worked. (It is not much different from what Spinebot suggested, although some variable names have been changed to more common names.):

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Spine;
using Spine.Unity;
using FairyGUI;
public class Demo : MonoBehaviour
{
    public SkeletonDataAsset Sword;
    public SkeletonDataAsset Wand;
    public SkeletonAnimation skeletonAnimation;
    public GameObject body;

    public GComponent ui;

    void Start()
    {
        skeletonAnimation = GetComponent<SkeletonAnimation>();
        ui = GameObject.Find("UIPanel").GetComponent<UIPanel>().ui;

        ui.GetChild("Sword").onClick.Set(() =>
        {
            ReloadSkeletonAsset(1);
        });

        ui.GetChild("Wand").onClick.Set(() =>
        {
            ReloadSkeletonAsset(2);
        });
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.O))
        {
            ReloadSkeletonAsset(1);
        }
        if(Input.GetKeyDown(KeyCode.P))
        {
            ReloadSkeletonAsset(2);
        }
    }

    public void ReloadSkeletonAsset(int id)
    {
        SkeletonDataAsset skeleton;
        string animation;
        if(id == 1)
        {
            skeleton = Sword;
            animation = "n-b0029-防守-姿势循环";
        }
        else
        {
            skeleton = Wand;
            animation = "h-b0029-防守-姿势循环";
        }

        if (skeleton != null)
        {
            skeletonAnimation.ClearState();
            skeletonAnimation.skeletonDataAsset = skeleton;
            skeletonAnimation.Initialize(true);
            skeletonAnimation.Skeleton.SetSkin("No1");
            PlayAnimation(animation);
        }
    }

    public void PlayAnimation(string animationName)
    {
        skeletonAnimation.AnimationState.SetAnimation(0, animationName, true);
    }
}

谢谢回复,示例项目在你们那边是正常运行的吗?我看代码除了变量名并没有任何修改。
我的unity版本为2022.3.12,spine版本为4.2 这个项目的问题在于:使用UI按钮切换和使用按键“O“,”P“切换时有所不同:键盘按键切换是正确的效果,而UI按钮切换时,很明显的可以观察到spine模型有一帧的骨骼设置状态

    fengmin Perhaps there is a translation problem, but I didn't realize that you meant that there is a difference between switching from keyboard inputs and switching from buttons, so I was only checking about switching skeletons from buttons. Anyway, I thought you should see how my modified Unity project looks like, so I sent you the modified Unity project via email.

    When I opened the project you sent us, the skeleton was pretty messed up when I used the buttons, so I thought the current behavior in my modified Unity project was correct, however, after trying again, I realized that the problem might be that the setup poses are showing up depending on the timing of the switch. Is that correct the problem you are having? If that is the case, it certainly has not been corrected by this script fix alone. Referring to section "SkeletonAnimation的生命周期" in the spine-unity runtime documentation and changing the order in which the scripts are executed may solve the problem.
    https://zh.esotericsoftware.com/spine-unity-main-components#SkeletonAnimation%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F

      Misaki
      是的,问题就是这个。
      我会去尝试了解FGUI的.onClick.Set生命周期执行时机和unity Update 以及SpineSkeletonAnimation的Update的执行顺序。看看问题出在哪

      Misaki 谢谢,通过检查我发现FGUI的按钮事件代码执行顺序优先与SkeletonAnimation的Update导致了该问题,我通过将事件触发后先进入一个专门的协程来等待一帧后执行修复了该问题。有什么别的建议吗?
      还有一个新的问题,从Demo.cs可以看出我的目的是在游戏运行中切换SkeletonDataAsset 。我发现在每次游戏运行的第一次切换时,会异常的卡顿一段比较长的时间,之后就不会了,刚开始我以为是Resources加载资源的原因,于是我对SkeletonDataAsset 资源进行了预加载,可是还是会在每次运行游戏的第一次卡顿很久。

        fengmin Glad to hear that reviewing the order of execution solved the problem! It is common for the order of execution to need to be modified, so if that is not causing any additional problems, I think the current solution should be fine.

        Regarding the long loading times each time you run the game, your skeleton data leaves room for you to try some of the commonly available loading and performance improvement methods, so I suggest you give that a try. For example, your skeleton data is exported in JSON format, but a file exported in binary format would be lighter and faster to load. Also, your skeleton has two texture atlas pages, but it would be more efficient to have them all on one page. Please take a look at the “性能问题” section in the FAQ section of the documentation:
        spine-unity 常见问题(FAQ) #性能问题