cocos2d-x lua-binding: 源码分析

简介: cocos2d-x lua-binding: 源码分析

cocos2dx自带的lua-binding没有处理好std::vector的情况,修改过程如下:

cocos2d-x/tools/bindings-generator/generator.py

通过命令行调用这个py脚本,vscode调试配置

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: generator",
            "type": "python",
            "request": "launch",
            "program": "E:/project/engineTools/frameworks/cocos2d-x/tools/bindings-generator/generator.py",
            "console": "integratedTerminal",
            "justMyCode": true,
            "cwd": "E:/project/engineTools/frameworks/cocos2d-x/tools/tolua",
            "args": [
                "e:/project/engineTools/frameworks/cocos2d-x/tools/tolua/cocos2dx_curve.ini",
                "-s",
                "cocos2dx_curve",
                "-t",
                "lua",
                "-o",
                "e:/project/engineTools/frameworks/cocos2d-x/cocos/scripting/lua-bindings/auto",
                "-n",
                "lua_cocos2dx_curve_auto"
            ]
        }
    ]
}
复制代码

需要注意args,和ini的配置有关系

执行逻辑流程:

  • 解析args参数
  • 读取userconf.ini配置,这个配置是 tolua里面自动生成的
  • 根据target查找对应的模板代码
  • 循环依次根据sectoins生成对应的bunding代码

比较核心的2段代码

# 在Generator的构造函数里面,会解析ini配置里面的参数
generator = Generator(gen_opts)
# 开始正式生成代码    
generator.generate_code()
复制代码
  • 读取lua/conversions.yaml配置文件
definitions:
  # the names of the functions - we use this to generate the code and to register the functions in
  # the javascript class
  ifunction: "lua_${generator.prefix}_${class_name}_${func_name}"
  sfunction: "lua_${generator.prefix}_${class_name}_${func_name}"
  constructor: "lua_${generator.prefix}_${class_name}_constructor"  
conversions:
  # some times you want to use a special native type when converting from spidermonkey to native
  # the most common case would be from JS-boolean to bool. Using "bool" will fail here since we
  # pass the address to the conversion method, and a JSBool is defined as an integer in spidermonkey
  native_types:
    float: "double"
  ns_map:
    "cocos2d::experimental::ui::": "ccexp."
  to_native:
    # lua to native
    int: "ok &= luaval_to_int32(tolua_S, ${arg_idx},(int *)&${out_value}, \"${lua_namespaced_class_name}:${func_name}\")"
  from_native:
    # native to lua
    int: "tolua_pushnumber(tolua_S,(lua_Number)${in_value})"
复制代码
  • 确定*.hpp, *.cpp, _api.lua文件路径
  • layout_foot.(h,c),layout_head.(h,c)这里面是cheetah模板文件

layout_head.h

\#include "base/ccConfig.h"
#if $macro_judgement
$macro_judgement
#end if 
\#ifndef __${prefix}_h__
\#define __${prefix}_h__
#if $hpp_headers
#for header in $hpp_headers
\#include "${header}"
#end for
#end if 
\#ifdef __cplusplus
extern "C" {
\#endif
\#include "tolua++.h"
\#ifdef __cplusplus
}
\#endif
int register_all_${prefix}(lua_State* tolua_S);
复制代码

模板结果

#include "base/ccConfig.h"
#ifndef __cocos2dx_curve_h__
#define __cocos2dx_curve_h__
#ifdef __cplusplus
extern "C" {
#endif
#include "tolua++.h"
#ifdef __cplusplus
}
#endif
int register_all_cocos2dx_curve(lua_State* tolua_S);
#endif // __cocos2dx_curve_h__
复制代码

以layout_foot.h为例

\#endif // __${prefix}_h__
#if $macro_judgement
\#endif //$macro_judgement
#end if  
复制代码

主要的lua-binding逻辑代码

def  generate_code(self):
   # ... 检查语法是否正确等相关工作
   self._parse_headers() #解析c++的头文件  
def _parse_headers(self):
  for header in self.headers:
    self._deep_iterate(tu.cursor) # 依次迭代检测语法树
def _deep_iterate(self, cursor, depth=0):
   if is_targeted_class and self.in_listed_classes(cursor.displayname):
        if not self.generated_classes.has_key(cursor.displayname):
            nclass = NativeClass(cursor, self)
            nclass.generate_code() # 真正开始生成binding代码的入口
class NativeClass(object):
  def  generate_code(self):
        # ... 前后的逻辑都是准备工作,创建模板引擎
        for m in self.methods_clean():
            m['impl'].generate_code(self)
        for m in self.static_methods_clean():
            m['impl'].generate_code(self)
        if self.generator.script_type == "lua":
            for m in self.override_methods_clean():
                m['impl'].generate_code(self, is_override = True)
        for m in self.public_fields:
            if self.generator.should_bind_field(self.class_name, m.name):
                m.generate_code(self)
class  NativeFunction(object): # 生成具体的function
  def generate_code(self):
     # 到这里就需要看具体对应的模板文件逻辑了
    Template(file=os.path.join(gen.target, "templates", "sfunction.c"),
                            searchList=[current_class, self])
