标签:des android style blog http color io os java
cocos2d-x lua binds c++ class, class functions ,enum and some global functions to lua through auto-binding by bindings-generator(tools/bindings-generator) and some manual binding,so we can call custom c++ from lua conveniently.
Open the lua-empty-test/src/hello.lua
file and we will find many function calls like cc.***
. They are actually calling the class member function.Let we see the initGLView
function.
local function initGLView()
local director = cc.Director:getInstance()
local glView = director:getOpenGLView()
if nil == glView then
glView = cc.GLViewImpl:create("Lua Empty Test")
director:setOpenGLView(glView)
end
director:setOpenGLView(glView)
glView:setDesignResolutionSize(480, 320, cc.ResolutionPolicy.NO_BORDER)
director:setDisplayStats(true)
director:setAnimationInterval(1.0 / 60)
end
The relationship between the lua function call and the c++ function call as follow:
| lua | c++ |
| cc.Director:getInstance() | cocos2d::Director::getInstance() |
| director:getOpenGLView() | director->getOpenGLView |
| cc.GLViewImpl:create("Lua Empty Test") | cocos2d::GLViewImpl::create("Lua Empty Test") |
| glView:setDesignResolutionSize(480, 320, cc.ResolutionPolicy.NO_BORDER) | glview->glView:setDesignResolutionSize(480, 320, ResolutionPolicy::NO_BORDER)|
From this table,we can see that the functions called in the lua are very similar with the functions called in the c++, and there are some key points that we need to pay attention to as follow:
cc
is a moudle name like namespace name in c++,it is cocos2d-x 3.0 new features.The relation between the lua modules and c++ namespaces as follow:| Lua module name | c++ namespace |
| cc | cocos2d,cocos2d::extension,CocosDenshion,cocosbuilder |
| ccui | cocos2d::ui |
| ccs | cocostudio,cocostudio::timeline |
| sp | spine |
| ccexp | cocos2d::experimental,cocos2d::experimental::ui |
:
cc.ResolutionPolicy.NO_BORDER
corresponds to ResolutionPolicy::NO_BORDER
which is enum value in the c++.Owing to faulty of current bindings-generator, enum values are bound to lua by manual.Different moudles have different lua file to keep the bindings value,the details as follows: | moudle name | const value files |
| cc | Cocos2dConstants.lua, ExtensionConstants.lua, NetworkConstants.lua|
| ccui | GuiConstants.lua |
| ccs | StudioConstants.lua|
| ccexp | experimentalUIConstants.lua|
void Node::setPosition(const Vec2& position)
In C++, we should call this function like this:
nodeObj->setPosition(Vec2(0.0f, 0.0f))
In lua, we should call this function like this:
nodeObj:setPosition(cc.p(0.0, 0.0))
cc.p(0.0, 0.0) is to construct an anonymous table like this {x = 0, y =0}
The other parametric types should be converted are listed as follow:
| parametric types | lua conversional format |
| cocos2d::Point | {x = numValue, y = numValue} |
| cocos2d::Vec3 | {x = numValue, y = numValue, z = numValue} |
| cocos2d::Vec4 | {x = numValue, y = numValue, z = numValue, w = numValue} |
| cocos2d::Rect | {x = numValue, y = numValue, width = numValue, height = numValue} |
| cocos2d::Size | {width = numValue, height = numValue} |
| cocos2d::Color4B | {r = numValue, g = numValue, b = numValue, a = numValue} |
| cocos2d::Color4F | {r = numValue, g = numValue, b = numValue, a = numValue} |
| cocos2d::Color3B | {r = numValue, g = numValue, b = numValue} |
| cocos2d::PhysicsMaterial | {density = numValue, restitution = numValue, friction = numValue} |
| cocos2d::AffineTransform | {a = numValue, b = numValue, c = numValue, d = numValue, tx = numValue, ty = numValue} |
| cocos2d::FontDefinition | {fontName = stringValue, fontSize = numValue, fontAlignmentH = numValue, fontAlignmentV = numValue, fontFillColor = {r = numValue, g = numValue, b = numValue}, fontDimensions = {width = numValue, height = numValue}, shadowEnabled = boolValue[,shadowOffset = {width = numValue, height = numValue}, shadowBlur = numValue, shadowOpacity = numValue], strokeEnabled = boolValue[,strokeColor = {r = numValue, g = numValue, b = numValue}, strokeSize = numValue]} |
| cocos2d::Vector | {objValue1,objValue2,...,objValuen,...}|
| cocos2d::Map<std::string, T>| {key1 = objValue1, key2 = objValue2,..., keyn = objValuen,...} |
| cocos2d::Value | {objValue1,objValue2,...,objValuen,...} or key1 = objValue1, key2 = objValue2,..., keyn = objValuen,...} or stringValue or boolValue or numValue |
| cocos2d::ValueMap | {key1 = Value1, key2 = Value2,..., keyn = Valuen,...} |
| cocos2d::ValueMapIntKey | {numKey1 = Value1, intKey2 = Value2, ...,intKeyn = Valuen,...} |
| cocos2d::ValueVector | {Value1, Value2, ..., Valuen, ...} |
| std::vector<string> | {stringValue1, stringValue2, ..., stringValuen, ...} |
| std::vector<int> | {numValue1, numValue2, ..., numValuen, ...} |
| std::vector<float> | {numValue1, numValue2, ..., numValuen, ...} |
| std::vector<unsigned short> | {numValue1, numValue2, ..., numValuen, ...} |
| cocos2d::Mat4 | {numValue1,numValue2,..., numValue16} |
| cocos2d::TTFConfig |{fontFilePath = stringValue, fontSize = numValue, glyphs = numValue, customGlyphs = stringValue, distanceFieldEnabled = boolValue, outlineSize = numValue}
| cocos2d::MeshVertexAttrib| {size = numValue, type = numValue, vertexAttrib = numValue, vertexAttrib =numValue} |
| cocos2d::BlendFunc | { src = numValue, dst = numValue} |
cocos2d-x v3 also binds some global functions to lua by manual, such as kmGLPushMatrix
, kmGLTranslatef
and kmGLLoadMatrix
etc.We can call these global function in the lua as follows:
kmGLPopMatrix()
cocos2d-x v3 binds some OpenGL functions to Lua.All the OpenGL functions are in the gl
moudle,and we can call OpenGL functions in the lua like this:
local glNode = gl.glNodeCreate()
glNode:setContentSize(cc.size(256, 256))
glNode:setAnchorPoint(cc.p(0.5, 0.5))
uniformCenter = gl.getUniformLocation(program,"center")
uniformResolution = gl.getUniformLocation( program, "resolution")
You can refer to lua-tests/DrawPrimitiveTest
and lua-tests/OpenGLTest
for more information.
From what has been discussed above, you can find that calling c++ functions from lua is conveniently, and you can refer to lua-tests
for more detail information.
Since cocos2d-x v3.0,we use bindings-generator to bind c++ class to lua automatically.
The bindings-generator is based on tolua++, you could config the ini file in the tools/tolua
directory and then run the genbindings.py script to generate the binding code. By using this method, it greatly reduce the configuration work of writing pkg files which used in cococ2d-x v2.
Next,let‘s take a custom class as an example to show how to use the bindings-generator to bind a c++ class to lua
Here is the code snippets:
// CustomClass.h
#ifndef __CUSTOM__CLASS
#define __CUSTOM__CLASS
#include "cocos2d.h"
namespace cocos2d {
class CustomClass : public cocos2d::Ref
{
public:
CustomClass();
~CustomClass();
static cocos2d::CustomClass* create();
bool init();
CREATE_FUNC(CustomClass);
};
} //namespace cocos2d
#endif // __CUSTOM__CLASS
Note:
removeScriptObjectByObject
to reduce reference count of userdata which create in the c++ automaticly to avoid the memory leak.Navigate to the tools/lua folder and create a new file named cocos2dx_custom.ini,the detail of cocos2dx_custom.ini is as follows:
[cocos2dx_custom]
# the prefix to be added to the generated functions. You might or might not use this in your own
# templates
prefix = cocos2dx_custom
# create a target namespace (in javascript, this would create some code like the equiv. to `ns = ns
# all classes will be embedded in that namespace
target_namespace = cc
android_headers = -I%(androidndkdir)s/platforms/android-14/arch-arm/usr/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.7/libs/armeabi-v7a/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.7/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.8/libs/armeabi-v7a/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.8/include
android_flags = -D_SIZE_T_DEFINED_
clang_headers = -I%(clangllvmdir)s/lib/clang/3.3/include
clang_flags = -nostdinc -x c++ -std=c++11 -U __SSE__
cocos_headers = -I%(cocosdir)s/cocos -I%(cocosdir)s/my -I%(cocosdir)s/cocos/2d -I%(cocosdir)s/cocos/base -I%(cocosdir)s/cocos/ui -I%(cocosdir)s/cocos/physics -I%(cocosdir)s/cocos/2d/platform -I%(cocosdir)s/cocos/2d/platform/android -I%(cocosdir)s/cocos/math/kazmath -I%(cocosdir)s/extensions -I%(cocosdir)s/external -I%(cocosdir)s/cocos/editor-support -I%(cocosdir)s
cocos_flags = -DANDROID -DCOCOS2D_JAVASCRIPT
cxxgenerator_headers =
# extra arguments for clang
extra_arguments = %(android_headers)s %(clang_headers)s %(cxxgenerator_headers)s %(cocos_headers)s %(android_flags)s %(clang_flags)s %(cocos_flags)s %(extra_flags)s
# what headers to parse
headers = %(cocosdir)s/cocos/my/CustomClass.h
# what classes to produce code for. You can use regular expressions here. When testing the regular
# expression, it will be enclosed in "^$", like this: "^Menu*$".
classes = CustomClass.*
# what should we skip? in the format ClassName::[function function]
# ClassName is a regular expression, but will be used like this: "^ClassName$" functions are also
# regular expressions, they will not be surrounded by "^$". If you want to skip a whole class, just
# add a single "*" as functions. See bellow for several examples. A special class name is "*", which
# will apply to all class names. This is a convenience wildcard to be able to skip similar named
# functions from all classes.
skip =
rename_functions =
rename_classes =
# for all class names, should we remove something when registering in the target VM?
remove_prefix =
# classes for which there will be no "parent" lookup
classes_have_no_parents =
# base classes which will be skipped when their sub-classes found them.
base_classes_to_skip =
# classes that create no constructor
# Set is special and we will use a hand-written constructor
abstract_classes =
# Determining whether to use script object(js object) to control the lifecycle of native(cpp) object or the other way around. Supported values are ‘yes‘ or ‘no‘.
script_control_cpp = no
All of the config files under tools/tolua
are the same format. Here is the list which you should care when writing your own ini file:
cc
as the module name, when you want to use CustomClass
in the lua, you must put a prefix named cc
in front of the name. For example, the CustomClass
could be reference as cc.CustomClass
.tools/tolua/cocos2dx.ini
.skip: To config the functions needed to be omit. Now the bindings-generator can‘t parse void*
type and also the delegate type, so these types needed to be bind manually. And at this circumstance, you should omit all these types first and then to bind them manually. You could ref to the config files under path cocos/scripting/lua-bindings/auto
.
rename_functions: To config the functions need to be renamed in the scripting layer. Due to some reasons, developers want more scripting friendly API, so the config option is for this purpose.
rename_classes: Not used any more.
remove_prefix: Not used any more.
classes_have_no_parents: To config the parent class needed to be filter. This option is seldom modified.
abstract_classes: To config the classes whose public constructor don‘t need to be exported.
script_control_cpp:yes. To config whether the scripting layer manage the object life time or not. If no, then the c++ layer cares about their life time. Now, it is imperfect to control native object‘s life time in scripting layer. So you could simply leave it to no.
Sometimes, C++ classes‘s lua bindings aren‘t satisfied with our requirements,so we want to add some new function to extend the bindings like inheritance mechanism of C++. Through class(classname, super)
function in the cocos/scripting/lua-bindings/script/cocos2d/extern.lua
,we can realize this requirement easily.The details function are as follow:
function class(classname, super)
local superType = type(super)
local cls
if superType ~= "function" and superType ~= "table" then
superType = nil
super = nil
end
if superType == "function" or (super and super.__ctype == 1) then
-- inherited from native C++ Object
cls = {}
if superType == "table" then
-- copy fields from super
for k,v in pairs(super) do cls[k] = v end
cls.__create = super.__create
cls.super = super
else
cls.__create = super
end
cls.ctor = function() end
cls.__cname = classname
cls.__ctype = 1
function cls.new(...)
local instance = cls.__create(...)
-- copy fields from class to native object
for k,v in pairs(cls) do instance[k] = v end
instance.class = cls
instance:ctor(...)
return instance
end
else
-- inherited from Lua Object
if super then
cls = clone(super)
cls.super = super
else
cls = {ctor = function() end}
end
cls.__cname = classname
cls.__ctype = 2 -- lua
cls.__index = cls
function cls.new(...)
local instance = setmetatable({}, cls)
instance.class = cls
instance:ctor(...)
return instance
end
end
return cls
end
Through this function,we can realize the inheritance mechanism easily.For example,if we want to derive from cc.Node
,the steps we should do are as follow
class
functionlocal SubNode = class("SubNode",function()
return cc.Node:create()
end)
--This functin like the construtor of c++ class
function SubNode:ctor()
-- do initialized
end
function SubNode:addSprite(filePath)
local sprite = cc.Sprite:create(filePath)
sprite:setPosition(cc.p(0, 0))
self:addChild(sprite)
end
...
local node = SubNode.new()
node:addSprite("xxx.jpg")
Note:
new
is implemented defautly in the class
function.Because the type of second parameter of class
we passed is function
,so when we call new
, the final realization is as follow:funtion SubNode.new(...)
local instance = cc.Node:create()
-- copy fields from SubNode to native object
for k,v in pairs(SubNode) do instance[k] = v end
instance.class = SubNode
instance:ctor(...)
return instance
end
So, the object which created by new
have all the property and behavior of the cc.Node
.In the meanwhile,it also have the property of SubNode
.
SubNode
is derived from cc.Node
, so it also can override the functions of cc.Node
. For example:function SubNode:setPostion(x,y)
print(string.format("x = %0.2f, y = %0.2f"), x, y)
end
If we still want to call the function of the same name of super class, you can call it as follows:
getmetatable(SubNode):setPosition(x, y)
Cocos2d-x v3.x use memory management and garbage collection of lua itself,except the release of userdata
.If the corresponding classes are derived from Ref
class, the release of userdata
is managed in the c++ by control the two table in the register table named toluafix_refid_ptr_mapping
and tolua_value_root
.
We will give a simple but typical test case to explain this mechanism through modifying the lua-empty-test\hello.lua
.
createDog
function local testSprite = cc.Sprite:create("res/land.png")
tick
function as follow: testSprite:getPosition()
cocos2d: [LUA-print] stack traceback:
[string "src/hello.lua"]:13: in function <[string "src/hello.lua"]:10>
[C]: in function ‘getPosition‘
[string "src/hello.lua"]:98: in function <[string "src/hello.lua"]:88>
cocos2d: [LUA-print] ----------------------------------------
cocos2d: [LUA-print] ----------------------------------------
cocos2d: [LUA-print] LUA ERROR: [string "src/hello.lua"]:98: invalid ‘self‘ in function ‘tolua_cocos2d_Node_getPosition‘
This error triggered is because the testsprite didn‘t add to any other node as a child after created,so the corresponding c++ object was released at the end of a frame.
For the details of memory management from create to release, We should analyze from several aspects as follows:
In the CCRef.h
, we could see the usage of CC_ENABLE_SCRIPT_BINDING
.
#if CC_ENABLE_SCRIPT_BINDING
public:
/// object id, ScriptSupport need public _ID
unsigned int _ID;
/// Lua reference id
int _luaID;
/// scriptObject, support for swift
void* _scriptObject;
#endif
We should notice the _ID
and _luaID
,these two class members is very important when push a Ref object to lua by calling toluafix_pushusertype_ccobject
to store a key-value table named toluafix_refid_ptr_mapping
in the registry.the _ID
is key and the related c++ object pointer is value. The related code fragment in the toluafix_pushusertype_ccobject
is as follows:
//Extract from `toluafix_pushusertype_ccobject` in the tolua_fix.cpp
lua_pushstring(L, TOLUA_REFID_PTR_MAPPING);
lua_rawget(L, LUA_REGISTRYINDEX); /* stack: refid_ptr */
lua_pushinteger(L, refid); /* stack: refid_ptr refid */
lua_pushlightuserdata(L, vPtr); /* stack: refid_ptr refid ptr */
lua_rawset(L, -3); /* refid_ptr[refid] = ptr, stack: refid_ptr */
lua_pop(L, 1); /* stack: - */
Note:
TOLUA_REFID_PTR_MAPPING
is macro definition represent for "toluafix_refid_ptr_mapping"LUA_REGISTRYINDEX
is definition of Pseudo-Index
for registry of Luarefid
is value of _ID
vPtr
is value of related c++ object pointerLike the upper test,when we called cc.Sprite:create("res/land.png")
in the lua,the related c++ processing flow is as follow:
cocos2d::Sprite::create("res/land.png")
by the lua bindings to create a Sprite object and push it into the lua stack,the related codes are as follows: //Extract from `lua_cocos2dx_Sprite_create` in the lua_cocos2dx_auto.cpp
std::string arg0;
ok &= luaval_to_std_string(tolua_S, 2,&arg0, "cc.Sprite:create");
if (!ok) { break; }
cocos2d::Sprite* ret = cocos2d::Sprite::create(arg0);
object_to_luaval<cocos2d::Sprite>(tolua_S, "cc.Sprite",(cocos2d::Sprite*)ret);
return 1;
toluafix_pushusertype_ccobject
when push created object to lua stack //Extract from `object_to_luaval` in the LuaBasicConversions.h
if (std::is_base_of<cocos2d::Ref, T>::value)
{
// use c style cast, T may not polymorphic
cocos2d::Ref* dynObject = (cocos2d::Ref*)(ret);
int ID = (int)(dynObject->_ID) ;
int* luaID = &(dynObject->_luaID);
toluafix_pushusertype_ccobject(L,ID, luaID, (void*)ret,type);
}
In the toluafix_pushusertype_ccobject
,we will use two tables named "toluafix_refid_ptr_mapping" and "toluafix_refid_type_mapping" in the lua‘s registry to store the two key-value pairs about _ID
-object pointer
and _ID
-object type name
.The details are as follow:
//Extract from `toluafix_pushusertype_ccobject` in the tolua_fix.cpp
if (*p_refid == 0)
{
*p_refid = refid;
lua_pushstring(L, TOLUA_REFID_PTR_MAPPING);
lua_rawget(L, LUA_REGISTRYINDEX); /* stack: refid_ptr */
lua_pushinteger(L, refid); /* stack: refid_ptr refid */
lua_pushlightuserdata(L, vPtr); /* stack: refid_ptr refid ptr */
lua_rawset(L, -3); /* refid_ptr[refid] = ptr, stack: refid_ptr */
lua_pop(L, 1); /* stack: - */
lua_pushstring(L, TOLUA_REFID_TYPE_MAPPING);
lua_rawget(L, LUA_REGISTRYINDEX); /* stack: refid_type */
lua_pushinteger(L, refid); /* stack: refid_type refid */
lua_pushstring(L, vType); /* stack: refid_type refid type */
lua_rawset(L, -3); /* refid_type[refid] = type, stack: refid_type */
lua_pop(L, 1); /* stack: - */
//printf("[LUA] push CCObject OK - refid: %d, ptr: %x, type: %s\n", *p_refid, (int)ptr, type);
}
tolua_pushusertype_internal
to determine whether to create a new userdata, or just update the userdata.void tolua_pushusertype_internal (lua_State* L, void* value, const char* type, int addToRoot)
{
if (value == NULL)
lua_pushnil(L);
else
{
luaL_getmetatable(L, type); /* stack: mt */
if (lua_isnil(L, -1)) { /* NOT FOUND metatable */
lua_pop(L, 1);
return;
}
lua_pushstring(L,"tolua_ubox");
lua_rawget(L,-2); /* stack: mt ubox */
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
lua_pushstring(L, "tolua_ubox");
lua_rawget(L, LUA_REGISTRYINDEX);
};
lua_pushlightuserdata(L,value); /* stack: mt ubox key<value> */
lua_rawget(L,-2); /* stack: mt ubox ubox[value] */
if (lua_isnil(L,-1))
{
lua_pop(L,1); /* stack: mt ubox */
lua_pushlightuserdata(L,value);
*(void**)lua_newuserdata(L,sizeof(void *)) = value; /* stack: mt ubox value newud */
lua_pushvalue(L,-1); /* stack: mt ubox value newud newud */
lua_insert(L,-4); /* stack: mt newud ubox value newud */
lua_rawset(L,-3); /* ubox[value] = newud, stack: mt newud ubox */
lua_pop(L,1); /* stack: mt newud */
/*luaL_getmetatable(L,type);*/
lua_pushvalue(L, -2); /* stack: mt newud mt */
lua_setmetatable(L,-2); /* update mt, stack: mt newud */
#ifdef LUA_VERSION_NUM
lua_pushvalue(L, TOLUA_NOPEER); /* stack: mt newud peer */
lua_setfenv(L, -2); /* stack: mt newud */
#endif
}
else
{
/* check the need of updating the metatable to a more specialized class */
lua_insert(L,-2); /* stack: mt ubox[u] ubox */
lua_pop(L,1); /* stack: mt ubox[u] */
lua_pushstring(L,"tolua_super");
lua_rawget(L,LUA_REGISTRYINDEX); /* stack: mt ubox[u] super */
lua_getmetatable(L,-2); /* stack: mt ubox[u] super mt */
lua_rawget(L,-2); /* stack: mt ubox[u] super super[mt] */
if (lua_istable(L,-1))
{
lua_pushstring(L,type); /* stack: mt ubox[u] super super[mt] type */
lua_rawget(L,-2); /* stack: mt ubox[u] super super[mt] flag */
if (lua_toboolean(L,-1) == 1) /* if true */
{
lua_pop(L,3); /* mt ubox[u]*/
lua_remove(L, -2);
return;
}
}
/* type represents a more specilized type */
/*luaL_getmetatable(L,type); // stack: mt ubox[u] super super[mt] flag mt */
lua_pushvalue(L, -5); /* stack: mt ubox[u] super super[mt] flag mt */
lua_setmetatable(L,-5); /* stack: mt ubox[u] super super[mt] flag */
lua_pop(L,3); /* stack: mt ubox[u] */
}
lua_remove(L, -2); /* stack: ubox[u]*/
if (0 != addToRoot)
{
lua_pushvalue(L, -1);
tolua_add_value_to_root(L, value);
}
}
}
We use a table named ubox
to store key-value pairs about userdata
and objec pointer
.This table would be used when the destruction of the object.
tolua_add_value_to_root
to add a reference count for userdata
in the lua by the tolua_value_root
table in the lua registry.The mechanism will make the object in the lua wouldn‘t collected by lua gc.For example: local node = cc.Node:create()
node.extendValue = 10000
nodeParent:addChild(node, 0 , 9999)
These codes create a node object and extend the attribute of the node object dynamically by the lua‘s feature.Whenwe want to get this node and its extended attribute somewhere, we can do as follow:
local child = lnodeParent:getChildByTag(9999)
print(child.extendValue)
If we don‘t call the tolua_add_value_to_root
,the result of print(child.extendValue)
would be uncertain.sometimes the result would be 10000,and sometimes it would be nil
.Because we wouldn‘t controll the lua automatic gc effectively,when lua gc think there is no other place have the references for this userdata,it will collect this userdata.When we call getChildByTag to get a node object,it would be create a new userdata and the extend attribute would disapper certainly. So,we add a reference count for the userdata tolua_value_root
table in the lua registry in the c++ to avoid generating this error.
When calling the desturctor of Ref, it would trigger the release of the userdata.
In the destructor of Ref, we could see the following codes:
#if CC_ENABLE_SCRIPT_BINDING
// if the object is referenced by Lua engine, remove it
if (_luaID)
{
ScriptEngineManager::getInstance()->getScriptEngine()->removeScriptObjectByObject(this);
}
...
#endif
Note:After we push a c++ object to lua,the related _luaID would be not 0,so we can call removeScriptObjectByObject
The removeScriptObjectByObject
called would trigger the call of toluafix_remove_ccobject_by_refid
, and this function would call some lua c APIs to operate the table like toluafix_refid_ptr_mapping
、toluafix_refid_type_mapping
and tolua_value_root
table in the registry.
The specific implementation of toluafix_remove_ccobject_by_refid
is as follows:
TOLUA_API int toluafix_remove_ccobject_by_refid(lua_State* L, int refid)
{
void* ptr = NULL;
const char* type = NULL;
void** ud = NULL;
if (refid == 0) return -1;
// get ptr from tolua_refid_ptr_mapping
lua_pushstring(L, TOLUA_REFID_PTR_MAPPING);
lua_rawget(L, LUA_REGISTRYINDEX); /* stack: refid_ptr */
lua_pushinteger(L, refid); /* stack: refid_ptr refid */
lua_rawget(L, -2); /* stack: refid_ptr ptr */
ptr = lua_touserdata(L, -1);
lua_pop(L, 1); /* stack: refid_ptr */
if (ptr == NULL)
{
lua_pop(L, 1);
// Lua stack has closed, C++ object not in Lua.
// printf("[LUA ERROR] remove CCObject with NULL ptr, refid: %d\n", refid);
return -2;
}
// remove ptr from tolua_refid_ptr_mapping
lua_pushinteger(L, refid); /* stack: refid_ptr refid */
lua_pushnil(L); /* stack: refid_ptr refid nil */
lua_rawset(L, -3); /* delete refid_ptr[refid], stack: refid_ptr */
lua_pop(L, 1); /* stack: - */
// get type from tolua_refid_type_mapping
lua_pushstring(L, TOLUA_REFID_TYPE_MAPPING);
lua_rawget(L, LUA_REGISTRYINDEX); /* stack: refid_type */
lua_pushinteger(L, refid); /* stack: refid_type refid */
lua_rawget(L, -2); /* stack: refid_type type */
if (lua_isnil(L, -1))
{
lua_pop(L, 2);
printf("[LUA ERROR] remove CCObject with NULL type, refid: %d, ptr: %p\n", refid, ptr);
return -1;
}
type = lua_tostring(L, -1);
lua_pop(L, 1); /* stack: refid_type */
// remove type from tolua_refid_type_mapping
lua_pushinteger(L, refid); /* stack: refid_type refid */
lua_pushnil(L); /* stack: refid_type refid nil */
lua_rawset(L, -3); /* delete refid_type[refid], stack: refid_type */
lua_pop(L, 1); /* stack: - */
// get ubox
luaL_getmetatable(L, type); /* stack: mt */
lua_pushstring(L, "tolua_ubox"); /* stack: mt key */
lua_rawget(L, -2); /* stack: mt ubox */
if (lua_isnil(L, -1))
{
// use global ubox
lua_pop(L, 1); /* stack: mt */
lua_pushstring(L, "tolua_ubox"); /* stack: mt key */
lua_rawget(L, LUA_REGISTRYINDEX); /* stack: mt ubox */
};
// cleanup root
tolua_remove_value_from_root(L, ptr);
lua_pushlightuserdata(L, ptr); /* stack: mt ubox ptr */
lua_rawget(L,-2); /* stack: mt ubox ud */
if (lua_isnil(L, -1))
{
// Lua object has released (GC), C++ object not in ubox.
//printf("[LUA ERROR] remove CCObject with NULL ubox, refid: %d, ptr: %x, type: %s\n", refid, (int)ptr, type);
lua_pop(L, 3);
return -3;
}
// cleanup peertable
lua_pushvalue(L, LUA_REGISTRYINDEX);
lua_setfenv(L, -2);
ud = (void**)lua_touserdata(L, -1);
lua_pop(L, 1); /* stack: mt ubox */
if (ud == NULL)
{
printf("[LUA ERROR] remove CCObject with NULL userdata, refid: %d, ptr: %p, type: %s\n", refid, ptr, type);
lua_pop(L, 2);
return -1;
}
// clean userdata
*ud = NULL;
lua_pushlightuserdata(L, ptr); /* stack: mt ubox ptr */
lua_pushnil(L); /* stack: mt ubox ptr nil */
lua_rawset(L, -3); /* ubox[ptr] = nil, stack: mt ubox */
lua_pop(L, 2);
//printf("[LUA] remove CCObject, refid: %d, ptr: %x, type: %s\n", refid, (int)ptr, type);
return 0;
}
The steps are as follows:
Get related object pointer stored in the toluafix_refid_ptr_mapping
table by the value of _luaID
,then store it
Remove reference relationship of the object pointer from toluafix_refid_ptr_mapping
table by _luID
Get related type name stored in the tolua_refid_type_mapping
table by the value of _luaID
,then store it
Remove reference relationship of type name from tolua_refid_type_mapping
table by _luID
Get the related metatable by the type name
Get the ubox
table
Remove reference relationship of userdata from tolua_value_root
table by the object pointer got in the upper step
Clean userdata and remove reference relationship of uesrdata from ubox
by the object pointer got in the upper step.Note:To destroy a object cited by lua, we only called ‘*ud = NULL;‘
Through the above steps,the refernce relationships in the toluafix_refid_ptr_mapping
、tolua_refid_type_mapping
and tolua_refid_type_mapping
table in the registry would be removed, release the userdata
which is created when push c++ object to lua stack, and when lua gc trigger, the related object would be collected if there is no other place refer to it.
Cocos2dx have been used toluafix_refid_function_mapping
table in the registry to manage the gc of lua callback function
When we define a lua function which would be called throuch c++ codes, we whould store the pointer of this function in the toluafix_refid_function_mapping
table by calling toluafix_ref_function
function in the tolua_fix.cpp
.Cocos2d-x bound a series of functions like registerScriptHandler
and addEventListener
to finish this work.
Let‘s use registerScriptHandler
of Node
as a sample,we could use it as follows in lua:
local function onNodeEvent(event)
if "enter" == event then
--do something
end
end
nodeObject:registerScriptHandler(onNodeEvent)
The related bindings funtion is named tolua_cocos2d_Node_registerScriptHandler
in the lua_cocos2dx_manual.cpp
,the most important sections are as follows:
LUA_FUNCTION handler = toluafix_ref_function(tolua_S,2,0);
ScriptHandlerMgr::getInstance()->addObjectHandler((void*)self, handler, ScriptHandlerMgr::HandlerType::NODE);
toluafix_ref_function
is implemented to store the related function pointer into toluafix_refid_function_mapping
table in the registry with a staic variable named s_function_ref_id
.This operation makes the lua function avoid being collected by lua gc because that toluafix_refid_function_mapping
table have a refrence of this function. The details are as follow:TOLUA_API int toluafix_ref_function(lua_State* L, int lo, int def)
{
// function at lo
if (!lua_isfunction(L, lo)) return 0;
s_function_ref_id++;
lua_pushstring(L, TOLUA_REFID_FUNCTION_MAPPING);
lua_rawget(L, LUA_REGISTRYINDEX); /* stack: fun ... refid_fun */
lua_pushinteger(L, s_function_ref_id); /* stack: fun ... refid_fun refid */
lua_pushvalue(L, lo); /* stack: fun ... refid_fun refid fun */
lua_rawset(L, -3); /* refid_fun[refid] = fun, stack: fun ... refid_ptr */
lua_pop(L, 1); /* stack: fun ... */
return s_function_ref_id;
}
addObjectHandler
is used to stored the map of object pointer and pair of s_function_ref_id and handler type.If lua callback funcion become useless, we should remove the refrence in the toluafix_refid_function_mapping
table in the registry. Cocos2d-x provided the toluafix_remove_function_by_refid
function to realize it.This function could be called by removeScriptHandler
of LuaStack
、removeScriptHandler
of LuaEngine
or directly.The details are as follows:
TOLUA_API void toluafix_remove_function_by_refid(lua_State* L, int refid)
{
lua_pushstring(L, TOLUA_REFID_FUNCTION_MAPPING);
lua_rawget(L, LUA_REGISTRYINDEX); /* stack: ... refid_fun */
lua_pushinteger(L, refid); /* stack: ... refid_fun refid */
lua_pushnil(L); /* stack: ... refid_fun refid nil */
lua_rawset(L, -3); /* refid_fun[refid] = nil, stack: ... refid_fun */
lua_pop(L, 1); /* stack: ... */
}
Note:
refid
is the corresponding value of s_function_ref_id
.ScriptHandlerMgr::getInstance()->removeObjectAllHandlers
to remove all the refrence funtion relationship which added by the ScriptHandlerMgr::getInstance()->addObjectHandler
automaticly //Extract from `lua_cocos2dx_TextureCache_addImageAsync` in the lua_cocos2dx_manual.cpp
LUA_FUNCTION handler = ( toluafix_ref_function(tolua_S, 3, 0));
self->addImageAsync(configFilePath, [=](Texture2D* tex){
int ID = (tex) ? (int)tex->_ID : -1;
int* luaID = (tex) ? &tex->_luaID : nullptr;
toluafix_pushusertype_ccobject(tolua_S, ID, luaID, (void*)tex, "cc.Texture2D");
LuaEngine::getInstance()->getLuaStack()->executeFunctionByHandler(handler,1);
LuaEngine::getInstance()->removeScriptHandler(handler);
});
By the mechanism of the lambda, we could get the value of handler which represents the corresponding value of s_function_ref_id
,so when we finish calling the lua callback function,we could call LuaEngine::getInstance()->removeScriptHandler(handler)
directly to remove the refrence of the lua callback function.
In this section, we explain that how to process some refrence tables in the c++ to manager the release of memory.These mechanisms don‘t break the orgin mechanism of lua gc.
Cocos Code IDE is tool that can debug a lua script,it has windows and mac version.you can debug Windows and Anroid lua games through the windows version, and you can debug Mac, iOS and android lua games through the Mac version. Now we will demonstrate how to use Cocos Code IDE to debug a lua game based on the mac version,the process of the windows version is almost the same.
If you have been not installed the Cocos Code IDE,you can refer to Cocos Code IDE Installation.
Click Cocos Code IDE/Preferences
to open the congiuration dialog,then select the Cocos/Lua
to set the directory of cocos2d-x v3.x in the Lua Frameworks
:
You should set directory of some compliling tools about android if you need to replace the Android runtime which Cocos Code IDE provided.Click Cocos Code IDE/Preferences
then pitch on Cocos
to configurate the directory of related tools:
Lua Projects Explorer
src/GameScene.lua
and open it,then toggle breakpoint by right click menu or double click3.Click debug button on toolbar
4.Trigger the breakpoint,select "Yes" to open Debug Perspective
,and you will find many useful debug views like Call stacks
, Variables
and Breakpoints
,etc.
5.Use Step over
, Step into
, Step out
in the tool bar to debug
We could realize the hot updating of lua code when debugging by the Cocos Code IDE.
If you want to change the moving path of dog int the src/GameScene.lua, you can modify the "tick()" funcion to control the dog‘s position
local function tick()
if spriteDog.isPaused then return end
local x, y = spriteDog:getPosition()
if x > self.origin.x + self.visibleSize.width then
x = self.origin.x
else
x = x + 1
end
spriteDog:setPositionX(x)
end
Modify the implementation of function, for example, change the value 1 to 10 and save your change. Then you will find that you have improved the speed of SpriteDog without restarting the app!
The above example is executed on the Mac platform because of the default configuration of Cocos Code IDE.If you debug on the other target platforms,you should modify Debug Configurations
.
Debug Configurations
button on the toolbar to open Debug Configurations
dialogCheck iOS Simulator radio button
Choose a runtime app
Click the Debug button,IDE will auto-install chosen runtime app and start runtime to debug
1.You need a runtime IPA, you can build a custom runtime IPA by Cocos Code IDE, then install runtime IPA to iOS device.
Click Build Runtime
on the toolbars
Click Yes
button on the pop-up Cocos
dialog
Click Generate
button to Generate Create Native Source Wizard
Click Close
button to finish Create Native Source Wizard
Click Build Runtime
on the toolbars to open Runtime Builder Wizard
dialog
Check Build iOS Device Runtime
and click Generate
button to generate
Click Close
button when Finished dialog pop up
Debug Configuration
,then check Remote Debug
radio button on the Debug Configuration
dialogiOS
platformTarget IP
and Fill the IP address that your PC used on the Host IP
(Make sure that the Target IP
and Host IP
can access each other)Click ‘Debug‘ button to begin to debug
Build Runtime
like first of Debug on the iOS Device
Android ADB Mode
radio buttonDebug
buttonDebug Configuration
,then check Remote Debug
radio button on the Debug Configuration
dialogTarget IP
and Fill the IP address that your PC used on the Host IP
(Make sure that the Target IP
and Host IP
can access each other)标签:des android style blog http color io os java
原文地址:http://www.cnblogs.com/Androider123/p/3991315.html