关于cocos2d-x下Lua调用C++的文档看了不少,但没有一篇真正把这事给讲明白了,我自己也是个初学者,摸索了半天,总结如下:

cocos2d-x下Lua调用C++这事之所以看起来这么复杂、网上所有的文档都没讲清楚,是因为存在5个层面的知识点:

1、在纯C环境下,把C函数注册进Lua环境,理解Lua和C之间可以互相调用的本质
2、在cocos2d-x项目里,把纯C函数注册进Lua环境,理解cocos2d-x是怎样创建Lua环境的、以及怎样得到这个环境并继续自定义它
3、了解为什么要使用toLua++来注册C++类
4、在纯C++环境下,使用toLua++来把一个C++类注册进Lua环境,理解toLua++的用法
5、在cocos2d-x项目里,使用cocos2d-x注册自身的方式把自定义的C++类注册进Lua环境,理解cocos2d-x是怎样通过bindings-generator脚本来封装toLua++的用法来节省工作量的

只有理解了前4层,在最后使用bindings-generator脚本的时候心里才会清清楚楚。而网上的文档,要么是只解释了第1层,要么是只填鸭式地告诉你第5层怎么用bindings-generator脚本,不仅中间重要的知识点一概不提,示例代码往往也写的不够简洁,这让我这种看见C++就眼晕的人理解起来大为头疼(不是我不会C++,而是我非常不接受C++的设计哲学,能避就避)。所以接下来的讲解我会对每一层知识点逐一讲解,示例代码也不求完整严谨,而是尽量用最简洁的方式把程序的关键点说明白。


第一层:纯C环境下,把C函数注册进Lua环境

直接看代码比啰哩啰嗦讲一大堆概念要清晰明了的多。建立一个a.lua和一个a.c文件,内容如下,一看就明白是怎么回事了:

a.lua

print(foo(99))

a.c

#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

int foo(lua_State *L) {
  int n = lua_tonumber(L,1);

  lua_pushnumber(L,n + 1);

  return 1;
}

main() {
  lua_State *L = lua_open();

  luaL_openlibs(L);

  lua_register(L,"foo",foo);

  luaL_dofile(L,152)">"a.lua");

  lua_close(L);

  0;
}

怎么样,这代码简单吧?一看就明白,简单的不能再简单了。我特别烦示例代码里又是判断错误又是加代码注释的,本来看自己不会的代码就够吃力的了,还加那么多花花绿绿的干扰项,纯粹增加学习负担。

在命令行下用gcc来编译并执行吧:

gcc a.c -llua && ./a.out

注意-llua选项是必要的,因为要连接lua的库。

看完上面那段代码,再解释起来就容易多了:

1、要想注册进Lua环境,函数需要定义为这个样:int xxx(lua_State *L)
2、使用lua_tonumberlua_tostring等函数,来取得传入的参数,比如lua_tonumber(L,1)就是得到传入的第一个参数,且类型为数字
3、使用lua_pushnumberlua_pushstring等函数,来将返回值压入Lua的环境中,因为Lua支持函数返回多个值,所以可以push多个返回值进Lua环境
4、最终函数返回的数字表示有多少个返回值被压入了Lua环境
5、使用lua_register宏定义来将这个函数注册进Lua环境,Lua脚本里就可以用它了,大功告成!就这么简单!


第二层:在cocos2d-x环境下,把C函数注册进Lua环境

也简单:

1、在frameworks/runtime-src/Classes/目录下,找到AppDelegate.cpp文件。如果frameworks目录不存在,则需要参考这篇Blog:用Cocos Code IDE写Lua,如何与项目中的C++代码和谐相处

