Unity---Spine动画

news2024/10/6 1:35:33

目录

1.介绍

2.优点

3.spine导出的unity资源

4.导入

5.导入报错的解决方案

6.使用

7.代码示例

1.加载Spine骨骼动画:

2.控制Spine动画的播放:

3.暂停和恢复动画播放:

4.监听动画事件:

5.切换皮肤(换装)

6.获取骨骼的Transform信息:

7.控制骨骼动画的混合和交叉淡入:

8.控制动画的速度:

9.获取动画状态信息:

10.动态替换骨骼纹理:

11.播放Spine动画的指定轨道:

12.获取当前动画的时间和持续时间:

13.控制动画循环次数:

14.动态更改动画混合的权重:

15.暂停和恢复所有动画轨道:

16.动态创建并替换插槽的Attachment:

17.使用Spine动画事件触发Unity事件:

18.控制动画播放速度随机化:

19.动态切换Spine Atlas图集:

20.动态创建骨骼动画:

21.骨骼动画的事件监听与处理:

8.unity-spine运行库下载


1.介绍

Unity Spine是一个收费的跨平台的强大的2D骨骼动画工具,它能够轻松创建复杂的角色动画。

要在unity中使用spine动画的话,需要下载对应的spine运行库

⚠️注意使用的spine运行库版本要与unity版本相对应,

对应关系见下图:

 

2.优点

  1. 轻量级和高效:Spine动画使用基于骨骼的动画技术,相比传统的逐帧动画,它们具有更小的文件大小和更低的内存占用。这使得Spine动画在移动设备和低端硬件上的性能更好。

  2. 灵活性:使用Spine,你可以创建高度可定制的动画,包括骨骼的变形、缩放、旋转和平移。你可以轻松地调整动画的速度、混合不同的动画,以及实现复杂的角色动画控制。

  3. 运行时动画:Spine动画可以在运行时进行实时修改和控制。这意味着你可以通过代码来改变动画的播放状态,根据角色的行为和环境条件进行相应的动画交互。

  4. 平台兼容性:Unity支持多个平台,包括PC、移动设备和主机,而Spine动画可以轻松地在这些平台上进行部署和播放。

3.spine导出的unity资源

一般情况下,美术会导出下列3个文件

.json 存储骨骼信息
.png 使用的图片图集
.atlas.txt 图片在图集中的位置信息
当我们把这三个资源导入到已经引入了Spine运行库的Unity工程后会自动为我们生成

_Atlas 材质和.atlas.txt文件的引用配置文件
_Material 材质文件
_SkeletonData json和_Atlas资源的引用配置文件

⚠️:但使用 .json 格式读取动画数据是比较慢且运行效率较低的方式。

因为spine动画使用json文件后期优化效果不太好,容易造成卡顿,加载过慢等。而二进制文件加载速度就比较快了。

Spine 支持Binary format ,二进制的数据导出,采用这种方式导出的格式是:.png 、.skel 和 .atlas 

4.导入

1.在图集.atlas后面加上.txt后缀

2.在二进制文件.skel后面加上.betys后缀

5.导入报错的解决方案

1.可能是美术那边导出时出现的问题。你可以让美术那边再导出一次。同一个spine动画让美术导出json文件和二进制文件。如果json文件导入也出现错误,那就是美术那边导出的问题。

  2.如果导入json没问题。导入二进制有问题,那么可能是运行库版本和spine版本不一致的问题。首先你可以查看一下untiy中spine运行库的版本是多少。是否和spine版本一致。

 3.二进制文件导入unity中后如果没有自动实例化对象,那么你需要手动创建。在创建的过程中有时候你会发现你创建的对象在场景中使用的时候变大了。具体说是变大了100倍。有人说我创建的时候也修改scale的大小了啊(0.01),怎么还会那么大呢?这其中就有些技巧了,你创建的时候先修改scale值,修改完之后再将需要的文件拖到相应的位置。那么你创建的大小就是缩放后的正常的大小。在场景中直接使用就是美术给你的正常大小了。

6.使用

1.unity中添加spine的三种方式 ,如下图:

 2.ugui中使用SkeletonGraphic(UnityUI)

 

3. 其他使用SkeletonAnimation

4.一般来说,在以下情况下你可能会使用SkeletonRenderer:

  1. 角色动画:如果你的游戏中有2D角色需要进行复杂的骨骼动画,你可以使用Spine创建角色的动画,并将SkeletonRenderer组件添加到Unity场景中的角色对象上。然后,该组件将负责在运行时渲染角色的骨骼动画。

  2. UI动画:你可以使用Spine创建2D UI元素的动画,例如按钮、图标、菜单等。通过将SkeletonRenderer添加到UI元素上,你可以在运行时播放Spine动画来增强用户界面的交互和视觉效果。

  3. 敌人/怪物动画:如果你的游戏中有敌人、怪物或NPC需要复杂的动画效果,你可以使用Spine创建骨骼动画,并将SkeletonRenderer组件添加到相应的游戏对象上。

  4. 特效动画:Spine还可以用于创建2D特效动画,比如爆炸、火焰、魔法等。将SkeletonRenderer组件与特效对象一起使用,可以实现更加生动逼真的效果。

7.代码示例

1.加载Spine骨骼动画:

使用Spine的SkeletonDataAsset类来加载Spine骨骼数据,并通过SkeletonAnimation组件来播放动画。

using Spine.Unity;

public class SpineAnimationController : MonoBehaviour
{
    public SkeletonDataAsset skeletonDataAsset;
    private SkeletonAnimation skeletonAnimation;