复制代码

在sfunction.c模板文件中有调用

${arg.to_native({})}
复制代码

to_native又回到了Python的脚本里面,这里就需要关注下c++头文件,举个例子:

class Action :public ActionInterval {
public:
  static Action* create(float duration, std::vector<Vec2> points);
};
复制代码
def to_native(self, convert_opts): #344
  keys = []
  # self.name为函数参数的类型
  # 比如Action的create函数参数分别为float、cocos2d::Vec2(注意这里是带namespace的)
  keys.append(self.name)
  if self.is_object:
    # 这里判断
    if not NativeType.dict_has_key_re(to_native_dict, keys):
      keys.append("object")
复制代码
def dict_has_key_re(dict, real_key_list):
        for real_key in real_key_list:
            for (k, v) in dict.items():
                if k.startswith('@'):
                    k = k[1:]
                    # @开头的,视为正则表达式,@之后的内容为正则
                    match = re.match("^" + k + "$", real_key)
                    if match:
                        return True
                else:
                    if k == real_key:
                        return True
        return False
复制代码

比如Action的create方法,最终正则计算为

reg.match("^vector<cocos2d::Vec2.*>$", "vector<cocos2d::Vec2, std::allocator<cocos2d::Vec2> >")
复制代码

conversions.yml需要这么写

conversions:
  to_native:
    "@vector<cocos2d::Vec2.*>":"这个是能够匹配上的",
    "@vector<Vec2.*>":"这个是无法匹配上的,因为没有写namespace",
复制代码

value内容的书写,也需要遵守cheetah的语法,才能被模板引擎正确的替换。

最终结果:

conversions:
 to_native:
   "@vector<cocos2d::Vec2.*>": "ok &= luaval_to_std_vector_vec2(tolua_S, ${arg_idx}, &${out_value}, \"${lua_namespaced_class_name}:${func_name}\")"
复制代码

hpp里面目录的问题

targets/lua/templates/layout_head.c

\#include "scripting/lua-bindings/auto/${out_file}.hpp"
#if $macro_judgement
$macro_judgement
#end if
#for header in $headers
    #set relative = os.path.relpath(header, $search_path)
    #if not '..' in relative
\#include "${relative.replace(os.path.sep, '/')}"
    #else
\#include "${os.path.basename(header)}"
    #end if
#end for
\#include "scripting/lua-bindings/manual/tolua_fix.h"
\#include "scripting/lua-bindings/manual/LuaBasicConversions.h"
#if $cpp_headers
#for header in $cpp_headers
\#include "${header}"
#end for
#end if
复制代码

中间的目录跟$headers$search_path有关系,

'search_path': os.path.abspath(os.path.join(userconfig.get('DEFAULT', 'cocosdir'), 'cocos')),
复制代码
  • $search_path: frameworks\cocos2d-x\cocos
  • $headers:对应init里面的headers

os.path.relpath的逻辑,导致只有在相对于cocos目录时,才会显示带目录的include,否则只显示文件名,所以想要带上路径的文件名,在不改动Python的前提下,只能放到cocos目录了



目录
相关文章
|
4月前
|
Linux 数据安全/隐私保护 iOS开发
【教程】使用ipagurd打包与混淆Cocos2d-x的Lua脚本
本文将介绍如何使用ipagurd工具对Cocos2d-x中的Lua脚本进行打包与混淆,以及在iOS应用开发中的实际应用。我们将以Cocos2d-x-2.2.1 samples中的HelloLua为例,详细展示整个处理流程,并提供相应的代码案例演示。
|
Windows
在VsCode上调试Cocos2d-x lua项目
在VsCode上调试Cocos2d-x lua项目
942 0
|
C++
cocos2d-x lua-binding:将lua-binding结果引入到项目中使用
cocos2d-x lua-binding:将lua-binding结果引入到项目中使用
132 0
|
Python
cocos2d-x lua-binding:环境配置
cocos2d-x lua-binding:环境配置
59 0
cocos2d-x lua-binding:cheetah模板引擎
cocos2d-x lua-binding:cheetah模板引擎
62 0
|
测试技术 Linux Android开发
如何使用ZEROBRANE STUDIO远程调试COCOS2D-X的LUA脚本(转)
http://www.cocos2d-x.org/docs/manual/framework/native/v2/lua/lua-remote-debug-via-zerobrane/zh ZeroBrane Studio做为一个轻量级的Lua IDE,因为它支持跨平台(支持Windows、Mac和Linux)和支持真机调试(Andorid、IPhone和IPad),所以经常被用来调试Lua。
1643 0
Cocos2d-x Lua中帧动画
<p><span style="font-size:14px;"><a target="_blank" name="OLE_LINK53"></a>帧动画就是按一定时间间隔、一定的顺序、一帧一帧地显示帧图片。我们的美工要为精灵的运动绘制每一帧图片,因此帧动画会由很多帧组成,按照一定的顺序切换这些图片就可以了。</span></p> <p><span style="font-size:14px;
1637 0