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(); 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(); 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
需要动态加载这些材质球的话,需要将这些材质球与其他材质球资源一起打包,否则在手机包会出现材质球丢失。
手机包贴图丢失问题
当然,除了材质丢失,还有可能出现贴图丢失的问题,情况如下:
只有几个白色的方块,而正常的应该如下:
问题分析
现在这就是贴图丢失了,看了 《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(); 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(); 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; /// /// 用于解决 Spine 动画在手机 ab 包资源管理模式下贴图丢失问题 /// 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(); 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 的地方挂上此组件即可解决移动端材质丢失的问题。
参考
Unity 使用 Spine 动画 https://blog.csdn.net/linshuhe1/article/details/79792432?utm_source=blogxgwz8 Spine Unity 官方文档 http://zh.esotericsoftware.com/spine-unity#Updating-Your-Projects-SpineUnity-Runtime
将Spine动画导入unity https://www.jianshu.com/p/5254f2059748
spine导出二进制文件怎么导入unity https://blog.csdn.net/crazyapp/article/details/72545557
unity 在代码中创建spine动画组件 http://blog.51cto.com/chenshuhb/1836481
关于unity 中使用AssetBundle加载资源,shader偶尔会丢失的问题解决办法 http://blog.51cto.com/chenshuhb/1836458