目录
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.优点
-
轻量级和高效:Spine动画使用基于骨骼的动画技术,相比传统的逐帧动画,它们具有更小的文件大小和更低的内存占用。这使得Spine动画在移动设备和低端硬件上的性能更好。
-
灵活性:使用Spine,你可以创建高度可定制的动画,包括骨骼的变形、缩放、旋转和平移。你可以轻松地调整动画的速度、混合不同的动画,以及实现复杂的角色动画控制。
-
运行时动画:Spine动画可以在运行时进行实时修改和控制。这意味着你可以通过代码来改变动画的播放状态,根据角色的行为和环境条件进行相应的动画交互。
-
平台兼容性: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:
-
角色动画:如果你的游戏中有2D角色需要进行复杂的骨骼动画,你可以使用Spine创建角色的动画,并将SkeletonRenderer组件添加到Unity场景中的角色对象上。然后,该组件将负责在运行时渲染角色的骨骼动画。
-
UI动画:你可以使用Spine创建2D UI元素的动画,例如按钮、图标、菜单等。通过将SkeletonRenderer添加到UI元素上,你可以在运行时播放Spine动画来增强用户界面的交互和视觉效果。
-
敌人/怪物动画:如果你的游戏中有敌人、怪物或NPC需要复杂的动画效果,你可以使用Spine创建骨骼动画,并将SkeletonRenderer组件添加到相应的游戏对象上。
-
特效动画: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运行库下载
官网
本地下载