    void Start()
    {
        skeletonAnimation = GetComponent<SkeletonAnimation>();
        skeletonAnimation.skeletonDataAsset = skeletonDataAsset;
        skeletonAnimation.Initialize(true);
    }
}

解释:

  • 首先,您需要在Unity中创建一个空GameObject,并附加SkeletonAnimation组件。这个组件用于播放Spine动画。
  • 创建一个public字段来存储SkeletonDataAsset,这是Spine动画的骨骼数据。
  • 在Start()方法中,获取SkeletonAnimation组件的引用,并将SkeletonDataAsset赋值给skeletonAnimation.skeletonDataAsset。
  • 调用Initialize(true)方法来初始化SkeletonAnimation。传入true表示启用MeshRenderer,以便在场景中显示骨骼动画。

2.控制Spine动画的播放:

using Spine.Unity;

public class SpineAnimationController : MonoBehaviour
{
    public SkeletonDataAsset skeletonDataAsset;
    private SkeletonAnimation skeletonAnimation;

    void Start()
    {
        skeletonAnimation = GetComponent<SkeletonAnimation>();
        skeletonAnimation.skeletonDataAsset = skeletonDataAsset;
        skeletonAnimation.Initialize(true);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            skeletonAnimation.AnimationState.SetAnimation(0, "walk", true);
        }
    }
}

解释:

  • 在Update()方法中,我们可以使用Input.GetKeyDown(KeyCode.Space)来检测是否按下了空格键。
  • 如果按下了空格键,我们通过skeletonAnimation.AnimationState.SetAnimation()方法来播放名为"walk"的动画。
  • 第一个参数0表示trackIndex,表示要将动画放置在哪个轨道(track)上。Spine允许在同一时间在多个轨道上播放多个动画,0是默认的轨道。
  • 第二个参数是动画名称,这里是"walk"。您可以将其替换为其他Spine动画的名称。
  • 第三个参数true表示循环播放动画,如果设置为false,则动画只会播放一次。

3.暂停和恢复动画播放:

using Spine.Unity;

public class SpineAnimationController : MonoBehaviour
{
    public SkeletonDataAsset skeletonDataAsset;
    private SkeletonAnimation skeletonAnimation;

    void Start()
    {
        skeletonAnimation = GetComponent<SkeletonAnimation>();
        skeletonAnimation.skeletonDataAsset = skeletonDataAsset;
        skeletonAnimation.Initialize(true);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            skeletonAnimation.AnimationState.SetAnimation(0, "walk", true);
        }

        if (Input.GetKeyDown(KeyCode.P))
        {
            if (skeletonAnimation.AnimationState.GetCurrent(0) != null)
            {
                skeletonAnimation.AnimationState.GetCurrent(0).TimeScale = 0f; // Pause animation
            }
        }

        if (Input.GetKeyDown(KeyCode.R))
        {
            if (skeletonAnimation.AnimationState.GetCurrent(0) != null)
            {
                skeletonAnimation.AnimationState.GetCurrent(0).TimeScale = 1f; // Resume animation
            }
        }
    }
}

解释:

  • 在Update()方法中,我们添加了检测按下"P"和"R"键的代码来实现暂停和恢复动画的播放。
  • 当按下"P"键时,我们通过GetCurrent(0)方法获取当前正在播放的动画轨道,并将其TimeScale设置为0,这会将动画暂停。
  • 当按下"R"键时,我们将动画轨道的TimeScale设置为1,这会恢复动画的正常播放。

4.监听动画事件:

using Spine;
using Spine.Unity;

public class SpineAnimationController : MonoBehaviour
{
    public SkeletonDataAsset skeletonDataAsset;
    private SkeletonAnimation skeletonAnimation;

    void Start()
    {
        skeletonAnimation = GetComponent<SkeletonAnimation>();
        skeletonAnimation.skeletonDataAsset = skeletonDataAsset;
        skeletonAnimation.Initialize(true);

        // 订阅动画事件
        skeletonAnimation.AnimationState.Event += HandleAnimationEvent;
    }

    private void HandleAnimationEvent(TrackEntry trackEntry, Spine.Event e)
    {
        Debug.Log("Animation Event: " + e.Data.Name);
        // 在此处执行事件相关的逻辑
    }
}

解释:

  • 在Start()方法中,我们通过skeletonAnimation.AnimationState.Event += HandleAnimationEvent;订阅了动画事件。
  • 创建HandleAnimationEvent方法来处理动画事件。
  • 当Spine动画的事件被触发时,HandleAnimationEvent将会被调用,并且您可以在其中执行与事件相关的逻辑。例如,您可以在Spine编辑器中为某个动画帧添加事件,并在代码中根据事件的名称来触发相应的逻辑。

5.切换皮肤(换装)

using Spine.Unity;

public class SpineSkinController : MonoBehaviour
{
    public SkeletonDataAsset skeletonDataAsset;
    private SkeletonAnimation skeletonAnimation;
    private string currentSkinName;

    void Start()
    {
        skeletonAnimation = GetComponent<SkeletonAnimation>();
        skeletonAnimation.skeletonDataAsset = skeletonDataAsset;
        skeletonAnimation.Initialize(true);

        // 设置初始皮肤
        currentSkinName = "default";
        skeletonAnimation.Skeleton.SetSkin(currentSkinName);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.S))
        {
            // 切换到另一个皮肤
            string newSkinName = "alternate";
            skeletonAnimation.Skeleton.SetSkin(newSkinName);
            skeletonAnimation.Skeleton.SetSlotsToSetupPose(); // 刷新插槽以更新换装后的显示
            skeletonAnimation.AnimationState.Apply(skeletonAnimation.Skeleton);
        }
    }
}