AppDelegate.cpp文件中的关键代码如下:
```c++
auto engine = LuaEngine::getInstance();
ScriptEngineManager::getInstance()->setScriptEngine(engine);

LuaStack* stack = engine->getLuaStack();
stack->setXXTEAKeyAndSign("2dxLua",strlen("2dxLua"),152)">"XXTEA",152)">"XXTEA"));

//register custom function
//LuaStack* stack = engine->getLuaStack();
//register_custom_function(stack->getLuaState());

可以看到cocos2d-x已经为我们留出了注册自定义C函数的位置,在注释代码后面这么写就可以了: ```cpp lua_State *L = stack->getLuaState(); lua_register(L,152)">"test_lua_bind",210)">test_lua_bind);

也可以通过ScriptEngineManager类从头取得当前的LuaEngine对象,然后再getLuaStack()方法得到封装的LuaStack对象,再调用getLuaState()得到原始的lua_State结构指针。只要知道了入口位置,其他一切就不成问题了,还是挺简单的。

感兴趣的话可以去看一下ScriptEngineManager类的详细定义,在frameworks/cocos2d-x/cocos/base/ccScriptSupport.h文件中。

BTW:这里还有一个小知识点,插入在AppDelegate.cpp中的自定义代码尽量写在COCOS2D_DEBUG宏定义的判断前面,因为在调试环境下和真机环境下后续执行的代码是不一样的:

#if (COCOS2D_DEBUG>0)
    if (startRuntime())
        return true;
#endif

    // 调试环境下代码就不会走到这里了
    engine->executeScriptFile(ConfigParser::getInstance()->getEntryFile().c_str());
    true;

2、接下来,找个地方把test_lua_bind函数定义写进去就算大功告成了。如果追求文件组织的优雅,按理说应该新建一个.c文件,但这样的话搞不好会把自己陷入到编译阶段的泥潭里,所以先不追求优雅,而就在AppDelegate.cpp文件末尾写上函数的定义就可以了,简单清楚明了:

test_lua_bind(lua_State *L) {
    int number = lua_tonumber(L,152)">1);

    number = number + 1;

    lua_pushnumber(L,number);

    1;
}

3、大功告成,现在就可以在main.lua文件里使用test_lua_bind()函数了:

local i = bind(99)
  print("lua bind: " .. tostring(i))

4、如果是新建一个.c文件呢?把AppDelegate.cpp文件里test_lua_bind函数定义的代码删掉,在头部#include后面加入:

#include "test_lua_bind.h"

frameworks/runtime-src/Classes目录下创建test_lua_bind.h文件,内容如下:

extern "C" {
#include "lua.h"
#include "lualib.h"
}

test_lua_bind(lua_State *L);

再创建test_lua_bind.c文件,内容不变:

#include "test_lua_bind.h"

 此时用cocos compile -p mac命令编译,会发现test_lua_bind.c文件并没有被编译。这是当然的,普通的C/C++项目都是用Makefile来指定编译哪些.c/cpp文件的,当前的cocos2d-x项目虽然没有Makefile文件,但也是遵循这个原则的,也即肯定是有一个地方来指定所有要编译的文件的,需要在这个地方把test_lua_bind.c加进去,使得整个项目编译时把它也作为项目的一部分。 

答案是,cocos2d-x项目没有使用Makefile,而是非常聪明地使用了与具体环境相关的工程文件来作为命令行编译的环境,比如在编译iOS或Mac时就使用Xcode工程文件,在编译Android时就使用Android.mk文件。

所以,添加好了test_lua_bind.htest_lua_bind.c文件后,用Xcode打开项目,将这俩文件添加进工程中就行了。

注意,千万不要勾选“copy items into destination group's folder(if needed)”,因为cocos2d-x的Xcode工程目录组织不是常规的结构,一旦勾选这个,会导致这两个文件被拷贝至frameworks/runtime-src/proj.ios_mac目录下,原来frameworks/runtime-src/Classes目录下的文件就废掉了,这样的组织方式会乱,而且会影响Android那边对这俩文件的引用。

test_lua_bind.cpp这俩文件添加进Xcode工程后,再去命令行执行cocos compile -p mac,编译就能成功了。

