Creator v2.0.1,从缓存中加载 Spine 动画后调用直接调用 setSkin ,动画消失。

同样的代码,步骤是:
[1] 用 cc.loader.loadRes 加载 Spine 动画,
[2] 在 completeCallback 中:

let spineScript: sp.Skeleton = stNode.getComponent(sp.Skeleton);
if(!spineScript)
spineScript = stNode.addComponent(sp.Skeleton);

spineScript.skeletonData = res; // res 就是加载到的 Spine 动画资源。
spineScript.setSkin(sSkinName);

第一次加载资源时,是正常的。同时,已加载的资源我们做了缓存。
所以第二次加载的时候就直接从缓存读取资源了,然后紧接着再执行步骤[2]。
但这时再调用 setSkin 动画会消失,不能起到整体换肤的效果。

setSkin: function(skinName) {
    if (this._skeleton) return this._skeleton.setSkinByName(skinName);
    return null;
}
断点进去是 setSkin 的时候 this._skeleton 为空。

目前我们的解决办法是,
将 setSkin 的操作 scheduleOnce,放到下一帧执行,就可以了…

怀疑第二次从缓存读取时,
spineScript.skeletonData = res;
并没有把所有该做的事情在当前帧做完,
所以 setSkin 的时候 this._skeleton 才会是空的,
而下一帧的时候,该做的事情都做完了,this._skeleton 就不再是空了。

至于第一次为什么是正常的,可能是因为有 cc.loader.loadRes 参与的原因吧…

后来发现,
如果一个 node 上有 sp.Skeleton 组件,
newNode = cc.instantiate(node) 之后马上调用 newNode 上 sp.Skeleton 组件的 findAnimation 和 setSkin 接口,
也可能会出现引擎源码 this._skeleton 为 null 的问题,因为 cc.instantiate 貌似会涉及 sp.Skeleton 组件的初始化。
所以推荐预先初始化好所有 node(也可能是个 node 数组),然后在向 node 上挂载加载好的 sp.Skeleton 组件。

基于这个发现,前面说的 scheduleOnce 的“解决办法”,其实不是“对症”的“药”。
所以 scheduleOnce 的方法也就可以不用了。

但这样,我又遇到一个问题,
后面从缓存中读取 Spine 资源时,是变成了同步加载的。
这时,初始化 nodeB,挂载同步加载到的 sp.Skeleton 组件,然后 setSkin,
之后再 nodeA.addchild(nodeB) 发现 没有成功?!
断了下点,发现 setSkin 时引擎源码的 this._skeleton 是不为 null 的,
但 setSkin 之后 this._skeleton 又被重新赋值了,就是因为这个 addchild ……

所以,即使更改了写法,解决了我前面的问题之后,我又把 scheduleOnce 给加了回来…
才可以了…:innocent:

不清除重新赋值是否是必要的操作…
但如果这样,写代码时,就要清楚的安排 setSkin 和 addchild 的顺序了…
感觉很容易写出隐藏bug啊…

2赞

我这边setSkin发现没任何效果,需要调用一下setToSetUp才有效

这问题真的太烦了…
用缓存的 Spine 动画 addComponent(sp.Skeleton) 后却不能立刻 setSkin、setAnimation…
那我缓存资源、将异步代码变成同步代码还有什么意义…

v2.1.0 问题依旧。

确实,setSkin后调用setSlotsToSetupPose才可以,但setSlotsToSetupPose也是遍历Slots去重置,不知道有没有更优化的办法?

creator 2.3.4 版本 setSkin 还是要在 addChild 之后才生效,刚刚实例化之后调用的话,也是 this._skeleton 为空

兄弟,2.4.0了,依旧如此,我是在setSkeletonData操作后,同一帧执行的setAnimation,根本不生效,各种找原因,发现在下一帧调用才会里生效,来网上一搜,原来兄弟你比我早两年遇到这种问题啊