解释:

  • 在Start()方法中,我们通过skeletonAnimation.Skeleton.SetSkin(currentSkinName)设置了初始的皮肤,这里使用了"default"皮肤。
  • 在Update()方法中,当按下"S"键时,我们通过skeletonAnimation.Skeleton.SetSkin(newSkinName)来切换到另一个皮肤,这里使用了"alternate"皮肤。
  • 切换皮肤后,为了确保新的皮肤在场景中立即显示,我们还需要调用skeletonAnimation.Skeleton.SetSlotsToSetupPose()刷新插槽,以更新换装后的显示。最后,通过skeletonAnimation.AnimationState.Apply(skeletonAnimation.Skeleton)将更改应用到动画状态。

6.获取骨骼的Transform信息:

using Spine.Unity;
using UnityEngine;

public class SpineBoneTransform : MonoBehaviour
{
    public SkeletonDataAsset skeletonDataAsset;
    private SkeletonAnimation skeletonAnimation;

    void Start()
    {
        skeletonAnimation = GetComponent<SkeletonAnimation>();
        skeletonAnimation.skeletonDataAsset = skeletonDataAsset;
        skeletonAnimation.Initialize(true);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.G))
        {
            // 获取指定骨骼的Transform信息
            Bone bone = skeletonAnimation.Skeleton.FindBone("boneName");
            if (bone != null)
            {
                Vector3 bonePosition = new Vector3(bone.WorldX, bone.WorldY, 0f);
                Quaternion boneRotation = Quaternion.Euler(0f, 0f, bone.WorldRotationX);
                Vector3 boneScale = new Vector3(bone.WorldScaleX, bone.WorldScaleY, 1f);

                Debug.Log("Bone Position: " + bonePosition);
                Debug.Log("Bone Rotation: " + boneRotation.eulerAngles);
                Debug.Log("Bone Scale: " + boneScale);
            }
        }
    }
}

解释:

  • 在Update()方法中,我们通过Input.GetKeyDown(KeyCode.G)检测是否按下"G"键。
  • 当按下"G"键时,我们使用skeletonAnimation.Skeleton.FindBone("boneName")查找指定名称的骨骼(需要将"boneName"替换为实际的骨骼名称)。
  • 如果找到了该骨骼,我们可以使用骨骼的WorldX、WorldY、WorldRotationX和WorldScaleX、WorldScaleY属性来获取骨骼的位置、旋转和缩放信息。注意,这些属性表示骨骼在世界坐标系中的信息。

7.控制骨骼动画的混合和交叉淡入:

using Spine.Unity;

public class SpineAnimationController : MonoBehaviour
{
    public SkeletonDataAsset skeletonDataAsset;
    private SkeletonAnimation skeletonAnimation;

    void Start()
    {
        skeletonAnimation = GetComponent<SkeletonAnimation>();
        skeletonAnimation.skeletonDataAsset = skeletonDataAsset;
        skeletonAnimation.Initialize(true);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Alpha1))
        {
            // 播放动画1,并在1秒内淡入混合到目标动画
            skeletonAnimation.AnimationState.SetAnimation(0, "animation1", true);
        }

        if (Input.GetKeyDown(KeyCode.Alpha2))
        {
            // 播放动画2,并在0.5秒内交叉淡入混合到目标动画
            skeletonAnimation.AnimationState.SetAnimation(1, "animation2", true).MixDuration = 0.5f;
        }
    }
}

解释:

  • 在Update()方法中,我们使用Input.GetKeyDown(KeyCode.Alpha1)和Input.GetKeyDown(KeyCode.Alpha2)来检测是否按下数字键1和2。
  • 当按下数字键1时,我们通过skeletonAnimation.AnimationState.SetAnimation()来播放名为"animation1"的动画。由于没有设置MixDuration,这里使用默认的混合时间(通常是0.2秒)。
  • 当按下数字键2时,我们通过skeletonAnimation.AnimationState.SetAnimation()来播放名为"animation2"的动画,并设置了MixDuration为0.5秒。这将导致动画2和当前正在播放的动画在0.5秒内交叉淡入混合。

8.控制动画的速度:

using Spine.Unity;

public class SpineAnimationController : MonoBehaviour
{
    public SkeletonDataAsset skeletonDataAsset;
    private SkeletonAnimation skeletonAnimation;
    public float animationSpeed = 1.0f;

    void Start()
    {
        skeletonAnimation = GetComponent<SkeletonAnimation>();
        skeletonAnimation.skeletonDataAsset = skeletonDataAsset;
        skeletonAnimation.Initialize(true);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.UpArrow))
        {
            // 增加动画速度
            animationSpeed += 0.5f;
            skeletonAnimation.timeScale = animationSpeed;
        }

        if (Input.GetKeyDown(KeyCode.DownArrow))
        {
            // 减少动画速度,但不低于0.1
            animationSpeed -= 0.5f;
            animationSpeed = Mathf.Max(animationSpeed, 0.1f);
            skeletonAnimation.timeScale = animationSpeed;
        }
    }
}

