如何优雅地为 cocos2d-x 添加或修改 Lua 绑定?

cocos2d-x 是目前很流行的开源游戏引擎。由于开源的便利,即便是在以 Lua 为开发的语言的项目里面,大家都会包含 cocos2d-x 的源代码。这为需要的时候修改 cocos2d-x 源代码提供了方便。然而,直接修改 cocos2d-x 源代码带来的坏处是:下次需要更新 cocos2d-x 版本的时候会很麻烦,一不小心就会弄丢自己的改动。更好的做法是可以将修改部分提交 pull request 到 cocos2d-x。如果运气好的话,cocos2d-x 的维护者会很快合并你的修改。当然,如果你的 pull request 半天没有人理,即便理了,很久都没有人合并,那么就很难达到想要的效果了。因此我们需要一个替代的方法。这里我介绍下我在项目里面采用的修改 cocos2d-x Lua 绑定的方法流程。

预备知识

  1. 你需要熟悉 git 和 GitHub
  2. 你需要熟悉如何使 Lua C API 和 cocos2d-x 使用的 tolua++ 系统

仓库管理

  1. 一个项目仓库(那是必须的),其中包一份由 cocos new ... 命令为你拷贝的一份位于 frameworks/cocos2d-x cocos2d-x项目本地源代码(后面简称这份 cocos2d-x 源代码为项目拷贝
  2. 一份 cocos2d-x 官方源代码的仓库(后面我们称这个仓库为官方仓库
  3. 你在 GitHub 上有一份 cocos2d-x 的 fork,将 cocos2d-x 官方仓库地址在 git 里面 remote 名设置为 upstream,将你的 fork 的地址设置为 origin

修补流程

当遇到 cocos2d-x 有 bug 或者缺少你想要的绑定的时候:

  1. 先修改项目拷贝源代码,作出你要的修改,测试通过(不过最好不要在项目代码中提交对 cocos2d-x 下面的修改,原因见下一步,下一步会进一步处理这些修改)
  2. checkout cocos2d-x 官方分支对应你使用版本的分支 (比如你用的是 3.9.0 那就拉取 v3 分支),本地分支名以你的修改命名,比如 add-xx-binding-to-xxx
  3. 将项目拷贝文件夹和官方仓库Beyond Compare 之类的工具进行对比,把在项目拷贝中修改部分复制到官方仓库
  4. 将上一步的更改提交,push 到 origin (即你的 fork 地址)
  5. 登录 GitHub,你的 cocos2d-x fork 项目页面,提及 pull request

隔离修改

书分两头,很可能 cocos2d-x 不能很快合并你的 pull request 甚至拒绝。你需要将你在项目拷贝中的修改移动到 framewroks/runtime-src/Classes 下面,当然建议弄个专门的目录,比如 framewroks/runtime-src/Classes/cocos2d-x-hotfixes。这样可以让你对项目拷贝不作出修改,这样下次需要升级 cocos2d-x 版本的时候基本上可以直接覆盖,(当然,你可能需要合并 ccConfig.h

比如我在项目里面添加了EventDispatcher::addCustomEventListener()的 Lua 绑定,我需要这样做:

实现

首先我在 framewroks/runtime-src/Classes/cocos2d-x-hotfixes/EventDispatcher_addCustomEventListener-hotfixes.cpp中:

  1. 实现 static int cc_EventDispatcher_addCustomEventListener(lua_State* L);
  2. 在末尾添加注入虚拟机的代码:
int EventDispatcher_addCustomEventListener_hotfixes(lua_State* L)
{
    tolua_open(L);
    tolua_module(L, "cc", 0);
    tolua_beginmodule(L, "cc");
        tolua_module(L, "EventDispatcher", 0);
        tolua_beginmodule(L,"EventDispatcher");
            tolua_function(L,"addCustomEventListener",cc_EventDispatcher_addCustomEventListener);
        tolua_endmodule(L);
    tolua_endmodule(L);

    return 0;
}

导出

第二步,然后添加framewroks/runtime-src/Classes/cocos2d-x-hotfixes/EventDispatcher_addCustomEventListener-hotfixes.hpp,内容如下:

#pragma once
#ifndef __EventDispatcher_addCustomEventListener_hotfixes_HPP__
#define __EventDispatcher_addCustomEventListener_hotfixes_HPP__

#if __cplusplus
extern "C" {
#endif

#include "lua.h"

int EventDispatcher_addCustomEventListener_hotfixes(lua_State* L);

#if __cplusplus
}
#endif

#endif

注入

第三步,在 frameworks/runtime-src/Classes/lua_module_register.h 中:

  1. 添加 #include "path/to/EventDispatcher_addCustomEventListener-hotfixes.hpp"
  2. static int lua_module_register(lua_State* L) 尾部添加:
static int lua_module_register(lua_State* L)
{
    //Don't change the module register order unless you know what your are doing
    register_cocosdenshion_module(L);
    register_network_module(L);
    register_cocosbuilder_module(L);
    register_cocostudio_module(L);
    register_ui_moudle(L);
    register_extension_module(L);
    register_spine_module(L);
    register_cocos3d_module(L);
    register_audioengine_module(L);
#if CC_USE_3D_PHYSICS && CC_ENABLE_BULLET_INTEGRATION
    register_physics3d_module(L);
#endif
#if CC_USE_NAVMESH
    register_navmesh_module(L);
#endif
    EventDispatcher_addCustomEventListener_hotfixes(L);
    return 1;
}

收尾

最后,提交到你的项目中。

总结

虽然我个人认为目前 cocos2d-x 处理 issues 和 pull requests 的速度有点打击参与者的积极性。然而在这样的情况下,这个流程能很好的适应 cocos2d-x 目前的情况:

进,能为 cocos2d-x 贡献代码和补丁;退,能将修改隔离,方便项目升级 cocos2d-x 的版本。

如果将来 cocos2d-x 官方合并了你的代码,你可以升级到相应的版本,然后删除上面的这些代码。