cocos2d-x lua test

有事要出门办,所以先发代码,回来详细说明。

local layerFarm     -- 农场层
local menuPopup     -- 弹出式菜单
local spriteDog     -- 小狗的 sprite
local pointBegin    -- 触摸农场层时的起始位置
local winSize = cocos2d.CCDirector:sharedDirector():getWinSize()

-- 农场层的触摸事件处理函数
function btnTouchMove(e)
    cocos2d.CCLuaLog("btnTouchMove")
    if pointBegin ~= nil then
        local v = e[1]
        local pointMove = v:locationInView(v:view())
        pointMove = cocos2d.CCDirector:sharedDirector():convertToGL(pointMove)

        local positionCurrent = layerFarm.__CCNode__:getPosition()
        local x = positionCurrent.x + pointMove.x - pointBegin.x
        local y = positionCurrent.y + pointMove.y - pointBegin.y
        layerFarm.__CCNode__:setPosition(cocos2d.CCPoint(x, y))
        pointBegin = pointMove
    end
end

function btnTouchBegin(e)
    for k,v in ipairs(e) do
        pointBegin = v:locationInView(v:view())
        pointBegin = cocos2d.CCDirector:sharedDirector():convertToGL(pointBegin)
        cocos2d.CCLuaLog("btnTouchBegin")
    end
end

function btnTouchEnd(e)
    cocos2d.CCLuaLog("btnTouchEnd")
    pointBegin = nil
end


-- 关闭弹出菜单
function menuCallbackClosePopup()
    menuPopup:setIsVisible(false)
end

-- 显示弹出菜单
function menuCallbackOpenPopup()
    menuPopup:setIsVisible(true)
end

-- 移动小狗位置
function tick()
    local point = spriteDog:getPosition();

    if point.x > winSize.width then
        point.x = 0
        spriteDog:setPosition(point)
    else
        point.x = point.x + 1
        spriteDog:setPosition(point)
    end
end

-- 创建农场层
local function createLayerFarm()
    local layer = cocos2d.CCLayer:node()
    layer:setIsTouchEnabled(true)

    -- 设置农场背景

    local spriteFarm = cocos2d.CCSprite:spriteWithFile("farm.jpg")
    spriteFarm:setPosition(cocos2d.CCPoint(winSize.width/2 + 80, winSize.height/2))
    layer:addChild(spriteFarm)

    for i=0,3,1 do
        for j=0,1,1 do
            local spriteLand = cocos2d.CCSprite:spriteWithFile("land.png")
            layer:addChild(spriteLand)
            spriteLand:setPosition(cocos2d.CCPoint(200+j*180 - i%2*90, 10+i*95/2))
        end
    end

    -- add crop

    for i=0,3,1 do
        for j=0,1,1 do
            local textureCrop = cocos2d.CCTextureCache:sharedTextureCache():addImage("crop.png")
            local rect = cocos2d.CCRectMake(0, 0, 105, 95)
            local frameCrop = cocos2d.CCSpriteFrame:frameWithTexture(textureCrop, rect)
            local spriteCrop = cocos2d.CCSprite:spriteWithSpriteFrame(frameCrop);
            layer:addChild(spriteCrop)
            spriteCrop:setPosition(cocos2d.CCPoint(10+200+j*180 - i%2*90, 30+10+i*95/2))
        end
    end

    -- 注册农场层的触摸事件
    layer.__CCTouchDelegate__:registerScriptTouchHandler(cocos2d.CCTOUCHBEGAN, "btnTouchBegin")
    layer.__CCTouchDelegate__:registerScriptTouchHandler(cocos2d.CCTOUCHMOVED, "btnTouchMove")
    layer.__CCTouchDelegate__:registerScriptTouchHandler(cocos2d.CCTOUCHENDED, "btnTouchEnd")

    -- 添加正在移动的 dog
    local FrameWidth = 105
    local FrameHeight = 95

    local textureDog = cocos2d.CCTextureCache:sharedTextureCache():addImage("dog.png")
    local rect = cocos2d.CCRectMake(0, 0, FrameWidth, FrameHeight)
    local frame0 = cocos2d.CCSpriteFrame:frameWithTexture(textureDog, rect)
    rect = cocos2d.CCRectMake(FrameWidth*1, 0, FrameWidth, FrameHeight)
    local frame1 = cocos2d.CCSpriteFrame:frameWithTexture(textureDog, rect)

    spriteDog = cocos2d.CCSprite:spriteWithSpriteFrame(frame0)
    spriteDog:setPosition(cocos2d.CCPoint(0, winSize.height/4*3))
    layer:addChild(spriteDog)

    local animFrames = cocos2d.CCMutableArray_CCSpriteFrame__:new(2)
    animFrames:addObject(frame0)
    animFrames:addObject(frame1)

    local animation = cocos2d.CCAnimation:animationWithName("wait", 0.5, animFrames)
    local animate = cocos2d.CCAnimate:actionWithAnimation(animation, false);
    spriteDog:runAction(cocos2d.CCRepeatForever:actionWithAction(animate))

    -- 让小狗移动
    cocos2d.CCScheduler:sharedScheduler():scheduleScriptFunc("tick", 0.01, false)

    return layer