解释:

  • 在Update()方法中,我们使用Input.GetKeyDown(KeyCode.UpArrow)和Input.GetKeyDown(KeyCode.DownArrow)来检测是否按下上箭头键和下箭头键。
  • 当按下上箭头键时,我们增加animationSpeed变量的值,并将新的值应用到skeletonAnimation.timeScale,从而增加动画的播放速度。
  • 当按下下箭头键时,我们减少animationSpeed变量的值,并确保其不会低于0.1,然后将新的值应用到skeletonAnimation.timeScale,从而减少动画的播放速度。

9.获取动画状态信息:

using Spine.Unity;

public class SpineAnimationController : MonoBehaviour
{
    public SkeletonDataAsset skeletonDataAsset;
    private SkeletonAnimation skeletonAnimation;

    void Start()
    {
        skeletonAnimation = GetComponent<SkeletonAnimation>();
        skeletonAnimation.skeletonDataAsset = skeletonDataAsset;
        skeletonAnimation.Initialize(true);
    }

    void Update()
    {
        TrackEntry currentAnimation = skeletonAnimation.AnimationState.GetCurrent(0);
        if (currentAnimation != null)
        {
            Debug.Log("Current Animation: " + currentAnimation.Animation.Name);
            Debug.Log("Animation Time: " + currentAnimation.Time);
            Debug.Log("Animation Is Complete: " + currentAnimation.IsComplete);
        }
    }
}

解释:

  • 在Update()方法中,我们通过skeletonAnimation.AnimationState.GetCurrent(0)获取当前正在播放的动画状态(TrackEntry)。
  • 然后,我们可以通过TrackEntry的属性来获取有关当前动画的信息,例如动画名称、动画播放时间和动画是否已经播放完成。

10.动态替换骨骼纹理:

using Spine.Unity;
using UnityEngine;

public class SpineTextureReplacement : MonoBehaviour
{
    public SkeletonDataAsset skeletonDataAsset;
    public Texture2D newTexture;
    public string slotName;

    private SkeletonAnimation skeletonAnimation;

    void Start()
    {
        skeletonAnimation = GetComponent<SkeletonAnimation>();
        skeletonAnimation.skeletonDataAsset = skeletonDataAsset;
        skeletonAnimation.Initialize(true);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.T))
        {
            // 获取插槽的当前Attachment并替换纹理
            Slot slot = skeletonAnimation.Skeleton.FindSlot(slotName);
            if (slot != null)
            {
                Attachment currentAttachment = slot.Attachment;
                if (currentAttachment is RegionAttachment)
                {
                    RegionAttachment regionAttachment = (RegionAttachment)currentAttachment;
                    regionAttachment.SetRegion(newTexture);
                    skeletonAnimation.Update(0f); // 强制更新以显示新纹理
                }
            }
        }
    }
}

解释:

  • 在Update()方法中,我们使用Input.GetKeyDown(KeyCode.T)检测是否按下"T"键。
  • 当按下"T"键时,我们通过skeletonAnimation.Skeleton.FindSlot(slotName)找到指定名称的插槽(需要将"slotName"替换为实际的插槽名称)。
  • 然后,我们获取该插槽的当前Attachment,并进行类型检查,以确保该Attachment是RegionAttachment(纹理类型的Attachment)。
  • 如果是RegionAttachment,我们将其转换为RegionAttachment,并使用SetRegion方法将其纹理替换为newTexture。
  • 由于更改了Attachment,我们需要调用skeletonAnimation.Update(0f)来强制更新SkeletonAnimation,以便在场景中显示新的纹理。

11.播放Spine动画的指定轨道:

using Spine.Unity;

public class SpineAnimationController : MonoBehaviour
{
    public SkeletonDataAsset skeletonDataAsset;
    private SkeletonAnimation skeletonAnimation;
    public int trackIndex = 0;

    void Start()
    {
        skeletonAnimation = GetComponent<SkeletonAnimation>();
        skeletonAnimation.skeletonDataAsset = skeletonDataAsset;
        skeletonAnimation.Initialize(true);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Alpha1))
        {
            // 播放动画1在指定轨道
            skeletonAnimation.AnimationState.SetAnimation(trackIndex, "animation1", true);
        }
    }
}

解释:

  • 在Update()方法中,我们使用Input.GetKeyDown(KeyCode.Alpha1)来检测是否按下数字键1。
  • 当按下数字键1时,我们通过skeletonAnimation.AnimationState.SetAnimation()来播放名为"animation1"的动画,并指定了trackIndex作为第一个参数。trackIndex是轨道的索引,用于在多个轨道上播放不同的动画。默认情况下,使用0作为轨道索引。

12.获取当前动画的时间和持续时间:

using Spine.Unity;

public class SpineAnimationController : MonoBehaviour
{
    public SkeletonDataAsset skeletonDataAsset;
    private SkeletonAnimation skeletonAnimation;
    public int trackIndex = 0;

    void Start()
    {
        skeletonAnimation = GetComponent<SkeletonAnimation>();
        skeletonAnimation.skeletonDataAsset = skeletonDataAsset;
        skeletonAnimation.Initialize(true);
    }

    void Update()
    {
        TrackEntry currentAnimation = skeletonAnimation.AnimationState.GetCurrent(trackIndex);
        if (currentAnimation != null)
        {
            float currentTime = currentAnimation.Time;
            float animationDuration = currentAnimation.Animation.Duration;

            Debug.Log("Current Animation Time: " + currentTime);
            Debug.Log("Animation Duration: " + animationDuration);
        }
    }
}

解释:

  • 在Update()方法中,我们通过skeletonAnimation.AnimationState.GetCurrent(trackIndex)获取当前正在播放的动画状态(TrackEntry)。
  • 然后,我们使用currentAnimation.Time来获取当前动画的播放时间,以及currentAnimation.Animation.Duration来获取当前动画的总持续时间。

