Unity---Spine动画

目录

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.1animationSpeed -= 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为新的SpriteSlot 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); // 复制原始MaterialnewMaterial.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.hqwc.cn/news/29546.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ vector容器注意事项

容量&#xff08;capacity&#xff09;和大小&#xff08;size&#xff09;的区别 vector 容器的容量&#xff08;用 capacity 表示&#xff09;&#xff0c;指的是在不分配更多内存的情况下&#xff0c;容器可以保存的最多元素个数&#xff1b;而 vector 容器的大小&#xff…

智能ai绘画软件帮你用科技点亮创意火花

李明&#xff1a;嘿&#xff0c;你听说过ai绘画软件吗&#xff1f;我最近对数字艺术产生了浓厚的兴趣 王磊&#xff1a;当然&#xff01;ai绘画软件真是太神奇了&#xff01;它可以将抽象的文字描述转换成惊人的艺术作品。 李明&#xff1a;是吗&#xff1f;它们绘制的效果怎…

Kubernetes_1.27.3_Harbor结合Nacos实战

Nacos 实战 作者:行癫(盗版必究) 一:Nacos简介 1.简介 ​ Nacos是 Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台;Nacos 致力于帮助您发现、配置和管理微服务;Nacos 提供了一组简单易用的特…

RabbitMQ安装

这里写目录标题 简介下载ELANG安装ELang配置环境变量安装RabbitMQ 简介 RabbitMQ 是一个开源的遵循 AMQP 协议实现的基于 Erlang语言编写&#xff0c;**即需要先安装部署Erlang环境再安装RabbitMQ环境。**需加注意的是&#xff0c;读者若不想跟着我的版本号下载安装&#xff0…

【UE4 塔防游戏系列】07-子弹对敌人造成伤害

目录 效果 步骤 一、让子弹拥有不同伤害 二、敌人拥有不同血量 三、修改“BP_TowerBase”逻辑 四、发射的子弹对敌人造成伤害 效果 步骤 一、让子弹拥有不同伤害 为了让每一种子弹拥有不同的伤害值&#xff0c;打开“TotalBulletsCategory”&#xff08;所有子弹的父类…

《Communication-Efficient Learning of Deep Networks from Decentralized Data》

Communication-Efficient Learning of Deep Networks from Decentralized Data 这篇文章算是联邦学习的开山之作吧&#xff0c;提出了FedAvg的算法&#xff0c;文中对比了不同客户端本地训练次数&#xff0c;客户端训练数据集划分的影响。 0. Abstract 现代移动设备可以获取大…

opencv -13 掩模

什么是掩膜&#xff1f; 在OpenCV中&#xff0c;掩模&#xff08;mask&#xff09;是一个与图像具有相同大小的二进制图像&#xff0c;用于指定哪些像素需要进行操作或被考虑。掩模通常用于选择特定区域或进行像素级别的过滤操作。 OpenCV 中的很多函数都会指定一个掩模&…

matlab入门

命名规则&#xff1a; clc&#xff1a;清除命令行的所有命令 clear all&#xff1a;清除所有工作区的内容 注释&#xff1a;两个% 空格 %% matlab的数据类型 1、数字 3 3 * 5 3 / 5 3 5 3 - 52、字符与字符串 s a %% 求s的ascill码 abs(s) char(97) num2str(65) str I…

Simulink仿真模块 - Data Store Read

Data Store Read:从数据存储中读取数据 在仿真库中的位置为:Simulink / Signal Routing 模型为: 说明 Data Store Read 模块将指定数据存储中的数据复制到其输出中。多个 Data Store Read 模块可从同一个数据存储读取数据。 用来读取数据的源数据存储由 Data Store Memory 模…

上门服务小程序|上门家政小程序开发

随着现代生活节奏的加快和人们对便利性的追求&#xff0c;上门家政服务逐渐成为了许多家庭的首选。然而&#xff0c;传统的家政服务存在着信息不透明、服务质量不稳定等问题&#xff0c;给用户带来了困扰。为了解决这些问题&#xff0c;上门家政小程序应运而生。上门家政小程序…

自动收小麦机(牛客2023萌新)

题目链接 示例1 输入 复制 4 1 2 1 1 4 5 2 2 2 3 4 输出 复制 10 说明 在第4格放出水流后&#xff0c;水流会流向第3格&#xff0c;由于第3格高度比第4格低&#xff0c;所以水流继续向左流向第2格&#xff0c;因为平地水流只能流2格&#xff0c;所以到达第2格后水流停…

sqli-labs 堆叠注入 解析

打开网页首先判断闭合类型 说明为双引号闭合 我们可以使用单引号将其报错 先尝试判断回显位 可以看见输出回显位为2&#xff0c;3 尝试暴库爆表 这时候进行尝试堆叠注入&#xff0c;创造一张新表 ?id-1 union select 1,database(),group_concat(table_name) from informatio…