网上有其他文章说还要修改Xcode工程的“User Headers Path”,这个经过试验是不需要的,哪怕把这俩文件放进新建的文件夹里也不需要,只要加入了Xcode工程即可,因为Xcode内部根本就不是按照文件夹的形式来组织文件的,它自己有一套叫做“Group”的东西。搞了好几年iOS开发,对Xcode的这个特性还是熟悉的。

说到这就不禁要插一句对网上所有cocos2d-x文档的吐槽了,学习cocos2d-x的人水平实在是良莠不齐,大部分人似乎都是对游戏热衷的编程初学者,他们大多底子薄基础差,甚至一大部分人之前都没做过移动APP的开发,他们学习cocos2d-x只想知其然而不想知其所以然,给他们讲他们也看不明白(因为编程基础差),所以网上不少cocos2d-x文章都是只讲123步骤,而不告诉你为什么这么做,包括cocos2d-x官方的大量文档也是基于这个思路写的,中文和英文都一样。我看这些文章就特别痛苦,一边看一边心里就总是在想,“凭什么要这么做啊”、“这一步是为了什么啊”、“怎么这么麻烦啊”、“这个步骤明显不是最佳实践啊”、“解决这事为啥要这么麻烦”、“有更好的方法吗”,所以我这种初学者来看cocos2d-x文档就变成了不是单纯的学习,而是学习、质疑、求证、反思、优化的过程,对别人来说cocos2d-x的入门比较容易,到我这里反倒成了入门比较难、入门之后比较容易了,因为文档中的垃圾信息和无效信息实在是太多了,别人可以照单全收、以后懂了之后再慢慢剔除,我是必须从一开始就自己甄别垃圾、只保留最佳实践,这也是这篇Blog写的比较长的原因。

扯远了。反正经过以上步骤,就完成了在cocos2d-x项目中把C函数注册进Lua环境这件事。至此,算是彻底搞懂了Lua和C函数之间的互相调用关系,也能在cocos2d-x的Lua环境中使用自定义的C函数了。但这还不够,因为一个正规的项目是需要狠好的组织结构的,全局C函数满天飞肯定是不行的,好一点的情况是把所有的C函数都在Lua中组织为模块注册进去,更好一点的情况是把C++类注册进Lua、并且C++类也是以Lua模块为组织方式注册进Lua环境的。这其实就是cocos2d-x自己把自己注册进Lua环境的方式。


第三层:了解为什么要使用toLua++来注册C++类

因为Lua的本质是C,不是C++,Lua提供给C用的API也都是基于面向过程的C函数来用的,要把C++类注册进Lua形成一个一个的table环境是不太容易一下子办到的事,因为这需要绕着弯地把C++类变成各种其他类型注册进Lua,相当于用面向过程的思维来维护一个面向对象的环境。这其中的细节就不去深究了,总之正是因为如此,所以单纯地手写lua_register()等代码来注册C++类是行不通的、代价高昂的,所以需要借助toLua++这个工具。

这一层的知识点看似简单,但其实是非常重要的,只有理解了手工用lua_register()去注册C++类的难度,才能理解使用toLua++这类工具的必要性。只有理解了使用toLua++工具的必要性,才会潜下心来冷静地接受toLua++本身的优点和缺点。只有看到了toLua++本身的缺点和使用上的麻烦,才会真心理解cocos2d-x使用bindings-generator脚本带来的好处。只有理解了bindings-generator脚本带来的好处,才能谅解这个脚本本身在使用上的一些不便之处。


第四层:在纯C++环境下,使用toLua++来把一个C++类注册进Lua环境

虽然终极方法是用bindings-generator脚本来注册C++类进cocos2d-x的Lua环境,但理解toLua++本身的用法还是狠有必要的,只有知道了toLua++原本的用法,才能更好地理解cocos2d-x是怎么把自己的C++类都注册进Lua环境的,这不仅能让编程时的思路更加清晰,也能为日后在源码中寻找各种接口文档的过程中不至于看不懂那一大堆tolua_beginmoduletolua_function是什么意思。影响程序员学习提高的一大障碍就是忽略那些一知半解的代码,不去刨根究底地搞明白。