13.控制动画循环次数:

using Spine.Unity;

public class SpineAnimationController : MonoBehaviour
{
    public SkeletonDataAsset skeletonDataAsset;
    private SkeletonAnimation skeletonAnimation;

    void Start()
    {
        skeletonAnimation = GetComponent<SkeletonAnimation>();
        skeletonAnimation.skeletonDataAsset = skeletonDataAsset;
        skeletonAnimation.Initialize(true);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Alpha1))
        {
            // 播放动画1并设置循环次数
            TrackEntry trackEntry = skeletonAnimation.AnimationState.SetAnimation(0, "animation1", true);
            trackEntry.Loop = false; // 关闭循环
            trackEntry.LoopCount = 2; // 设置循环次数为2次
        }
    }
}

解释:

  • 在Update()方法中,我们使用Input.GetKeyDown(KeyCode.Alpha1)来检测是否按下数字键1。
  • 当按下数字键1时,我们通过skeletonAnimation.AnimationState.SetAnimation()来播放名为"animation1"的动画,并获取返回的TrackEntry。
  • 然后,我们可以通过设置TrackEntry的Loop属性为false来关闭动画的循环播放。
  • 同时,通过设置LoopCount属性为2来指定动画的循环次数为2次。

14.动态更改动画混合的权重:

using Spine.Unity;

public class SpineAnimationController : MonoBehaviour
{
    public SkeletonDataAsset skeletonDataAsset;
    private SkeletonAnimation skeletonAnimation;

    void Start()
    {
        skeletonAnimation = GetComponent<SkeletonAnimation>();
        skeletonAnimation.skeletonDataAsset = skeletonDataAsset;
        skeletonAnimation.Initialize(true);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Alpha1))
        {
            // 播放动画1并增加混合权重
            TrackEntry trackEntry = skeletonAnimation.AnimationState.SetAnimation(0, "animation1", true);
            trackEntry.Alpha = 0.5f; // 设置混合权重为0.5
        }
    }
}

解释:

  • 在Update()方法中,我们使用Input.GetKeyDown(KeyCode.Alpha1)来检测是否按下数字键1。
  • 当按下数字键1时,我们通过skeletonAnimation.AnimationState.SetAnimation()来播放名为"animation1"的动画,并获取返回的TrackEntry。
  • 然后,我们可以通过设置TrackEntry的Alpha属性来调整动画的混合权重。Alpha的取值范围是0到1,1表示完全显示动画,0表示完全隐藏动画。在这个例子中,我们将混合权重设置为0.5,使得动画以一半的透明度进行混合。

15.暂停和恢复所有动画轨道:

using Spine.Unity;

public class SpineAnimationController : MonoBehaviour
{
    public SkeletonDataAsset skeletonDataAsset;
    private SkeletonAnimation skeletonAnimation;

    void Start()
    {
        skeletonAnimation = GetComponent<SkeletonAnimation>();
        skeletonAnimation.skeletonDataAsset = skeletonDataAsset;
        skeletonAnimation.Initialize(true);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            // 切换暂停和恢复动画
            bool isPaused = skeletonAnimation.timeScale == 0f;
            skeletonAnimation.timeScale = isPaused ? 1f : 0f;
        }
    }
}

解释:

  • 在Update()方法中,我们使用Input.GetKeyDown(KeyCode.Space)来检测是否按下空格键。
  • 当按下空格键时,我们检查当前动画的时间缩放(timeScale)是否为0。如果为0,则表示当前动画处于暂停状态,我们将时间缩放设置为1以恢复动画播放。如果不为0,则表示当前动画正在播放中,我们将时间缩放设置为0以暂停动画。

16.动态创建并替换插槽的Attachment:

using Spine;
using Spine.Unity;
using UnityEngine;

public class SpineAttachmentReplacement : MonoBehaviour
{
    public SkeletonDataAsset skeletonDataAsset;
    private SkeletonAnimation skeletonAnimation;
    public string slotName;
    public Sprite newSprite;

    void Start()
    {
        skeletonAnimation = GetComponent<SkeletonAnimation>();
        skeletonAnimation.skeletonDataAsset = skeletonDataAsset;
        skeletonAnimation.Initialize(true);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.R))
        {
            // 获取插槽并替换Attachment为新的Sprite
            Slot slot = skeletonAnimation.Skeleton.FindSlot(slotName);
            if (slot != null)
            {
                Attachment currentAttachment = slot.Attachment;
                if (currentAttachment is RegionAttachment)
                {
                    RegionAttachment regionAttachment = (RegionAttachment)currentAttachment;
                    Material material = new Material(Shader.Find("Sprites/Default"));
                    Material newMaterial = new Material(material); // 复制原始Material
                    newMaterial.mainTexture = newSprite.texture; // 设置新的Sprite纹理
                    regionAttachment.GetRegion().RenderObject.SetMeshMaterial(newMaterial);
                    skeletonAnimation.Update(0f); // 强制更新以显示新Attachment
                }
            }
        }
    }
}
  • 在Update()方法中,我们使用Input.GetKeyDown(KeyCode.R)来检测是否按下'R'键。
  • 当按下'R'键时,我们通过skeletonAnimation.Skeleton.FindSlot(slotName)找到指定名称的插槽(需要将'slotName'替换为实际的插槽名称)。
  • 然后,我们获取插槽当前的Attachment,并进行类型检查,以确保该Attachment是RegionAttachment(纹理类型的Attachment)。
  • 如果是RegionAttachment,我们创建一个新的Material,并将新的Sprite纹理赋值给它。
  • 然后,我们使用regionAttachment.GetRegion().RenderObject.SetMeshMaterial(newMaterial)来将新的Material应用到Attachment,并强制调用skeletonAnimation.Update(0f)来更新SkeletonAnimation,以显示新的Attachment。

