更新 spine 的 cocos2d-x runtimes (二)

  1. 1. 1. 替换最新的 spine runtimes
  2. 2. 2. 修复 lua binding 错误
  3. 3. 3. 修复 API 变动导致的编译错误
    1. 3.1. 1. *Listener 回调函数参数变化
    2. 3.2. spEventType 变化
    3. 3.3. TrianglesCommand 参数错误
  • 1. Spine Error:Unknown attachment type: skinnedmesh
  • 2. 真的有必要从 cocos2d-x-lite 中更新吗?
  • 前段时间一个朋友向我咨询播放 spine 动画时遇到的问题:

    这个问题我们在很久之前也遇到过, 当时是另个一小伙伴负责的. 当时猜测可能是 spine runtimes 需要升级, 但是升级时遇到了技术问题, 恰好项目比较紧, 便让美术同学重新用老版的 Spine 编辑器重新做了一遍.

    无独有偶, 上周另一个小伙伴在播放 spine 动画时遇到了 崩溃 的问题, 而放到 creator 中测试是没有问题的, 看来又是运行库太过陈旧的原因.

    趁着周末有点时间, 我也来尝试更新下 spine runtimes. 15 年的时候就更新过一次, 当时还写了一篇很水的文章 <<更新 spine 的 cocos2d-x runtimes>>, 时隔两年, 这次是否也会依然顺利呢 ?

    我这次并没有选择从 spine 的官方仓库 更新 runtimes, 既然 creator 中是没有问题的, 为什么不选择直接从 creator 附带的 cocos2d-lite 中复制过来呢 ?

    1. 替换最新的 spine runtimes

    简单浏览了下, 目录结构没有发生变化, 看来是一个好的开端. 我们先从用 cocos2d-lite 中的 cocos/editor-support/spine 替换掉 quick-cocos2d-x 中的同名目录.

    用 XCode 打开 quick/player/proj.mac/player3.xcodeproj 模拟器工程, 我们用 quick-cocos2d-x 的 Mac 版来完成后续的工作. 在 Project Navigator 找到 spine 文件夹, 删除掉红色的缺失文件, 右键 Add files to 'cocos2d_libs.xcodepro' 添加上新增的文件, 编译一下.

    2. 修复 lua binding 错误

    发现有 lua_cocos2dx_spine_auto.cpp 有报错, 错误内容:

    1
    2
    3
    4
    5
    6
    7
    8
    ...lua_cocos2dx_spine_auto.cpp:701:32: No viable conversion from 'function<void (int)>' to 'const function<void (spTrackEntry *)>'
    ...lua_cocos2dx_spine_auto.cpp:758:43: No viable conversion from 'function<void (int, spEvent *)>' to 'const function<void (spTrackEntry *, spEvent *)>'
    ...lua_cocos2dx_spine_auto.cpp:815:46: No viable conversion from 'function<void (int, int)>' to 'const function<void (spTrackEntry *)>'
    ...lua_cocos2dx_spine_auto.cpp:872:43: No viable conversion from 'function<void (int)>' to 'const function<void (spTrackEntry *)>'
    ...lua_cocos2dx_spine_auto.cpp:925:35: No viable conversion from 'function<void (int, int)>' to 'const function<void (spTrackEntry *)>'
    ...lua_cocos2dx_spine_auto.cpp:982:41: No viable conversion from 'function<void (int)>' to 'const function<void (spTrackEntry *)>'
    ...lua_cocos2dx_spine_auto.cpp:1035:32: No viable conversion from 'function<void (int, spEvent *)>' to 'const function<void (spTrackEntry *, spEvent *)>'
    ...lua_cocos2dx_spine_auto.cpp:1143:30: No viable conversion from 'function<void (int)>' to 'const function<void (spTrackEntry *)>'

    看到这个文件名就应该联想到没有重新生成 lua 绑定的代码. 打开终端进入 tools/tolua, 运行:

    1
    python genbindings.py

    如果之前没有运行过这个脚本的话, 可以参照这份官方文档需要搭建环境. 等待运行结束后, 再次编译.

    3. 修复 API 变动导致的编译错误

    1. *Listener 回调函数参数变化

    这次改为 lua_cocos2dx_spine_manual.cpp 出错了:

    1
    2
    3
    4
    ...lua_cocos2dx_spine_manual.cpp:233:48: No viable conversion from '(lambda at ...lua_cocos2dx_spine_manual.cpp:233:48)' to 'const StartListener' (aka 'const function<void (spTrackEntry *)>')
    ...lua_cocos2dx_spine_manual.cpp:241:46: No viable conversion from '(lambda at ...lua_cocos2dx_spine_manual.cpp:241:46)' to 'const EndListener' (aka 'const function<void (spTrackEntry *)>')
    ...lua_cocos2dx_spine_manual.cpp:249:51: No viable conversion from '(lambda at ...lua_cocos2dx_spine_manual.cpp:249:51)' to 'const CompleteListener' (aka 'const function<void (spTrackEntry *)>')
    ...lua_cocos2dx_spine_manual.cpp:257:48: No viable conversion from '(lambda at ...lua_cocos2dx_spine_manual.cpp:257:48)' to 'const EventListener' (aka 'const function<void (spTrackEntry *, spEvent *)>')

    这个文件中放的是手动导出 lua 接口的代码, 我们需要推测一下这些错误发生的原因. 让我们看下出错的具体代码:

    1
    2
    3
    self->setStartListener([=](int trackIndex){
    executeSpineEvent(self, handler, eventType, trackIndex);
    });

    StartListener 是声明在 SkeletonAnimation.h 中的, 相关代码的 git diff 如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    -typedef std::function<void(int trackIndex)> StartListener;
    -typedef std::function<void(int trackIndex)> EndListener;
    -typedef std::function<void(int trackIndex, int loopCount)> CompleteListener;
    -typedef std::function<void(int trackIndex, spEvent* event)> EventListener;
    +typedef std::function<void(spTrackEntry* entry)> StartListener;
    +typedef std::function<void(spTrackEntry* entry)> InterruptListener;
    +typedef std::function<void(spTrackEntry* entry)> EndListener;
    +typedef std::function<void(spTrackEntry* entry)> DisposeListener;
    +typedef std::function<void(spTrackEntry* entry)> CompleteListener;
    +typedef std::function<void(spTrackEntry* entry, spEvent* event)> EventListener;

    显而易见, 函数的参数发生了变化, 我们修改下 lambda 函数参数即可, 同时还需要考虑 executeSpineEvent 函数的变化. 我的修改如下:

    1
    int executeSpineEvent(LuaSkeletonAnimation* skeletonAnimation, int handler, spEventType eventType,  spTrackEntry * entry, int loopCount = 0, spEvent* event = nullptr )

    改变了这个函数的参数, 调用的地方也要改变, 这个就不贴代码了.

    spEventType 变化

    还需要注意到的一个变化是:

    1
    2
    3
    typedef enum {
    SP_ANIMATION_START, SP_ANIMATION_INTERRUPT, SP_ANIMATION_END, SP_ANIMATION_COMPLETE, SP_ANIMATION_DISPOSE, SP_ANIMATION_EVENT
    } spEventType;

    想必之前多了 SP_ANIMATION_INTERRUPTSP_ANIMATION_DISPOSE 事件, 我们要更新所有用到 spEventType 的地方.

    SpineConstants.lua :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    sp.EventType =
    {
    ANIMATION_START = 0,
    ANIMATION_INTERRUPT = 1,
    ANIMATION_END = 2,
    ANIMATION_COMPLETE = 3,
    ANIMATION_DISPOSE = 4,
    ANIMATION_EVENT = 5,
    }

    以及 LuaScriptHandlerMgr.hlua_cocos2dx_spine_manual.cpp 中共计五处变化.

    TrianglesCommand 参数错误

    解决了这个问题, 编译后发现还有一个错误:

    1
    SkeletonBatch.cpp:95:37: No matching member function for call to 'init'

    是调用 TrianglesCommand:init 时多传入了一个参数, 可能是新版 cocos 的 api 改动吧, 我们直接删掉最后一个参数:

    1
    2
    -        _command->trianglesCommand->init(globalZOrder, textureID, glProgramState, blendFunc, *_command->triangles, transform, transformFlags);
    + _command->trianglesCommand->init(globalZOrder, textureID, glProgramState, blendFunc, *_command->triangles, transform);

    到此搞定了所有的编译问题, 尝试了下 Android 和 iOS 的编译, 也没有什么问题. 让我们运行下看下效果:

    后记:

    1. Spine Error:Unknown attachment type: skinnedmesh

    跟新完后有的 Spine 文件会报这个错误, 论坛里有解决方案, 虽然不报错了, 但是也不动了, 可能需要美术重新做一遍.

    2. 真的有必要从 cocos2d-x-lite 中更新吗?

    经过好友 @郭彬 的提醒, 确实没有必要, 他就是从官方仓库更新的, 是我画蛇添足了.