end


-- 创建菜单层
local function createLayerMenu()
    local layer = cocos2d.CCLayer:node()

    local menuPopupItem = cocos2d.CCMenuItemImage:itemFromNormalImage("menu2.png", "menu2.png")
    menuPopupItem:setPosition( cocos2d.CCPoint(0, 0) )
    menuPopupItem:registerScriptHandler("menuCallbackClosePopup")

    menuPopup = cocos2d.CCMenu:menuWithItem(menuPopupItem)
    menuPopup:setPosition( cocos2d.CCPoint(winSize.width/2, winSize.height/2) )
    menuPopup:setIsVisible(false)
    layer:addChild(menuPopup)

    -- add the left-bottom "tools" menu to invoke menuPopup
    local menuToolsItem = cocos2d.CCMenuItemImage:itemFromNormalImage("menu1.png", "menu1.png")
    menuToolsItem:setPosition( cocos2d.CCPoint(0, 0) )
    menuToolsItem:registerScriptHandler("menuCallbackOpenPopup")
    local menuTools = cocos2d.CCMenu:menuWithItem(menuToolsItem)
    menuTools:setPosition( cocos2d.CCPoint(30, 40) )
    layer:addChild(menuTools)

    return layer
end


-- 创建场景,并将农场层和菜单层添加到场景中
local function createScene()
    layerFarm = createLayerFarm()

    scene = cocos2d.CCScene:node()
    scene:addChild(layerFarm)
    scene:addChild(createLayerMenu())

    return scene
end

-- 开始执行
local sceneGame = createScene()
cocos2d.CCDirector:sharedDirector():runWithScene(sceneGame)

基本思路就是尽量避免全局变量(全局变量如果不仔细清理会导致内存泄漏)和全局函数。

Thank you for your work.
I think it is better to write in English.
Then you can share the work to all over the world.

我的英语太烂,所以只能用中文了 :slight_smile:

我的修改主要有两个目的:
1、避免全局变量
2、避免全局函数

在 lua 中,在某个函数里面以 local 定义的变量,在该函数结束执行时会自动标记为不再使用,
从而被垃圾收集器回收掉。而全局变量除非明确指定其值为 nil,否则是不会被垃圾收集器回收的。
因此全局变量很容易造成内存泄露。

至于全局函数,其问题则在于命名污染,这和 C 里面的问题一样。而且全局函数有时候还需要依赖
全局变量才能正常工作,参考第一贴中 btnTouchMove() 等函数对 pointBegin 变量的依赖。


我修改后的代码,虽然变量都以 local 定义,但实际上仍然会存在于整个应用程序的生命周期。
要彻底消除“全局”变量,就必须消除 cocos2d-x 对 lua 全局函数的依赖。

cocos2d-x 对全局函数的依赖,根源在于 cocos2d-x 无法识别“函数类型”的变量,因此对于
registerScriptTouchHandler()、scheduleScriptFunc() 等方法,只能通过函数名字符串
来查找需要回调的 lua 函数。

解决此问题我个人认为有个比较好的途径:在需要回调函数的地方,用对象代替函数名。

假设下列代码:

local function createLayer()
    local layer = cocos2d.CCLayer:node()

    function layer:touchMove(event)
        ....
    end

    function layer:touchBegin(event)
        ....
    end

    function layer:touchEnd(event)
        ....
    end

    function layer:clean()
        layer.__CCTouchDelegate__:unregisterScriptTouchHandler(cocos2d.CCTOUCHBEGAN, layer)
    end

    layer.__CCTouchDelegate__:registerScriptTouchHandler(cocos2d.CCTOUCHBEGAN, layer)
    return layer
end

上述代码构造了 layer 对象后,为 layer 对象添加了 touchMove() 等方法。
当触摸事件发生时,cocos2d-x 就会调用 layer:touchBegin()、layer:touchMove() 等方法。

由于 layer 是在一个函数中定义的非全局变量,因此我们可以在需要的时候用下列代码彻底清理 layer:

local myLayer = createLayer()

....

myLayer:clean()
myLayer = nil

要实现这样的效果,我想对 cocos2d-x 做一些小修改应该就可以办到。

感谢感谢!