17.使用Spine动画事件触发Unity事件:

using Spine.Unity;
using UnityEngine;
using UnityEngine.Events;

public class SpineAnimationEventTrigger : MonoBehaviour
{
    public SkeletonDataAsset skeletonDataAsset;
    private SkeletonAnimation skeletonAnimation;

    public UnityEvent onAnimationEvent;

    void Start()
    {
        skeletonAnimation = GetComponent<SkeletonAnimation>();
        skeletonAnimation.skeletonDataAsset = skeletonDataAsset;
        skeletonAnimation.Initialize(true);

        // 订阅动画事件
        skeletonAnimation.AnimationState.Event += HandleAnimationEvent;
    }

    private void HandleAnimationEvent(TrackEntry trackEntry, Spine.Event e)
    {
        if (e.Data.Name == "eventName") // 将"eventName"替换为实际动画中设置的事件名称
        {
            onAnimationEvent.Invoke(); // 触发Unity事件
        }
    }
}

解释:

  • 在Update()方法中,我们使用skeletonAnimation.AnimationState.Event += HandleAnimationEvent;订阅了动画事件。
  • 在Start()方法中,我们创建了一个Unity事件(UnityEvent),名为onAnimationEvent。在Spine动画中设置的事件名称(例如"eventName")需要替换为实际动画中设置的事件名称。
  • 当Spine动画的事件被触发时,HandleAnimationEvent方法将会被调用。在这个方法中,我们检查事件的名称是否匹配设定的名称,如果匹配,则触发Unity事件onAnimationEvent.Invoke()。

18.控制动画播放速度随机化:

using Spine.Unity;
using UnityEngine;

public class SpineRandomAnimationSpeed : MonoBehaviour
{
    public SkeletonDataAsset skeletonDataAsset;
    private SkeletonAnimation skeletonAnimation;
    public float minSpeed = 0.8f;
    public float maxSpeed = 1.2f;

    void Start()
    {
        skeletonAnimation = GetComponent<SkeletonAnimation>();
        skeletonAnimation.skeletonDataAsset = skeletonDataAsset;
        skeletonAnimation.Initialize(true);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            // 播放动画,并随机设置播放速度
            float randomSpeed = Random.Range(minSpeed, maxSpeed);
            skeletonAnimation.timeScale = randomSpeed;
            skeletonAnimation.AnimationState.SetAnimation(0, "animation1", true);
        }
    }
}

解释:

  • 在Update()方法中,我们使用Input.GetKeyDown(KeyCode.Space)来检测是否按下空格键。
  • 当按下空格键时,我们随机生成一个播放速度(randomSpeed)在minSpeed和maxSpeed之间。
  • 然后,我们将randomSpeed设置为skeletonAnimation.timeScale,以调整动画的播放速度,并使用skeletonAnimation.AnimationState.SetAnimation()来播放名为"animation1"的动画。

19.动态切换Spine Atlas图集:

using Spine;
using Spine.Unity;
using UnityEngine;

public class SpineAtlasSwitch : MonoBehaviour
{
    public TextAsset newAtlasText;
    public Material newMaterial;
    public SkeletonDataAsset skeletonDataAsset;

    private SkeletonData currentSkeletonData;
    private Atlas currentAtlas;

    void Start()
    {
        LoadNewAtlas();
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.S))
        {
            LoadNewAtlas();
        }
    }

    private void LoadNewAtlas()
    {
        if (currentAtlas != null)
        {
            currentAtlas.Dispose();
        }

        currentAtlas = new Atlas(newAtlasText, "", newMaterial);
        currentSkeletonData = SkeletonData.CreateFromatlas(currentAtlas);

        skeletonDataAsset.Clear();
        skeletonDataAsset.Reset();
        skeletonDataAsset.atlasAssets[0].materials[0] = newMaterial;
        skeletonDataAsset.atlasAssets[0].materials = new Material[] { newMaterial };
        skeletonDataAsset.skeletonJSON = new TextAsset(currentSkeletonData.Json.ToString());
        skeletonDataAsset.GetSkeletonData(true);

        skeletonDataAsset.GetSkeletonData(false).AssetAtPath("path/to/asset");

        skeletonDataAsset.GetSkeletonData(false).FindSlot("slotName");

        skeletonDataAsset.GetSkeletonData(false).FindAnimation("animationName");

        skeletonDataAsset.GetSkeletonData(false).FindSkin("skinName");
    }
}

解释:

  • 这是一个更复杂的用例,用于动态切换Spine Atlas图集。我们通过Input.GetKeyDown(KeyCode.S)来检测是否按下"S"键,以触发图集的切换。
  • 在LoadNewAtlas()方法中,加载新的Atlas并创建新的SkeletonData。然后,更新SkeletonDataAsset以使用新的Atlas和SkeletonData。

20.动态创建骨骼动画:

using Spine;
using Spine.Unity;
using UnityEngine;

public class SpineDynamicAnimation : MonoBehaviour
{
    public SkeletonDataAsset skeletonDataAsset;
    private SkeletonAnimation skeletonAnimation;
    public string newAnimationName;
    public AnimationReferenceAsset newAnimationReference;

