Unity 使用 Spine 动画
在 Unity 中,常常使用 Spine 来制作一些动画,引擎本身并不能直接播放 Spine 动画,需要额外导入一个 RunTime 插件库才能支持。运行库导入关于运行库的导入,有两种方式:1. 从开源库导入下载 Spine 的 runtime 库:EsotericSoftware/spine-runtimes ,只需要将其中的 spine-csharp 和 spine-un...
在 Unity 中,常常使用 Spine 来制作一些动画,引擎本身并不能直接播放 Spine 动画,需要额外导入一个 RunTime 插件库才能支持。
运行库导入
关于运行库的导入,有两种方式:
1. 从开源库导入
下载 Spine 的 runtime 库:EsotericSoftware/spine-runtimes ,只需要将其中的 spine-csharp
和 spine-unity
两个部分导入到 Unity 中即可。
2. 从官网插件导入
当然,也可以到 Spine 官网关于 Unity 插件的下载地址spine-unity-download 直接下载最新的插件包,例如:Spine-Unity 3.6 runtime unitypackage ,然后直接导入工程中。
第二种方式是比较常用而且方便的,因为在导入插件核心库的同时,还是导入一些 demo 例子,帮助我们快速上手使用插件的 API ,因此这里我也选择使用第二种方式导入插件。建议新建一个空工程来导入相关 demo 了解插件的使用,而正式工程只导入 spine-csharp
和 spine-unity
两个部分即可。
插件导入步骤
-
在 Unity 菜单中依次点击:
Assets
->Improt Package
->Custom Package
; -
选中
Spine-Unity 3.6 runtime unitypackage
并导入,去掉Spine Examples
的勾选(假如需要查看 demo 则点击 All ); -
点击
Import
完成导入,导入完成后在工程Assets
下会多出一个Spine
目录,表示导入成功。
Spine 资源导入步骤
通常制作好的 Spine 动画导出时会有三个文件: .png
、.json
和 .atlas
:
-
在导入 Unity 之前需要将
.atlas
后缀的文件改为.atlas.txt
后缀(不修改可能会有问题); -
将三个文件拖到 Unity 的
Project
面板中,假如运行库导入正常,此时会生成三个新的文件:_Atlas
、_Material
和_SkeletinData
,并且在 Console 面板中会打印导入成功的日志:三个文件的作用:
-
_Material资源包含一个
着色器
引用和.png纹理。 -
_Atlas资源包含一个
材质
引用和.atlas.txt 。 -
_SkeletonData资源包含一个json引用和_Atlas资源。
-
在 UGUI 中使用 Spine
在 Hierarchy
面板中,右键 Spine
->SkeletonGraphic(UnityUI)
:
然后将 Spine 资源导入时生成的 _SkeletinData
文件拖到动画 UI 对象的 Skeleton Data Asset
属性中,且可以编辑相应的动画控制属性:
勾选 Starting Loop
然后运行场景即可看到当前 Starting Animation
所选中动画的效果,并能动态切换其他动画。
程序控制
很多时候,动画需要通过程序控制实现动态创建、切换和销毁,主要会用到几个组件:SkeletonAnimation
、SkeletonGraphic
和 SkeletonRenderer
:
-
SkeletonAnimation
动画基本控制组件:skeletonAnimation.timeScale = 1.5f; skeletonAnimation.loop = true; skeletonAnimation.AnimationName = "run";
这里用于设置动画的播放速度,是否循环,还有切换动画,如下脚本,将其挂在带
SkeletonAnimation
或继承自SkeletonAnimation
的组件的物体上,用于播放一次动画:using UnityEngine; public class TestSpineAnimCtl : MonoBehaviour { public SkeletonAnimation sa; // Use this for initialization void Start () { sa = sa ?? gameObject.GetComponent<SkeletonAnimation>(); PlayAnim("run2"); } public void PlayAnim(string animName) { sa.state.SetAnimation(0, animName, false); } }
通过获取其
state
属性,其实是Spine.AnimationState
对象。 -
SkeletonGraphic
用于 Unity 的 UI 中:using UnityEngine; public class TestSpineAnimCtl : MonoBehaviour { public SkeletonGraphic sgp; // Use this for initialization void Start () { sgp = sgp ?? gameObject.GetComponent<SkeletonGraphic>(); PlayAnim("run2"); } public void PlayAnim(string animName) { sgp.AnimationState.SetAnimation(0, animName, false); } }
通过其
AnimationState
组件来控制动画,其实也是获取一个Spine.AnimationState
对象。 -
SkeletonRenderer
还没用到,暂不记录。 -
Spine.AnimationState
主要需要了解其事件和回调:Spine事件 & AnimationState回调
效率优化
上面提到从 Spine 导出的文件有三种:.png
、.json
和 .atlas
,但使用 .json
格式读取动画数据是比较慢且运行效率较低的方式。Spine 新版本其实还支持更快的数据导出方式,即 Binary format
,那就是二进制的数据导出,采用这种方式导出的格式是:.png
、.skel
和 .atlas
。
导入前需要将 .skel
后缀改成 .skel.bytes
,将 .atlas
后缀改成 .atlas.txt
,然后再拖入 Unity 中,不然 .skel
文件不会对应生成 Unity 可识别的 .asset
格式的数据文件。
插件自带材质
由于打包的时候资源需要打包成 Assetbundle ,有几个插件自带的材质球需要注意一下的:
-
spine-unity\Modules\SkeletonGraphic\Shaders
中的SkeletonGraphicDefault.mat
和SkeletonGraphicTintBlack.mat
; -
spine-unity\Shaders\Utility
中也有HiddenPass.mat
; -
spine-unity\Modules\SlotBlendModes
中有SkeletonPMAMultiply.mat
和SkeletonPMAScreen.mat
需要动态加载这些材质球的话,需要将这些材质球与其他材质球资源一起打包,否则在手机包会出现材质球丢失。
手机包贴图丢失问题
当然,除了材质丢失,还有可能出现贴图丢失的问题,情况如下:
只有几个白色的方块,而正常的应该如下:
1. 问题分析
现在这就是贴图丢失了,看了 《unity 在代码中创建spine动画组件》 才发现项目中用到了spine 动画,使用 Assetbundle 打包后,在手机上运行会出现丢材质的情况。如果不进行打包,直接放到 Resources 目录下是可以正常加载的,但是,这样包就会很大,而且也不能进行热更新。进过测试,发现在代码中创建 spine 组件是可以解决这个问题。
其实,最根本的问题是 :xxx_Altas.asset
和 xxx_SkeletonData.asset
这两个资源打包到 Assetbundle 之后就取不出来了(偶尔能取出),从而导致了 SkeletonGraphic
的 skeletonDataAsset
属性值变为 null 。
所以,要解决此问题可以干脆用代码动态创建两个对象替换这两个文件的作用即可:
// 动态创建一个 spine 动画数据
public static SkeletonDataAsset BuildSkeletonDataAsset(string skeletonPath, GameObject go) {
SkeletonDataAsset sda;
sda = ScriptableObject.CreateInstance<SkeletonDataAsset>();
sda.fromAnimation = new string[0];
sda.toAnimation = new string[0];
sda.duration = new float[0];
sda.scale = 0.01f;
sda.defaultMix = 0.2f;
AtlasAsset[] arrAtlasData = new AtlasAsset[1];
for (int i = 0; i < arrAtlasData.Length; i++) {
AtlasAsset atlasdata = ScriptableObject.CreateInstance<AtlasAsset>();
atlasdata.atlasFile = ResourceManager.Instance.LoadAsset(skeletonPath + ".atlas.txt", typeof(TextAsset), go) as TextAsset;
atlasdata.materials = new Material[1];
atlasdata.materials[0] = ResourceManager.Instance.LoadAsset(skeletonPath + "_Material.mat", typeof(Material), go) as Material;
arrAtlasData[i] = atlasdata;
}
sda.atlasAssets = arrAtlasData;
sda.skeletonJSON = ResourceManager.Instance.LoadAsset(skeletonPath + ".skel.bytes", typeof(TextAsset), go) as TextAsset;
return sda;
}
步骤其实很简单,用代码动态创建一个 SkeletonDataAsset
对象,然后赋值给 SkeletonGraphic
中的 skeletonDataAsset
再调整相应的动画参数即可:
using Spine.Unity;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 用于解决 Spine 动画在手机 ab 包资源管理模式下贴图丢失问题
/// </summary>
public class SkeletonGraphicABReseter : MonoBehaviour {
private SkeletonGraphic skg;
public string spinePath = "";
public string animName = "";
public bool loop = false;
public float timeScale = 1.0f;
// Use this for initialization
void Start () {
#if UNITY_EDITOR
#else
skg = gameObject.GetComponent<SkeletonGraphic>();
if(skg == null) {
return;
}
ResetSkData();
#endif
}
void ResetSkData() {
if(skg.SkeletonDataAsset == null && spinePath.Length > 0) {
Debugger.Log("------------ 重置动画");
SkeletonDataAsset sda = Utils.BuildSkeletonDataAsset(spinePath, gameObject);
skg.skeletonDataAsset = sda;
// 重置动画
skg.Initialize(true);
skg.AnimationState.SetAnimation(0, animName, loop);
skg.AnimationState.TimeScale = timeScale;
}
}
}
在使用 SkeletonGraphic
的地方挂上此组件即可解决移动端材质丢失的问题。
参考
更多推荐
所有评论(0)