使用toLua++的标准做法是:

1、准备好自己的C++类,该怎么写就怎么写
2、仿造这个类的.h文件,改一个.pkg文件出来,具体格式要按照toLua++的规定,比如移除所有的private成员等
3、建一个专门用来桥接C++和Lua之间的C++类,使用特殊的函数签名来写它的.h文件,.cpp文件不写,等着toLua++来生成
4、给这个桥接的C++类写一个.pkg文件,按照toLua++的特殊格式来写,目的是把真正做事的C++类给定义进去
5、在命令行下用toLua++生成桥接类的.cpp文件
6、程序入口引用这个桥接类,执行生成的桥接函数,Lua环境中就可以使用真正做事的C++类了

toLua++这种自己手写.pkg文件的方式古老又难受,所以我没有仔细地去学习,这套流程放在10年前的那个年代是没有太大问题的,作者怎么规定就怎么用好了,但是放在2014年的今天,任何程序的架构设计都讲究学习成本低、轻量化、符合以往的习惯,因此toLua++用起来我觉得其实是难受的。

下面我以尽量最少的代码来走一遍toLua++的流程,注意这是在纯C++环境下,跟任何框架都没关系,也不考虑内存释放等细节:

MyClass.h

class
MyClass { public: MyClass() {}; foo(int i); };

MyClass.cpp

#include "MyClass.h"

int MyClass::foo(int i)
{
  return i + 100;
}

MyClass.pkg

MyClass
{
  MyClass();
   MyLuaModule.h 
#include "tolua++.h"
} #include "MyClass.h" TOLUA_API tolua_MyLuaModule_open(lua_State* tolua_S);

MyLuaModule.pkg

$#include "MyLuaModule.h"

$pfile "MyClass.pkg"

main.cpp

"C" { 
#include <lauxlib.h>
}

#include "MyLuaModule.h"