    void Start()
    {
        skeletonAnimation = GetComponent<SkeletonAnimation>();
        skeletonAnimation.skeletonDataAsset = skeletonDataAsset;
        skeletonAnimation.Initialize(true);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.N))
        {
            // 动态创建并播放新的骨骼动画
            Animation newAnimation = new Animation(newAnimationName, newAnimationReference.GetAnimation().Timelines);
            skeletonAnimation.Skeleton.Data.AddAnimation(newAnimationName, newAnimation, skeletonAnimation.Skeleton.Data.FindAnimation(skeletonAnimation.AnimationState.GetCurrent(0).Animation.Name).Duration, true);

            skeletonAnimation.AnimationState.SetAnimation(1, newAnimationName, false);
        }
    }
}

解释:

  • 在Update()方法中,我们使用Input.GetKeyDown(KeyCode.N)来检测是否按下"N"键。
  • 当按下"N"键时,我们动态创建一个新的骨骼动画(newAnimation)并将其添加到SkeletonData中。这里使用了newAnimationName和newAnimationReference作为新动画的名称和参考动画。
  • 然后,我们使用skeletonAnimation.AnimationState.SetAnimation()来播放新创建的动画。

21.骨骼动画的事件监听与处理:

using Spine;
using Spine.Unity;
using UnityEngine;

public class SpineAnimationEvent : MonoBehaviour
{
    public SkeletonDataAsset skeletonDataAsset;
    private SkeletonAnimation skeletonAnimation;

    void Start()
    {
        skeletonAnimation = GetComponent<SkeletonAnimation>();
        skeletonAnimation.skeletonDataAsset = skeletonDataAsset;
        skeletonAnimation.Initialize(true);

        // 订阅动画事件
        skeletonAnimation.AnimationState.Event += HandleAnimationEvent;
    }

    private void HandleAnimationEvent(TrackEntry trackEntry, Spine.Event e)
    {
        // 根据动画事件名称做相应处理
        if (e.Data.Name == "event_name_1")
        {
            Debug.Log("Event 1 triggered!");
            // 在这里添加处理事件1的逻辑
        }
        else if (e.Data.Name == "event_name_2")
        {
            Debug.Log("Event 2 triggered!");
            // 在这里添加处理事件2的逻辑
        }
    }
}

解释:

  • 在Update()方法中,我们使用skeletonAnimation.AnimationState.Event += HandleAnimationEvent;订阅了动画事件。
  • 在HandleAnimationEvent方法中,我们根据动画事件的名称(例如"event_name_1"和"event_name_2")来执行不同的处理逻辑。您可以根据动画中设置的事件名称,添加相应的逻辑来响应这些事件。

⚠️注意:这里的用法可能会因Spine版本和Unity项目结构而有所变化。请根据您的具体情况进行调整。

8.unity-spine运行库下载

官网

本地下载

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/772805.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Rsync(二十七)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一、概述 二、特性 三、应用场景 四、数据的同步方式 五、rsync传输模式 六、rsync应用 七、rsync 命令 1. 格式 1.1 作为远程命令 1. 2 作为rsync服务 2. 选项 3.…

​通达信量比捉牛股指标源码​_通达信公式

JL5:MA(V,5); LB:DYNAINFO(17); 历史量比:IF(DYNAINFO(4)OANDDYNAINFO(5)HANDDYNAINFO(6)LANDDYNAINFO(7) C,DYNAINFO(17),V/JL5),LINETHICK,COLORYELLOW; STICKLINE(历史量比>1,历史量比,0,3,1)COLORRED; STICKLINE(历史量比<1,历史量比,0,3,0)COLO…

Debug调试的使用(IDEA 基础篇 喂奶级教程)

引言 在编程中&#xff0c;Debug&#xff08;调试&#xff09;是指在程序运行时&#xff0c;通过一系列的工具和技术&#xff0c;对程序进行逐行调试和分析&#xff0c;从而发现和修复程序中的错误和问题。Debug 功能是程序开发中非常重要的一个环节&#xff0c;它可以帮助开发…

JSON对象的stringify()和parse()方法使用

JSON对象的stringify和parse方法使用 JSON 格式JSON 对象JSON.stringify()1.JSON.stringify(value)2.JSON.stringify(value,replace)3.JSON.stringify(value,replace,space)4.注意的点 JSON.parse() JSON 格式 JSON 格式&#xff08;JavaScript Object Notation 的缩写&#xf…

一文读懂【TypeScript】的发展设计理念

导语&#xff1a; 在了解 TypeScript 之前&#xff0c;我们需要了解 什么是强类型语言和什么是弱类型语言&#xff0c;以及什么是静态类型&#xff0c;什么又是动态类型。 强类型不允许任意的隐式类型转换&#xff0c;而 弱类型 允许静态类型&#xff1a;一个变量声明时它的类型…

STM32使用高级定时器输出互补pwm波

STM32使用高级定时器输出互补pwm波 前言硬件和软件cubemx新建工程打开Debug模式配置时钟源六大时钟的作用选择Crystal/Ceramic Resonator&#xff0c;即使用外部晶振作为HSE的时钟源。 配置时钟配置高级定时器TIM8和通用定时器TIM3这里大概解释一下配置pwm输出用到的几个参数我…

ASEMI快恢复二极管MUR20100CTR在电子工程中的应用

编辑-Z 随着电子技术的日益发展&#xff0c;各种电子元件的使用场景与需求也在逐步扩大。今天&#xff0c;我们将聚焦于一款广泛应用于各类电路的二极管——MUR20100CTR&#xff0c;来详细解读其性能特征及应用。 一、MUR20100CTR二极管的主要特性 MUR20100CTR是一款极高性能的…

