cocos2dx开发中的lua继承与覆盖C++方法

发表评论 阅读评论

前段时间开始学习cocos2d-x,后面会陆续写一些cocos2d-x方面的东西。由于cocos2d-x支持lua脚本绑定,所以我们的项目是C++与lua混用,要求运行时效率高及内存占用少的部分使用C++,而那些需要动态更新的部分使用lua来写。
lua虽说是脚本语言,但也可以实现类(class)和继承的概念。具体到cocos2d-x,是用{cocos2d-x根目录}/samples/Lua/TestLua/Resources/luaScript/extern.lua里的class方法来实现的。

刚开始写lua的时候不知道可以直接创建一个lua类继承C++(C++对象在lua里都是userdata),就在lua对象里方一个layer或者node属性来访问userdata对象。
类似于下面的代码:

local MyLayer = class("MyLayer")
function MyLayer:ctor()
    self.layer = CCLayer:create()
end
return MyLayer

这样的代码,每次添加到显示对象的时候,都不能直接addChild,必须要addChild(myLayer.layer)才行,很麻烦,后来问了老大才知道原来lua类可以直接继承C++类(userdada)的,于是就把上面的代码改成了下面的样子。

local MyLayer = class("MyLayer", function()
    return CCLayer:create()
end)
return MyLayer

这样就可以直接addChild(myLayer),并且可以直接调用基类CCLayer的方法,很方便。

今天用lua写了一个动态的组件,显示的时候总是会动啊动的,隐藏的时候当然要让它停下来以节省资源,毫无疑问,我需要为其添加start()stop()两个方法。在设置visible的时候显示的调用下start和stop方法就可以啦。但是老大说这个不方便,直接在调用setVisible的时候在组件内部start或者stop,经过仔细研究lua继承C++类(userdata)的class函数,并测试后终于搞定了lua类覆盖C++基类的方法,原理就是构造的时候先保存C++方法的指针,后面调用时候就跟C++中一样。
示例代码如下:

local _setVisible = nil
local MyLayer = class("MyLayer", function()
    local layer = CCLayer:create()
    -- save c++ member method point.
    _setVisible = layer.setVisible
    return layer
end)
-- override CCLayer::setVisible
function MyLayer:setVisible(visible)
    -- invoke CCLayer::setVisible
    _setVisible(self, visible)
    -- to do something.
end
return MyLayer

之前使用metatable来调用C++方法的时候出现了点问题就以为不能使用metatable的方式调用,~~后面再次测试后发现是可以正常使用的~~。相对于先保存引用的方式,通过metatable调用的方式更简单些。

经过再次研究代码后发现通过metatable调用C++类成员函数有限制,因为只有在lua绑定(pkg文件)的时候明确定义了的成员函数才会直接出现在metatable中,而C++的基类里的成员函数在其metatable的metatable中,继承关系越深,需要查找的metatable越多。
示例代码如下:

-- 获取基类的某个方法
-- table C++类或者lua table
-- methodName 函数名,也可以是成员变量名
-- return 基类的函数或成员变量值(如果methodName为变量名)
--          nil 表示找不到
local function getSuperMethod(table, methodName)
    local mt = getmetatable(table)
    local method = nil
    while mt and not method do
        method = mt[methodName]
        if not method then
            local index = mt.__index
            if index and type(index) == "function" then
                method = index(mt, methodName)
            elseif index and type(index) == "table" then
                method = index[methodName]
            end
        end
        mt = getmetatable(mt)
    end
    return method
end

local MyLayer = class("MyLayer", function()
    return CCLayer:create()
end)
-- override CCLayer::setVisible
function MyLayer:setVisible(visible)
    -- invoke CCLayer::setVisible
    getSuperMethod(self, "setVisible")(self, visible)
    -- to do something.
end
return MyLayer

  1. lite3 | | #1

    @aaaa
    lua中的局部变量不能跨文件的,所以require后返回的就是MyLayer了,尽量不要直接用全局变量,以后代码会变的很乱。

  2. aaaa | #2

    提示这句是怎么回事呢?attempt to index global 'MyLayer' (a nil value)

  3. lite3 | | #3

    @iArsenal
    我没看到,你直接加我QQ吧。 735486078

  4. iArsenal | #4

    @lite3
    我在你的QQ邮箱里留言并附上我的的代码,求解答,可能是我漏了哪步?

  5. lite3 | | #5

    @iArsenal
    不应该的,CCLayer 和CCSprite在lua绑定这块没有什么区别。

  6. iArsenal | #6

    @lite3
    Layer没有被释放,而将继承CCLayer改成CCNode或CCSprite,都会成功,但若是用CCLayer,CCScene,CCMenu等就会出错了,太神奇了!

  7. lite3 | | #7

    @iArsenal
    应该是Layer被回收了。

  8. iArsenal | #8

    请问,最后一步那里,getmetatable(self)获取为nil,请问是怎么回事?

  9. lite3 | | #9

    @yu
    cocos2dx中有CCDrawNode这个类,是专门用来画线的,没有绑定到lua,你自己绑定到lua就可以用了。

  10. yu | #10

    @lite3
    谢谢你的解惑, 还想问下, 在lua里面,如果画线?
    我找了好多办法,貌似都只能在draw里面画线..但是lua又不能重载draw

  11. lite3 | | #11

    @yu
    通过该方法重载的所有方法仅能被lua调用,而你说的setPosition有效,必定是通过lua调用的。而draw方法通常是由c++来调用的,所以对draw方法的重载无效。
    也就是说,lua重载只能被lua调用,c++调用不到lua的方法。

  12. yu | #12

    楼主你好, 继承ccsprite之后, 重载setPosition 有效, 但是我重载draw为什么无效呢?

  13. lite3 | | #13

    @N4_118
    为什么要使用多重继承呢,通常多重继承可以使用包含来实现的。

  14. lite3 | | #14

    @N4_118
    lua里不支持多重继承的。

  15. N4_118 | | #15

    @lite3
    请问要怎么实现多重继承,这个class可以做到吗?

  16. N4_118 | | #16

    @lite3
    请问怎么实现多重继承呢?

  17. lite3 | | #17

    @N4_118
    在{cocos2d-x根目录}/samples/Lua/TestLua/Resources/luaScript/extern.lua里

  18. N4_118 | | #18

    请问这个“Class”方法的实现代码是什么?

  19. lite3 | | #19

    @woo
    我没有遇到过无法找到CCScene的问题。是不是项目搞坏了,建议重开个新的,看看有没有。

  20. woo | #20

    我最近在自己的COCOS2DX项目中想加入LUA脚本,也遇到了在LUA里无法找到CCScene的ERROR,想请问你是怎么解决的?

  1. 本文目前尚无任何 trackbacks 和 pingbacks.
回到顶部