main() {
  lua_State *L = lua_open();

  luaL_openlibs(L);

  tolua_MyLuaModule_open(L);

  luaL_dofile(L,152)">"main.lua");

  lua_close(L);

   main.lua 
local
test = MyClass:new() print(test:foo( 先在命令行下执行:
tolua++ -o MyLuaModule.cpp MyLuaModule.pkg

此命令用来生成桥接文件MyLuaModule.cpp。注意命令行中-o参数的顺序不能随意摆放,从这个小事也能看出tolua++的古老和难用

生成好MyLuaModule.cpp文件后,就能看到它里面的那一大堆桥接代码了,比如tolua_function等。以后看到这些东西就不陌生了,就明白这些函数只是toLua++用来做桥接的必备代码了,简单看一下代码,就理解toLua++是怎样把MyClass这个C++类注册进Lua中的了:

接下来,用g++来编译:

g++ MyClass.cpp MyLuaModule.cpp main.cpp -llua -ltolua++

默认就生成了a.out文件,执行,就能看到main.lua的执行结果了:

至此,对toLua++的运作原理心里就透亮了,无非就是:

1、把自己该写的类写好
2、写个.pkg文件,告诉toLua++这个类暴露出哪些接口给Lua环境
3、再写个桥接的.h和.pkg文件,让toLua++去生成桥接代码
4、在程序里使用这个桥接代码,类就注册进Lua环境里了


第五层:使用cocos2d-x的方式来将C++类注册进Lua环境

cocos2d-x在2.x版本里就是用toLua++和.pkg文件这么把自己注册进Lua环境里的。不过这种方法明显笨拙,既要写真正做事的.pkg文件,也要写桥接的.pkg文件和.h文件,工作量又大又枯燥。所以从cocos2d-x 3.x开始,用bindings-generator脚本代替了toLua++。

bindings-generator脚本的工作机制是:

1、不用挨个类地写桥接.pkg和.h文件了,直接定义一个ini文件,告诉脚本哪些类的哪些方法要暴露出来,注册到Lua环境里的模块名是什么,就行了,等于将原来的每个类乘以3个文件的工作量变成了所有类只需要1个.ini文件
2、摸清了toLua++工具的生成方法,改由Python脚本动态分析C++类,自动生成桥接的.h和.cpp代码,不调用tolua++命令了
3、虽然不再调用tolua++命令了,但是底层仍然使用toLua++的库函数,比如tolua_function,bindings-generator脚本生成的代码就跟使用toLua++工具生成的几乎一样

bindings-generator脚本掌握了生成toLua++桥接代码的主动权,不仅可以省下大量的.pkg和.h文件,而且可以更好地插入自定义代码,达到cocos2d-x环境下的一些特殊目的,比如内存回收之类的。所以cocos2d-x从3.x开始放弃了toLua++和.pkg而改用了自己写的bindings-generator脚本是非常值得赞赏的聪明做法。

接下来说怎么用bindings-generator脚本:

1、写自己的C++类,按照cocos2d-x的规矩,继承cocos2d::Ref类,以便使用cocos2d-x的内存回收机制。当然不这么干也行,但是不推荐,不然在Lua环境下对象的释放狠麻烦。
2、编写一个.ini文件,让bindings-generator可以根据这个配置文件知道C++类该怎么暴露出来
3、修改bindings-generator脚本,让它去读取这个.ini文件
4、执行bindings-generator脚本,生成桥接C++类方法
5、用Xcode将自定义的C++类和生成的桥接文件加入工程,不然编译不到
6、修改AppDelegate.cpp,执行桥接方法,自定义的C++类就注册进Lua环境里了

看着步骤挺多,其实都狠简单。下面一步一步来。

首先是自定义的C++类。我习惯将文件保存在frameworks/runtime-src/Classes/目录下:

frameworks/runtime-src/Classes/MyClass.h

#include "cocos2d.h"
using namespace cocos2d; class MyClass : public Ref { public: MyClass() {}; ~MyClass() {}; bool init() { true; }; CREATE_FUNC(MyClass); frameworks/runtime-src/Classes/MyClass.cpp
 然后编写.ini文件。在frameworks/cocos2d-x/tools/tolua/目录下能看到genbindings.py脚本和一大堆.ini文件,这些就是bindings-generator的实际执行环境了。随便找一个内容比较少的.ini文件,复制一份,重新命名为MyClass.ini。大部分内容都可以凑合不需要改,这里仅列出必须要改的重要部分: 

frameworks/cocos2d-x/tools/tolua/MyClass.ini

[MyClass]
prefix = MyClass
target_namespace = my
headers = %(cocosdir)s/../runtime-src/Classes/MyClass.h
classes = MyClass

也即在MyClass.ini中指定MyClass.h文件的位置,指定要暴露出来的类,指定注册进Lua环境的模块名。

注意,这个地方我踩了个坑。如果.ini配置文件中存在macro_judgement = ...宏定义,要特别小心,我第一次是从cocos2dx_controller.ini文件复制来的,结果没注意macro_judgement,导致生成的桥接类文件加入了不该加入的宏,只在iOS和Android平台上才起作用,对Mac平台无效,这个要特别注意。

然后修改genbindings.py文件129行附近,将MyClass.ini文件加进去:

frameworks/cocos2d-x/tools/tolua/genbindings.py

cmd_args = {'cocos2dx.ini' : ('cocos2d-x',152)">'lua_cocos2dx_auto'
),\ 'MyClass.ini' : ('MyClass',152)">'lua_MyClass_auto'
),\ ...

(其实这一步本来是可以省略的,只要让genbindings.py脚本自动搜寻当前目录下的所有ini文件就行了,不知道将来cocos2d-x团队会不会这样优化)

至此,生成桥接文件的准备工作就做好了,执行genbindings.py脚本:

python genbindings.py

(在Mac系统上可能会遇到缺少yaml、Cheetah包的问题,安装这些Python包狠简单,先sudo easy_install pip,把pip装好,然后用pip各种pip searchsudo pip install就可以了)

成功执行genbindings.py脚本后,会在frameworks/cocos2d-x/cocos/scripting/lua-bindings/auto/目录下看到新生成的文件:

每次执行genbindings.py脚本时间都挺长的,因为它要重新处理一遍所有的.ini文件,建议大胆修改脚本文件,灵活处理,让它每次只处理需要的.ini文件就可以了,比如像这个样子:

frameworks/cocos2d-x/cocos/scripting/lua-bindings/auto/目录下观察一下生成的C++桥接文件lua_MyClass_auto.cpp,里面的注册函数名字为register_all_MyClass(),这就是将MyClass类注册进Lua环境的关键函数:

编辑frameworks/runtime-src/Classes/AppDelegate.cpp文件,首先在文件头加入对lua_MyClass_auto.hpp文件的引用:

然后在正确的代码位置加入对register_all_MyClass函数的调用:

最后在执行编译前,将新加入的这几个C++文件都加入到Xcode工程中,使得编译环境知道它们的存在:

这其中还有一个小坑,由于lua_MyClass_auto.cpp文件要引用MyClass.h文件,而这俩文件分属于不同的子项目,互相不认识头文件的搜寻路径,因此需要手工修改一下cocos2d_lua_bindings.xcodeproj子项目的User Header Search Paths配置。特别注意一共有几个../

最后,就可以用cocos compile -p mac命令重新编译整个项目了,不出意外的话编译一定是成功的。

修改main.lua文件中,尝试调用一下MyClass类:

test = my.MyClass:create()
"lua bind: " ..  然后执行程序(用cocos rum -p mac或在Cocos Code IDE中均可),见证奇迹的时刻~~~~咦我擦?!程序崩溃!为毛? 

这是我作为cocos2d-x初学者遇到的最大的坑,坑了我整整一天半,具体的研究细节就不详细说了,总之罪魁祸首是cocos2d-x框架中的ccluaEngine.cpp文件的这段代码:

原因是executeScriptFile函数执行时,对当前Lua环境中的栈进行了清理,当register_all_MyClass函数被调用时,Lua栈是全空的状态,函数内部执行到tolua_module函数调用时就崩溃了:

解决办法是修改AppDelegate.cpp为这个样子:

文本形式的代码如下:

AppDelegate.cpp

lua_State *L = stack->getLuaState();
lua_getglobal(L,152)">"_G"
); register_all_MyClass(L); lua_settop(L,152)">0);

重新编译并执行,程序就正确执行了:

至此,就彻底搞清楚应该怎样在cocos2d-x项目里绑定一个C函数或者C++类到Lua环境中了,感兴趣的话可以再进一步深入研究Lua内部Metatable的运作原理、类对象的生成与释放、以及垃圾回收。我自己也是刚接触cocos2d-x不到一个星期,理解不深,以上难免会有用词不当或理解错误的地方,如有错误请多包涵。

后记补充:如果C++类定义了namespace,则需要修改frameworks/cocos2d-x/tools/bindings-generator/targets/lua/conversions.yaml文件,定义namespace与Lua之间的映射关系,否则会报conversion wasn't set错误:

Cocos2d-x下Lua调用自定义C++类和函数的最佳实践的更多相关文章

  1. ios – Xcode找不到Alamofire,错误:没有这样的模块’Alamofire’

    我正在尝试按照github(https://github.com/Alamofire/Alamofire#cocoapods)指令将Alamofire包含在我的Swift项目中.我创建了一个新项目,导航到项目目录并运行此命令sudogeminstallcocoapods.然后我面临以下错误:搜索后我设法通过运行此命令安装cocoapodssudogeminstall-n/usr/local/bin

  2. ios – Xcode在发布时崩溃

    我正在开发iOS应用程序,我在应用程序中没有更改任何内容.但是在重新启动我的Mac时,XCode每次都会崩溃.不知道这是否重要,但我的聚光灯找不到任何应用程序当我打开取景器并使用快捷键cmdshiftH时我看到一个白色的窗户.Xcode不再启动并抛出此错误:我希望有一个人可以帮助我.这会导致与权限冲突吗?

  3. xcode找不到匹配的配置文件

    我有一个AdhociOS应用程序,它给了我“在xcode6中找不到匹配的配置文件”,我创建了一个Adhoc配置文件,下载它,双击它并在General–Identity下选择了一个团队.但我接着得到了那条消息,并尝试使用“修复问题”按钮没有帮助.在构建设置–供应配置文件–发布我有“自动”.任何人都可以帮助我,我完全迷失了……

  4. ios – 仅在异步函数完成执行后运行代码

    所以,例如:如果问题是你不知道要调用什么函数,你可以配置你周围的函数/对象,这样有人可以给你一个函数,然后你在我上面说“调用函数”的地方调用你的函数.例如:

  5. iOS:调试无法在XCode中运行

    我正在使用XCode4和iOSSDK4.3.我的调试工作正常,但现在我发现在设置断点时,应用暂停,但XCode不关注编辑器中的行.点击断点时也不会显示绿色箭头.我有一个在AppDelegate中分配的UINavigationController.当我在didFinishLaunchingWithOptions中设置断点时,一切都按预期工作:这是当一个断点暂停了mapViewController中的

  6. ios – 如何/是否在Xcode中制作通用故事板

    在Xcode中创建故事板文件时,您必须选择是否适用于iPhone或iPad.这意味着应始终将iPhone和iPadUI放入单独的故事板中.这是真的?我的应用程序有多个故事板.虽然Main.storyboard文件在iPhone和iPad之间存在很大差异,但其他故事板几乎完全相同.唯一的区别可能是推动iPhone与iPhone上的popover,可以通过编程方式处理.制作两个故事板似乎非常愚蠢和多余.因此,如果制作一个“通用”故事板,是否应该在Xcode中选择iPhone或iPad?

  7. ios – 如何使用Objective C类中的多个参数调用Swift函数?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  8. ios – Swift 4添加手势:覆盖vs @objc

    我想在我的视图中添加一个手势,如下所示:但是,在Swift4中,我的编译器给出了以下错误:建议添加@objc以将此实例方法公开给Objective-C.实现此目的的另一个选项将覆盖touchesBegan()函数并使用它来处理点击.我试图以“Swift”的方式做到这一点,而不必带入Obj-C.有没有纯粹的Swift方式来添加这个轻击手势而不使用@objc?

  9. xcode – 如何通过LLDB命令行添加断点操作?

    如果你从Xcode编辑一个断点,有一个超级有用的选项,可以添加一个“Action”,以便在每次遇到断点时自动执行.如何从LLDB命令行添加此类操作?

  10. XCode:将故事板导出到图像

    我有一个小问题……

随机推荐

  1. 【cocos2d-x 3.x 学习笔记】对象内存管理

    Cocos2d-x的内存管理cocos2d-x中使用的是上面的引用计数来管理内存,但是又增加了一些自己的特色。cocos2d-x中通过Ref类来实现引用计数,所有需要实现内存自动回收的类都应该继承自Ref类。下面是Ref类的定义:在cocos2d-x中创建对象通常有两种方式:这两中方式的差异可以参见我另一篇博文“对象创建方式讨论”。在cocos2d-x中提倡使用第二种方式,为了避免误用第一种方式,一般将构造函数设为protected或private。参考资料:[1]cocos2d-x高级开发教程2.3节[

  2. 利用cocos2dx 3.2开发消灭星星六如何在cocos2dx中显示中文

    由于编码的不同,在cocos2dx中的Label控件中如果放入中文字,往往会出现乱码。为了方便使用,我把这个从文档中获取中文字的方法放在一个头文件里面Chinese.h这里的tex_vec是cocos2dx提供的一个保存文档内容的一个容器。这里给出ChineseWords,xml的格式再看看ChineseWord的实现Chinese.cpp就这样,以后在需要用到中文字的地方,就先include这个头文件然后调用ChineseWord函数,获取一串中文字符串。

  3. 利用cocos2dx 3.2开发消灭星星七关于星星的算法

    在前面,我们已经在GameLayer中利用随机数初始化了一个StarMatrix,如果还不知道怎么创建星星矩阵请回去看看而且我们也讲了整个游戏的触摸事件的派发了。

  4. cocos2dx3.x 新手打包APK注意事项!

    这个在编译的时候就可以发现了比较好弄这只是我遇到的,其他的以后遇到再补充吧。。。以前被这两个问题坑了好久

  5. 利用cocos2dx 3.2开发消灭星星八游戏的结束判断与数据控制

    如果你看完之前的,那么你基本已经拥有一个消灭星星游戏的雏形。开始把剩下的两两互不相连的星星消去。那么如何判断是GameOver还是进入下一关呢。。其实游戏数据贯穿整个游戏,包括星星消除的时候要加到获得分数上,消去剩下两两不相连的星星的时候的加分政策等,因此如果前面没有做这一块的,最好回去搞一搞。

  6. 利用cocos2dx 3.2开发消灭星星九为游戏添加一些特效

    needClear是一个flag,当游戏判断不能再继续后,这个flag变为true,开始消除剩下的星星clearSumTime是一个累加器ONE_CLEAR_TIME就是每颗星星消除的时间2.连击加分信息一般消除一次星星都会有连击信息和加多少分的信息。其实这些combo标签就是一张图片,也是通过控制其属性或者runAction来实现。源码ComboEffect.hComboEffect.cpp4.消除星星粒子效果消除星星时,为了实现星星爆裂散落的效果,使用了cocos2d提供的粒子特效引擎对于粒子特效不了

  7. 02 Cocos2D-x引擎win7环境搭建及创建项目

    官网有搭建的文章,直接转载记录。环境搭建:本文介绍如何搭建Cocos2d-x3.2版本的开发环境。项目创建:一、通过命令创建项目前面搭建好环境后,怎样创建自己的Cocos2d-x项目呢?先来看看Cocos2d-x3.2的目录吧这就是Cocos2d-x3.2的目录。输入cocosnew项目名–p包名–lcpp–d路径回车就创建成功了例如:成功后,找到这个项目打开proj.win32目录下的Hello.slnF5成功了。

  8. 利用cocos2dx 3.2开发消灭星星十为游戏添加音效项目源码分享

    一个游戏,声音也是非常的重要,其实cocos2dx里面的简单音效引擎的使用是非常简单的。我这里只不过是用一个类对所有的音效进行管理罢了。Audio.hAudio.cpp好了,本系列教程到此结束,第一次写教程如有不对请见谅或指教,谢谢大家。最后附上整个项目的源代码点击打开链接

  9. 03 Helloworld

    程序都有一个入口点,在C++就是main函数了,打开main.cpp,代码如下:123456789101112131415161718#include"main.h"#include"AppDelegate.h"#include"cocos2d.h"USING_NS_CC;intAPIENTRY_tWinMain{UNREFERENCED_ParaMETER;UNREFERENCED_ParaMETER;//createtheapplicationinstanceAppDelegateapp;return

  10. MenuItemImage*图标菜单创建注意事项

    学习cocos2dx,看的是cocos2d-x3.x手游开发实例详解,这本书错误一大把,本着探索求知勇于发现错误改正错误的精神,我跟着书上的例子一起调试,当学习到场景切换这个小节的时候,出了个错误,卡了我好几个小时。

返回
顶部