03 QT对象树

Tips: QT通过对象树机制&#xff0c;能够自动、有效的组织和管理继承自QObject的Qt对象&#xff0c;不需要用户手动回收资源&#xff0c;系统自动调用析构函数。 验证对象树功能&#xff1a; 新建C文件 继承自QPushButton&#xff0c;但没有QPushButton&#xff0c;但有其父类…

简单高效的交易系统,只需这种行情分析工具

行情分析的意义在于&#xff0c;首先它给我们不得不做出的方向性选择提供一些技术的支持。其次&#xff0c;它可以给我们提出一个入市点&#xff0c;一个理想的盈利点&#xff0c;一个认输的失败点。无论你用什么办法进行行情预测或指导&#xff0c;只要你的分析结果中有这样三…

Vue中的侦听器:数据变化的秘密揭示

一、侦听器&#xff1a;vue中想监听数据的变化 &#x1f680;&#xff08;一&#xff09;侦听器watch 如何侦听到某个变量值改变呢&#xff1f;使用watch配置项&#x1f6a7;&#x1f6a7;&#x1f6a7;watch&#xff1a;可以侦听到data/computed属性值的改变。语法&#xff…

C++入门学习(2)

思维导图&#xff1a; 一&#xff0c;缺省参数 如何理解缺省参数呢&#xff1f;简单来说&#xff0c;缺省参数就是一个会找备胎的参数&#xff01;为什么这样子说呢&#xff1f;来看一个缺省参数就知道了&#xff01;代码如下&#xff1a; #include<iostream> using std…

商城-学习整理-基础-环境搭建(二)

目录 一、环境搭建1、安装linux虚拟机1&#xff09;下载&安装 VirtualBox https://www.virtualbox.org/&#xff0c;要开启 CPU 虚拟化2&#xff09;虚拟机的网络设置3&#xff09;虚拟机允许使用账号密码登录4&#xff09;VirtualBox冲突5&#xff09;修改 linux 的 yum 源…

JDK JRE JVM

JDK JRE JVM JDKJREJVM三者之间的联系三者之间的区别 JDK JDK是用于开发、编译、调试和运行Java应用程序的软件包&#xff0c;包含了Java编程语言的开发工具和Java运行时环境。JDK包括Java编译器&#xff08;javac&#xff09;、Java虚拟机&#xff08;JVM&#xff09;和Java类…

AtcoderABC246场

#A - Four PointsA - Four Points 题目大意 在平面上有一个矩形&#xff0c;矩形的每条边都平行于x轴或y轴&#xff0c;并且它的面积不为零。 给定这个矩形三个顶点的坐标 (x1, y1), (x2, y2), (x3, y3)&#xff0c;找到另外一个顶点的坐标。 思路分析 根据矩形的性质可以通…

为什么低代码只能掀起小浪花?了解低代码的得失与前景

导语&#xff1a;低代码是相对于高代码和无代码的一个中间概念&#xff0c;通常强调的是用户不需要学习如何写代码&#xff0c;就能完成工作。然而低代码模式一直不温不火&#xff0c;原因是什么呢&#xff1f;一起来看一下吧。 最近互联网大公司裁员消息又起&#xff0c;“低代…

裁剪内核和新内核available差异大原因分析

背景 host内核裁剪时会进行收益的比较&#xff0c;比如裁剪前用5.10最新内核得出内存数据&#xff0c;然后和裁剪后的内存数据进行对比。 在进行对比中&#xff0c;发现裁剪后的内存available比裁剪前多了10个G&#xff0c;有点不正常了&#xff0c;需要分析下这10个G到底是怎…

基于VORS、CCDM模型、GeoDetector、GWR模型集成技术在城镇化与生态系统健康空间关系分析及影响效应

详情点击链接&#xff1a;基于VORS、CCDM模型、GeoDetector、GWR模型集成技术在城镇化与生态系统健康空间关系分析及影响效应 第一&#xff1a;生态系统健康理论基础及研究热点分析 1.生态系统健康概念及内涵 2.生态系统健康评价方法与指标体系 3.城镇化与生态系统健康 4.…

Git使用规范Git常用命令

Git使用规范&&Git常用命令 分支规范 master分支 master的分支HEAD和历史commit均是是稳定、可发布的状态。master分支的每个commit都需要打tag&#xff0c;如v1.0、v1.1、v1.2、v2.0等。仅能从test分支和hotfix分支合并过来。 hotfix的合并必须是通过代码审核和测试…

AI数字人:图像超分辨率模型 Real-ESRGAN

1 Real-ESRGAN介绍 1.1 Real-ESRGAN是什么&#xff1f; Real-ESRGAN全名为Enhanced Super-Resolution GAN&#xff1a;增强的超分辨率的对抗生成网络&#xff0c;是由腾讯ARC实验室发布的一个盲图像超分辨率模型&#xff0c;它的目标是开发出实用的图像/视频修复算法&#xf…

oled拼接屏在柳州的户外广告中有哪些应用展现?

柳州oled拼接屏是一种高端的显示屏&#xff0c;它采用了OLED技术&#xff0c;具有高亮度、高对比度、高色彩饱和度、高刷新率等优点&#xff0c;能够呈现出更加真实、清晰、细腻的图像效果。 同时&#xff0c;柳州oled拼接屏还具有拼接功能&#xff0c;可以将多个屏幕拼接在一…