存档

2013年3月 的存档

红孩儿工具箱使用介绍2:切图处理

2013年3月30日 没有评论

红孩儿工具箱使用介绍2:切图处理


红孩儿工具箱之切图介绍:
功能清单:
(1)将大图载入显示。
(2)增加切块
(3)编辑切块
(4)保存切块
(5)导出切图信息PLIST文件。


实例:
以图1为例:

红孩儿工具箱使用介绍2:切图处理

第一步:在界面上点击“切图/拼图切换”,进入切图界面:
红孩儿工具箱使用介绍2:切图处理
第二步:点击“浏览图片文件”打开大图:

红孩儿工具箱使用介绍2:切图处理

第三步:在图片树项上右键弹出菜单“增加新切块”
红孩儿工具箱使用介绍2:切图处理

第四步:选中新创建的切块
红孩儿工具箱使用介绍2:切图处理

第五步:拖拉切块的边框进行调整,或输入数值后确定。

红孩儿工具箱使用介绍2:切图处理
第六步:如此反复操作至所有切块都编辑好

红孩儿工具箱使用介绍2:切图处理
第七步:导出
红孩儿工具箱使用介绍2:切图处理

第八步:查看导出的PLIST
红孩儿工具箱使用介绍2:切图处理

分类: 未分类 标签:

红孩儿工具箱使用介绍1:拼图处理

2013年3月30日 没有评论

红孩儿工具箱使用介绍1:拼图处理

       红孩儿工具箱是本博主为建立快速化移动开发流程而做的一款集成工具产品,即将在近日发布。其设计拥有强大的功能,高度的集成效果。今天起我来为大家介绍一下此款软件的目前情况,也算是王婆卖瓜,自卖自夸下吧!

 

首先,是登录界面:
基本功能:
(1)新版本下载
(2)最新视频教程
(3)注册用户
(4)免费版离线登录
(5)输入游戏兔子账号和密码进行登录
红孩儿工具箱登录界面截图展示:


红孩儿工具箱使用介绍1:拼图处理


 然后我们来看一下拼图功能:


功能列表:
(1)对多级目录进行批量化生成拼合图及PLIST。
(2)对多级目录进行右键菜单一键生成拼合图及PLIST。
(3)统计拼合图内存节省容量。

主界面展示:

红孩儿工具箱使用介绍1:拼图处理


实例介绍:
对一个目录中的图片进行拼合图生成:
比如下面的目录:
红孩儿工具箱使用介绍1:拼图处理


第一步:浏览源图片目录:

红孩儿工具箱使用介绍1:拼图处理


第二步:详细设置导出项,查看小图,游览导出目录。

红孩儿工具箱使用介绍1:拼图处理


第三步:点击立即导出

红孩儿工具箱使用介绍1:拼图处理


第四步:查看拼图边框

红孩儿工具箱使用介绍1:拼图处理


结束!

附加功能:

(1)最小化到托盘并返回主界面。


红孩儿工具箱使用介绍1:拼图处理


红孩儿工具箱使用介绍1:拼图处理


(2)一键操作:
先在主界面上点击“将拼较加入资源管理器右键菜单”这一项
然后即可以对目录右键菜单中进行处理:

红孩儿工具箱使用介绍1:拼图处理


(3)修改导出文件名,即对左边树项的图片名称树项,右键弹出菜单修改名称即可。



分类: 未分类 标签:

Cocos2d-x setAnimationInterval doesn't work on Android

2013年3月29日 没有评论

I try to set the max FPS in my application in Cocos2d-x with the following code:

CCDirector::sharedDirector()->setAnimationInterval(1.0 / 30);

It’s working on iOS, but when I test it on three Android devices it’s ignored, and renders frames with the standard interval (1/60).

How can I properly set the max FPS on Android using cocos2d-x?

So I’ve actually managed to kind of achieve it. You have to edit the Cocos2dxRenderer.java file and then clean and rebuild Cocos2d-x.

Here is the code :

public void onDrawFrame(final GL10 gl) {

     // FPS controlling algorithm is not accurate, and it will slow down FPS
     // on some devices. So comment FPS controlling code.



    try {
        if (loopRuntime < 40) {
            Log.wtf("RENDERER", "Sleeping for == " + (40 - loopRuntime));
            Thread.sleep(40 - loopRuntime);
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    //final long nowInNanoSeconds = System.nanoTime();
    //final long interval = nowInNanoSeconds - this.mLastTickInNanoSeconds;

            loopStart = System.currentTimeMillis();

    // should render a frame when onDrawFrame() is called or there is a
    // "ghost"
    Cocos2dxRenderer.nativeRender();

    loopEnd = System.currentTimeMillis();
    loopRuntime = (loopEnd - loopStart);

    Log.wtf("RENDERER", "loopRunTime == " + loopRuntime);

    // fps controlling
    /*if (interval < Cocos2dxRenderer.sAnimationInterval) {
        try {
            // because we render it before, so we should sleep twice time interval
            Thread.sleep((Cocos2dxRenderer.sAnimationInterval - interval) / Cocos2dxRenderer.NANOSECONDSPERMICROSECOND);
        } catch (final Exception e) {
        }
    }*/

    //this.mLastTickInNanoSeconds = nowInNanoSeconds;
}

Weird thing is that when I uncommented the fps control parts that were there it did nothing, and when I wrote my version it does…
Anyway, the “magic” value of 40 there gives about 35fps, but you could of course easily change it to work with values passed through setAnimationInterval();

EDIT : I moved the loopStart line to after the sleep -> the sleep time shouldn’t be included in loopTime.

That is a werid thing.But if you have two scenes play together and you are making both scenes 30 fps, although pDirector->getAnimationInterval()returns interval of 1/30,but you still get 60+ fps.

This code works fine …

found from http://discuss.cocos2d-x.org/t/setanimationinterval-does-nothing-on-android/6419/4

private long renderingElapsedTime;


@Override

public void onDrawFrame(final GL10 gl) 

{

/*
 * FPS controlling algorithm is not accurate, and it will slow down FPS
 * on some devices. So comment FPS controlling code.
 */

try {
    if (renderingElapsedTime * NANOSECONDSPERMICROSECOND < Cocos2dxRenderer.sAnimationInterval) {
        Thread.sleep((Cocos2dxRenderer.sAnimationInterval - renderingElapsedTime * NANOSECONDSPERMICROSECOND) / NANOSECONDSPERMICROSECOND);
    }
} catch (InterruptedException e) {
    e.printStackTrace();
}

/*
final long nowInNanoSeconds = System.nanoTime();
final long interval = nowInNanoSeconds - this.mLastTickInNanoSeconds;
*/

// Get the timestamp when rendering started
long renderingStartedTimestamp = System.currentTimeMillis();

// should render a frame when onDrawFrame() is called or there is a
// "ghost"
Cocos2dxRenderer.nativeRender();

// Calculate the elapsed time during rendering
renderingElapsedTime = (System.currentTimeMillis() - renderingStartedTimestamp);

/*
// fps controlling
if (interval < Cocos2dxRenderer.sAnimationInterval) {
    try {
        // because we render it before, so we should sleep twice time interval
        Thread.sleep((Cocos2dxRenderer.sAnimationInterval - interval) / Cocos2dxRenderer.NANOSECONDSPERMICROSECOND);
    } catch (final Exception e) {
    }
}

this.mLastTickInNanoSeconds = nowInNanoSeconds;
*/

}

Hope it helps

分类: cocos2d, stackoverflow精选 标签:

Cocos2d-x HelloLua 介绍

2013年3月28日 没有评论

猴子原创,欢迎转载。转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢!

原文地址: http://www.cocos2dev.com/?p=393


Cocos2d-x HelloLua 介绍Cocos2d-x HelloLua 介绍

最近网游使用lua做更新比较火,最近也有人问我能否写下lua的文章。刚新建了一个lua工程,看了下代码,就干脆先介绍下HelloLua吧。边看代码边写的注释,应该基本都写了。


-- for CCLuaEngine traceback 输出绑定执行函数发生错误的信息
function __G__TRACKBACK__(msg)
    print("----------------------------------------")
    print("LUA ERROR: " .. tostring(msg) .. "/n")
    print(debug.traceback())
    print("----------------------------------------")
end

local function main()
    -- avoid memory leak 设置脚本内存回收参数 避免内存泄露
    collectgarbage("setpause", 100)
    collectgarbage("setstepmul", 5000)

    -- 就是local function cclog(...) 定义局部Log函数
    local cclog = function(...)
        print(string.format(...))
    end

    -- 类似c++的include,会检查是否重复引入
    require "hello2"

    -- 调用外部函数,在hello2.lua中
    cclog("result is " .. myadd(3, 5))

    ---------------

    -- 获取可视区域
    local visibleSize = CCDirector:sharedDirector():getVisibleSize()
    -- 可视原点坐标 OpenGL坐标系 左下角为原点
    local origin = CCDirector:sharedDirector():getVisibleOrigin()

    -- add the moving dog 创建小松鼠
    local function creatDog()

        -- 每一帧尺寸设置,local表示局部变量
        local frameWidth = 105
        local frameHeight = 95

        -- create dog animate 加载动画资源并创建精灵帧
        -- 加载精灵动画所在纹理
        local textureDog = CCTextureCache:sharedTextureCache():addImage("dog.png") 
        -- 设置第一帧帧区域
        local rect = CCRectMake(0, 0, frameWidth, frameHeight)
        -- 创建第一帧精灵Frame
        local frame0 = CCSpriteFrame:createWithTexture(textureDog, rect)
        -- 设置第二帧帧区域
        rect = CCRectMake(frameWidth, 0, frameWidth, frameHeight)
        -- 创建第二帧精灵Frame
        local frame1 = CCSpriteFrame:createWithTexture(textureDog, rect)

        -- 基于使用第一帧Frame创建Sprite对象
        local spriteDog = CCSprite:createWithSpriteFrame(frame0)
        spriteDog.isPaused = false
        spriteDog:setPosition(origin.x, origin.y + visibleSize.height / 4 * 3)

        -- 将上面创建的两帧生成一个帧数组(这个松鼠一共就2帧)
        local animFrames = CCArray:create()

        animFrames:addObject(frame0)
        animFrames:addObject(frame1)

        -- 根据帧序列数组创建一个动画animation。帧间隔时间delay等于0.5秒
        local animation = CCAnimation:createWithSpriteFrames(animFrames, 0.5)
        -- 根据动画animation创建动作实例
        local animate = CCAnimate:create(animation);
        -- 松鼠精灵执行该动作
        spriteDog:runAction(CCRepeatForever:create(animate))

        -- moving dog at every frame 用来更新松鼠的位置,后面会调用该函数
        local function tick()
            if spriteDog.isPaused then return end
            local x, y = spriteDog:getPosition()
            if x > origin.x + visibleSize.width then
                x = origin.x
            else
                x = x + 1
            end

            spriteDog:setPositionX(x)
        end

        -- 生成一个schedule,每帧执行tick函数
        CCDirector:sharedDirector():getScheduler():scheduleScriptFunc(tick, 0, false)

        return spriteDog
    end

    -- create farm 创建地面的农场
    local function createLayerFarm()
        -- 创建一个新的Layer用作农场管理
        local layerFarm = CCLayer:create()

        -- add in farm background 添加农场背景图
        local bg = CCSprite:create("farm.jpg")
        bg:setPosition(origin.x + visibleSize.width / 2 + 80, origin.y + visibleSize.height / 2)
        layerFarm:addChild(bg)

        -- add land sprite 添加地免砖块
        for i = 0, 3 do
            for j = 0, 1 do
                local spriteLand = CCSprite:create("land.png")
                spriteLand:setPosition(200 + j * 180 - i % 2 * 90, 10 + i * 95 / 2)
                layerFarm:addChild(spriteLand)
            end
        end

        -- add crop 添加庄稼,注意crop.png是多张图的合成贴图,所以只取了里面的部分贴图
        local frameCrop = CCSpriteFrame:create("crop.png", CCRectMake(0, 0, 105, 95))
        for i = 0, 3 do
            for j = 0, 1 do
                local spriteCrop = CCSprite:createWithSpriteFrame(frameCrop);
                spriteCrop:setPosition(10 + 200 + j * 180 - i % 2 * 90, 30 + 10 + i * 95 / 2)
                layerFarm:addChild(spriteCrop)
            end
        end

        -- add moving dog 调用上面的creatDog()方法,创建一个移动的松鼠
        local spriteDog = creatDog()
        layerFarm:addChild(spriteDog)

        -- handing touch events 手指触摸事件处理
        local touchBeginPoint = nil

        -- 手指点击开始
        local function onTouchBegan(x, y)
            cclog("onTouchBegan: %0.2f, %0.2f", x, y)
            touchBeginPoint = {x = x, y = y} -- 保存点击位置
            spriteDog.isPaused = true -- 将松鼠暂停移动
            -- CCTOUCHBEGAN event must return true 这里必须返回ture,否则后续touch事件无法接收
            return true
        end

        -- 手指按住移动
        local function onTouchMoved(x, y)
            cclog("onTouchMoved: %0.2f, %0.2f", x, y)
            if touchBeginPoint then
                -- 将整个农场层拖动,因为之前已经将农场里面所有对象加入在layerFarm
                local cx, cy = layerFarm:getPosition()
                layerFarm:setPosition(cx + x - touchBeginPoint.x,
                                      cy + y - touchBeginPoint.y)
                touchBeginPoint = {x = x, y = y}
            end
        end

        -- 手指离开
        local function onTouchEnded(x, y)
            cclog("onTouchEnded: %0.2f, %0.2f", x, y)
            touchBeginPoint = nil -- 点击位置数据清空
            spriteDog.isPaused = false -- 回复松鼠移动
        end

        -- touch事件的接收函数
        local function onTouch(eventType, x, y)
            if eventType == "began" then   
                return onTouchBegan(x, y)
            elseif eventType == "moved" then
                return onTouchMoved(x, y)
            else
                return onTouchEnded(x, y)
            end
        end

        -- 注册touch事件
        layerFarm:registerScriptTouchHandler(onTouch)
        layerFarm:setTouchEnabled(true)

        return layerFarm
    end


    -- create menu 创建界面菜单
    local function createLayerMenu()
        -- 创建一个新的CCLayer管理所有菜单
        local layerMenu = CCLayer:create()

        local menuPopup, menuTools, effectID

        -- 点击菜单回调函数
        local function menuCallbackClosePopup()
            -- stop test sound effect 关闭音效
            SimpleAudioEngine:sharedEngine():stopEffect(effectID)
            menuPopup:setVisible(false) -- 隐藏菜单
        end

        -- 点击菜单回调函数
        local function menuCallbackOpenPopup()
            -- loop test sound effect 打开音效
            local effectPath = CCFileUtils:sharedFileUtils():fullPathForFilename("effect1.wav")
            effectID = SimpleAudioEngine:sharedEngine():playEffect(effectPath)
            menuPopup:setVisible(true) -- 显示菜单
        end

        -- add a popup menu 创建弹出的菜单面板
        local menuPopupItem = CCMenuItemImage:create("menu2.png", "menu2.png")
        menuPopupItem:setPosition(0, 0)
        menuPopupItem:registerScriptTapHandler(menuCallbackClosePopup) -- 注册点击回调地址
        menuPopup = CCMenu:createWithItem(menuPopupItem)
        menuPopup:setPosition(origin.x + visibleSize.width / 2, origin.y + visibleSize.height / 2)
        menuPopup:setVisible(false)
        layerMenu:addChild(menuPopup)

        -- add the left-bottom "tools" menu to invoke menuPopup 左下角的工具按钮,用来弹出菜单面板
        local menuToolsItem = CCMenuItemImage:create("menu1.png", "menu1.png")
        menuToolsItem:setPosition(0, 0)
        menuToolsItem:registerScriptTapHandler(menuCallbackOpenPopup) -- 注册点击回调地址
        menuTools = CCMenu:createWithItem(menuToolsItem)
        local itemWidth = menuToolsItem:getContentSize().width
        local itemHeight = menuToolsItem:getContentSize().height
        menuTools:setPosition(origin.x + itemWidth/2, origin.y + itemHeight/2)
        layerMenu:addChild(menuTools)

        return layerMenu
    end

    -- play background music, preload effect 

    -- uncomment below for the BlackBerry version
    -- local bgMusicPath = CCFileUtils:sharedFileUtils():fullPathForFilename("background.ogg")
    local bgMusicPath = CCFileUtils:sharedFileUtils():fullPathForFilename("background.mp3")
    SimpleAudioEngine:sharedEngine():playBackgroundMusic(bgMusicPath, true) -- 播放背景音乐
    local effectPath = CCFileUtils:sharedFileUtils():fullPathForFilename("effect1.wav")
    SimpleAudioEngine:sharedEngine():preloadEffect(effectPath) -- 预加载音效

    -- run
    local sceneGame = CCScene:create() -- 创建场景
    sceneGame:addChild(createLayerFarm()) -- 将农场层加入场景
    sceneGame:addChild(createLayerMenu()) -- 将菜单界面层加入场景
    CCDirector:sharedDirector():runWithScene(sceneGame)
end

--[[
xpcall( 调用函数, 错误捕获函数 );
lua提供了xpcall来捕获异常
xpcall接受两个参数:调用函数、错误处理函数。
当错误发生时,Lua会在栈释放以前调用错误处理函数,因此可以使用debug库收集错误相关信息。
两个常用的debug处理函数:debug.debug和debug.traceback
前者给出Lua的提示符,你可以自己动手察看错误发生时的情况;
后者通过traceback创建更多的错误信息,也是控制台解释器用来构建错误信息的函数。
--]]
xpcall(main, __G__TRACKBACK__)

写得还算详细的,后面会慢慢介绍。

分类: cocos2d, cocos2d-lua 标签:

cocos2d-x 2.x 获取当前动画是第几帧 (修复之前无法获取第几帧问题)

2013年3月27日 没有评论

猴子原创,欢迎转载。转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢!
原文地址: http://www.cocos2dev.com/?p=391

有些人跟我QQ留言说 cocos2d-x获取当前动画是第几帧里面的方法已经不能对比到第几帧了,我看了下代码,找到了这个

CCSpriteFrame* CCSprite::displayFrame(void)
{
    return CCSpriteFrame::createWithTexture(m_pobTexture,
                                           CC_RECT_POINTS_TO_PIXELS(m_obRect),
                                           m_bRectRotated,
                                           CC_POINT_POINTS_TO_PIXELS(m_obUnflippedOffsetPositionFromCenter),
                                           CC_SIZE_POINTS_TO_PIXELS(m_obContentSize));
}

大家看到了吧,到这里估计有人看出来问什么对比不到第几帧了吧。

当你调用displayFrame的时候,它重新生成了一个新的CCSpriteFrame对象给你,你拿着它和原来的作地址对比,肯定不相同,所以为什么不能找到相同的。


那么是不是就没有办法了呢?


想一下,相同帧不就是贴图相同吗?所以看下Texture,果然看到这个

GLuint CCTexture2D::getName()
{
    return m_uName;
}

返回当前Texture的Name,这个name是GLuint,你可以看成它的ID,每个Texture都是唯一的ID。


所以把之前的对比方法稍微修改下就可以了。


下面是修改好之后的

int currentAnimIndex = 0; //精灵当前播放的是第几帧
    for(int i = 0; i < 5; i++)
    {
        //5张图5帧
        GLuint tID = prite->getTexture()->getName();
        CCSpriteFrame* animFrame = (CCSpriteFrame*)anim->getAnimation()->getFrames()->objectAtIndex(i);
        if(tID == animFrame->getTexture()->getName()){
            //这个i返回的只是一个索引,如果帧数是从1开始计算就要+1
            currentAnimIndex = i+1;
        }
    }

现在可以获取到第几帧了,解决起来也不是很难,希望大家遇到问题,可以自己尝试这去找出原因,知道了原因就有解决的办法。

分类: 未分类 标签:

PhoneGap 创建XCode工程

2013年3月27日 没有评论

猴子原创,欢迎转载。转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢!

原文地址: http://www.cocos2dev.com/?p=386


今天有人问我PhoneGap怎么创建iOS程序,我记得去年写过一个教程,可能创建不成功,现在有空,就写下怎么创建工程吧。


PhoneGap是干什么的?有什么好处,我就不说了,不清楚的自己Google下。


一、下载PhoneGap,

http://phonegap.com/download/


下载好压缩包后,解压目录:

PhoneGap 创建XCode工程

二、创建PhoneGap的XCode工程

打开终端,我用的iTerm。cd到你的PhoneGap目录下的lib/ios/bin目录,运行create命令


$ ./create 存放目录 com.cocos2dev.app YourAppName

create命令的参数:

第一个是存放你工程的目录,第二个是BundleIdentifier,第三个是你工程的名字


比如我执行

$ ./create MyPro com.cocos2dev.app TestG


执行之后,在你刚才指定的目录会出现一个xcode工程。

PhoneGap 创建XCode工程

三、运行工程


1、直接用xcode打开创建的工程即可。


2、或者使用PhoneGap提供的命令行编译运行


命令行编译:

进入刚才创建的工程的cordova目录,

$ ./build

出现 ** BUILD SUCCEEDED **  表示编译成功。


运行程序:

$ ./emulate

就可以看到了运行的结果了。

PhoneGap 创建XCode工程

呵呵,放了个老黄历到www目录

分类: 未分类 标签:

《众神召唤HD》 国内版发布了 – 基于cocos2dx

2013年3月27日 没有评论

发布前夕的加班,终于发布了国内版,目前是91内测。


下载地址:http://app.91.com/Soft/iPhone/com.boyojoy.91-1.0-1.0.html


游戏采用的cocos2dx开发,欢迎大家评论吐槽


《众神召唤HD》 是一款结合了MMORPG & SLG玩法的游戏,故事围绕众神之间的战争展开。玩家在游戏中扮演拯救世界的英雄,通过招募伙伴,拓展城堡,升级装备来完成一系列的任务和挑战。


游戏特色:
*完美的界面和音效
*有趣的背景故事和多样化的任务
*强大的联盟系统让玩家得以聚合
*种族和联盟间的宏伟战斗场面
*富有挑战性的地下城副本
*多样化的成就系统


《众神召唤HD》 国内版发布了 - 基于cocos2dx

分类: 未分类 标签:

Estimating memory usage of Cocos2d game

2013年3月22日 没有评论

I am using Cocos2d 2.0 with ARC enabled. My game is a random generated game so I need to load/unload textures( spritesheet-batchnode) within a scene in the middle of the game. I am trying to removespriteframesfromfile and remove texture for key for a spritesheet that i wish to unload in the middle. When I use dumpcachedtextureInfo after unloading the sheet, that spritesheet texture doesnot show up in the logs of dumpcachedtextureinfo.

But when I use Instruments/activity monitor to profile my app, the real memory usage doesnot drop after unloading the texture and spritesheet.

Is there a way where dumpcachedtextureinfo will not show a texture that is still in memory?
Also, My livebytes column in allocations shows 4 MB.

Also as i read at various places that allocations doesnot show texture memory.

So my allocations live bytes column says 4 MB, my total texture memory as shown with logs of dumpcchedtextureinfo is shown to be 84MB, but memory monitor stills shows real memory usage to be 84+Spritesheet size+ Live Bytes

I am not able to understand this difference?

I am not exiting the scene, so I am not sure if ARC is holding the memory or what?

Some useful notes for you

Estimating memory usage of Cocos2d game

Estimating memory usage of Cocos2d game

  • To check memory leaks use: Instrument-> Leaks
    Estimating memory usage of Cocos2d game
分类: cocos2d, stackoverflow精选 标签:

'itemFromNormalSprite:selectedSprite:target:selector:' is deprecated

2013年3月21日 没有评论

Why am I getting this warning and how can I fix it?

I’m using Kobold2D v2.1.0 (Uses cocos2d-iphone v2.1 and OpenGL ES 2.0)

My code:

CCMenuItemSprite *flareButton = [CCMenuItemSprite itemFromNormalSprite:flareSprite selectedSprite:flareSelectedSprite target:self selector:@selector(flareSelected)];

Can you also tell me how I can avoid these deprecations in the future by looking at a reference of any kind. Thanks

Look into ccDeprecated.m – you’ll find all deprecated methods there as well as the correct (changed) way to use the function.

Deprecated in cocos2d means in almost all cases that the name of the function, the parameter list or its location have changed. You can also disable deprecation warnings in Build Settings and just ignore it for at least until the next major cocos2d update.

You can try with:

CCMenuItemSprite *flareButton = [CCMenuItemSprite itemWithNormalSprite:flareSprite selectedSprite:flareSprite target:self selector:@selector(flareSelected)];

Change From to With on itemFromNormalSprite: will be itemWithNormalSprite:

分类: stackoverflow精选, unity3d 标签:

Cocos2d-x之LUA脚本引擎深入分析

2013年3月21日 没有评论
                Cocos2d-x之LUA脚本引擎深入分析

         大家好,又是一周过去了,这一周忙的有点焦头烂额,除了工作照例每天加班到九点外,工具箱又做了大幅改进,新的论坛游戏兔子game2z也上线了,Cocos2d-x的学习时间被压缩的很少了,现在是凌晨一点零六分,看着妻子睡熟的样子,我也只能告诉自已,坚持到底。

 

         好了,不说废话,本周奉上一篇初级入门教程博文,Cocos2d-x中的LUA引导与入门。

做为惯例,一切都是以HelloWorld的样例为准。我们今天学习用LUA来完成一版HelloWorld。

 

         大家既使没有看过我的“HelloWorld 深入分析”一文,想必也无数次运行过Cocos2d-x里的HelloCpp工程,对于运行的结果画面熟烂于心。我们回想一下,这个画面里有什么。嗯,一个背景图精灵,一个文字标签,一个关闭按钮。OK,咱们就做这么个东西。

 

         首先,我们要知道LUA是个什么东西,至于官方怎么说可以百度去查,但我想告诉你的是LUA就是一种可以在不必修改C++代码的情况下实现逻辑处理的手段。稍微讲的再明白一点,就是你用指定语法写一些逻辑处理函数然后保存成文本格式,这个文件称为脚本文件,可以被游戏执行。经过若干年的发展,现在在LUA中写逻辑,除了调用注册到LUA的静态C函数外,也已经可以方便的访问到C++工程中的类的成员函数。这是游戏开发史上最重要的技术之一。其改变了很多设计方案,使游戏变的灵活强大而极具扩展性。

 

         在Cocos2d-x中,有两个类来完成对于LUA脚本文件的处理。

 

1. CCLuaEngine:LUA脚本引擎

 

2. CCScriptEngineManager:脚本引擎管理器。

 

 

CCLuaEngine类的基类是一个接口类,叫做CCScriptEngineProtocol,它规定了所有LUA引擎的功能函数,它和CCScriptEngineManager都存放在libcocos2d下的script_support目录中的CCScriptSupport.h/cpp中。

 

首先我们来看一下CCScriptEngineProtocol:


 

class CC_DLL CCScriptEngineProtocol : public CCObject
{
public:
	//取得LUA的全局指针,所有的LUA函数都需要使用这个指针来做为参数进行调用。
    virtual lua_State* getLuaState(void) = 0;
    
	//通过LUA脚本ID移除对应的CCObject
    virtual void removeCCObjectByID(int nLuaID) = 0;
    
    //通过函数索引值移除对应的LUA函数。
    virtual void removeLuaHandler(int nHandler) = 0;
    
    //将一个目录中的LUA文件加入到LUA资源容器中。
    virtual void addSearchPath(const char* path) = 0;
    
    //执行一段LUA代码
    virtual int executeString(const char* codes) = 0;
    
    //执行一个LUA脚本文件。
    virtual int executeScriptFile(const char* filename) = 0;
    
    //调用一个全局函数。
    virtual int executeGlobalFunction(const char* functionName) = 0;
    
//通过句柄调用函数多种形态。
//通过句柄调用函数,参数二为参数数量。
virtual int executeFunctionByHandler(int nHandler, int numArgs = 0) = 0;
//通过句柄调用函数,参数二为整数数据。
virtual int executeFunctionWithIntegerData(int nHandler, int data) = 0;
//通过句柄调用函数,参数二为浮点数据。
virtual int executeFunctionWithFloatData(int nHandler, float data) = 0;
//通过句柄调用函数,参数二为布尔型数据。
virtual int executeFunctionWithBooleanData(int nHandler, bool data) = 0;
//通过句柄调用函数,参数二为CCObject指针数据和其类型名称。
virtual int executeFunctionWithCCObject(int nHandler, CCObject* pObject, const char* typeName) = 0;    

//将一个整数数值压栈做为参数。
virtual int pushIntegerToLuaStack(int data) = 0;
//将一个浮点数值压栈做为参数。
virtual int pushFloatToLuaStack(int data) = 0;
//将一个布尔数值压栈做为参数。
virtual int pushBooleanToLuaStack(int data) = 0;
//将一个CCObject指针和类型名压栈做为参数。
    virtual int pushCCObjectToLuaStack(CCObject* pObject, const char* typeName) = 0;
    
    // 执行单点触屏事件
virtual int executeTouchEvent(int nHandler, int eventType, CCTouch *pTouch) = 0;
//执行多点触屏事件。
    virtual int executeTouchesEvent(int nHandler, int eventType, CCSet *pTouches) = 0;
    // 执行一个回调函数。
    virtual int executeSchedule(int nHandler, float dt) = 0;
};

 

这个接口类的功能函数的具体实现,我们要参看CCLuaEngine类。

现在我们打开CCLuaEngine.h:

//加入lua的头文件,约定其中代码使用C风格
extern "C" {
#include "lua.h"
}

//相应的头文件。
#include "ccTypes.h"
#include "cocoa/CCObject.h"
#include "touch_dispatcher/CCTouch.h"
#include "cocoa/CCSet.h"
#include "base_nodes/CCNode.h"
#include "script_support/CCScriptSupport.h"

//使用Cocos2d命名空间
NS_CC_BEGIN

// 由CCScriptEngineProtocol派生的实际功能类。

class CCLuaEngine : public CCScriptEngineProtocol
{
public:
	//析构
    ~CCLuaEngine();
    
    //取得LUA的全局指针,所有的LUA函数都需要使用这个指针来做为参数进行调用。
    virtual lua_State* getLuaState(void) {
        return m_state;
    }
    
    …此处省略若干字。
    
    // 加入一个多线程加载LUA脚本的实时回调函数,此函数用于ANDROID
    virtual void addLuaLoader(lua_CFunction func);
    //取得当前单件实例指针
    static CCLuaEngine* engine();
    
private:
	//构造,单例,你懂的。
    CCLuaEngine(void)
    : m_state(NULL)
    {
    }
    //初始化函数。
bool init(void);
//将一个句柄压栈
    bool pushFunctionByHandler(int nHandler);
    //唯一的LUA指针
    lua_State* m_state;
};

NS_CC_END

 

分析其CPP实现:

//本类的头文件。
#include "CCLuaEngine.h"
//这里用到了tolua++库,tolua++库是一个专门处理LUA脚本的第三方库,可以很好的完成LUA访问C++类及成员函数的功能。如果没有tolua++,这块要处理起来可是麻烦死了。
#include "tolua++.h"

//加入lua库的相应头文件。
extern "C" {
#include "lualib.h"
#include "lauxlib.h"
#include "tolua_fix.h"
}

//加入Cocos2d-x所用的相应头文件。
#include "cocos2d.h"
#include "LuaCocos2d.h"
#include "cocoa/CCArray.h"
#include "CCScheduler.h"

//如果是ANDROID平台,加上对多线程加载LUA脚本的支持,使用相应的头文件。

#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include "Cocos2dxLuaLoader.h"
#endif

//开始Cocos2d-x命名空间。
NS_CC_BEGIN

//析构。
CCLuaEngine::~CCLuaEngine()
{
	//结束对LUA指针的使用,关闭LUA。
    lua_close(m_state);
}

//初始始。
bool CCLuaEngine::init(void)
{
	//开始对LUA的使用,创建一个LUA指针。
m_state = lua_open();
//打开相应的库。
luaL_openlibs(m_state);
//打开使用tolua封装的访问Cocos2d-x的库。
    tolua_Cocos2d_open(m_state);
tolua_prepare_ccobject_table(m_state);
//如果是ANDROID平台,也加上对LUA进行多线程加载的库支持。
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    addLuaLoader(loader_Android);
#endif
    return true;
}

//取得单例指针。
CCLuaEngine* CCLuaEngine::engine()
{
    CCLuaEngine* pEngine = new CCLuaEngine();
    pEngine->init();
    pEngine->autorelease();
    return pEngine;
}

//通过LUA脚本ID移除对应的CCObject 
void CCLuaEngine::removeCCObjectByID(int nLuaID)
{
    tolua_remove_ccobject_by_refid(m_state, nLuaID);
}
//通过函数索引值移除对应的LUA函数。
void CCLuaEngine::removeLuaHandler(int nHandler)
{
    tolua_remove_function_by_refid(m_state, nHandler);
}
//将一个目录中的LUA文件加入到LUA资源容器中。
void CCLuaEngine::addSearchPath(const char* path)
{
	//取得全局表package
lua_getglobal(m_state, "package");                              
//取得其中的path字段,压入栈顶。
lua_getfield(m_state, -1, "path");            
//取得当前的目录字符串。
const char* cur_path =  lua_tostring(m_state, -1);
//参数出栈,恢复堆栈。
lua_pop(m_state, 1);                                            
//将新路径字符串加入到路径串列中,压入栈顶。
lua_pushfstring(m_state, "%s;%s/?.lua", cur_path, path);
//设置path字段值路径
lua_setfield(m_state, -2, "path");      
//参数出栈,恢复堆栈。
 lua_pop(m_state, 1);                                            
}
//执行一段LUA代码
int CCLuaEngine::executeString(const char *codes)
{
	//执行一段LUA代码。返回值存放到nRet中。
int nRet =    luaL_dostring(m_state, codes);
//进行下拉圾收集。
    lua_gc(m_state, LUA_GCCOLLECT, 0);
	//如果出错,打印日志。
    if (nRet != 0)
    {
        CCLOG("[LUA ERROR] %s", lua_tostring(m_state, -1));
        lua_pop(m_state, 1);
        return nRet;
    }
    return 0;
}
//执行一个LUA脚本文件。

int CCLuaEngine::executeScriptFile(const char* filename)
{
	//执行一个LUA脚本文件。返回值存放到nRet中。
    int nRet = luaL_dofile(m_state, filename);
//    lua_gc(m_state, LUA_GCCOLLECT, 0);
	//如果出错,打印日志。
    if (nRet != 0)
    {
        CCLOG("[LUA ERROR] %s", lua_tostring(m_state, -1));
        lua_pop(m_state, 1);
        return nRet;
    }
    return 0;
}
//调用一个全局函数。
int    CCLuaEngine::executeGlobalFunction(const char* functionName)
{
	//将全局函数放在栈顶
lua_getglobal(m_state, functionName);  /* query function by name, stack: function */
//判断是否是函数。
    if (!lua_isfunction(m_state, -1))
    {
        CCLOG("[LUA ERROR] name '%s' does not represent a Lua function", functionName);
        lua_pop(m_state, 1);
        return 0;
    }
	//调用函数。
    int error = lua_pcall(m_state, 0, 1, 0);         /* call function, stack: ret */
//    lua_gc(m_state, LUA_GCCOLLECT, 0);

    if (error)
    {
        CCLOG("[LUA ERROR] %s", lua_tostring(m_state, - 1));
        lua_pop(m_state, 1); // clean error message
        return 0;
    }

    // get return value
	//如果取得的第一个参数不是数字,返回错误。
    if (!lua_isnumber(m_state, -1))
    {
        lua_pop(m_state, 1);
        return 0;
    }
	//取得数字的参数存放在ret中。
int ret = lua_tointeger(m_state, -1);
//参数出栈,恢复堆栈。
    lua_pop(m_state, 1);                                            /* stack: - */
    return ret;
}
//通过句柄调用函数多种形态。
//通过句柄调用函数,参数二为参数数量。
int CCLuaEngine::executeFunctionByHandler(int nHandler, int numArgs)
{
    if (pushFunctionByHandler(nHandler))
    {
        if (numArgs > 0)
        {
            lua_insert(m_state, -(numArgs + 1));                        /* stack: ... func arg1 arg2 ... */
        }

        int error = 0;
        // try
        // {
            error = lua_pcall(m_state, numArgs, 1, 0);                  /* stack: ... ret */
        // }
        // catch (exception& e)
        // {
        //     CCLOG("[LUA ERROR] lua_pcall(%d) catch C++ exception: %s", nHandler, e.what());
        //     lua_settop(m_state, 0);
        //     return 0;
        // }
        // catch (...)
        // {
        //     CCLOG("[LUA ERROR] lua_pcall(%d) catch C++ unknown exception.", nHandler);
        //     lua_settop(m_state, 0);
        //     return 0;
        // }
        if (error)
        {
            CCLOG("[LUA ERROR] %s", lua_tostring(m_state, - 1));
            lua_settop(m_state, 0);
            return 0;
        }

        // get return value
        int ret = 0;
		//如果返回参数是数字转为整数。
        if (lua_isnumber(m_state, -1))
        {
            ret = lua_tointeger(m_state, -1);
        }//如果是布尔型转为true或false
        else if (lua_isboolean(m_state, -1))
        {
            ret = lua_toboolean(m_state, -1);
        }
	//参数出栈,恢复堆栈。
        lua_pop(m_state, 1);
        return ret;
    }
    else
    {
        return 0;
    }
}

//通过句柄调用函数,参数二为整数数据。
int CCLuaEngine::executeFunctionWithIntegerData(int nHandler, int data)
{
    lua_pushinteger(m_state, data);
    return executeFunctionByHandler(nHandler, 1);
}
//通过句柄调用函数,参数二为浮点数据。
int CCLuaEngine::executeFunctionWithFloatData(int nHandler, float data)
{
    lua_pushnumber(m_state, data);
    return executeFunctionByHandler(nHandler, 1);
}
//通过句柄调用函数,参数二为布尔型数据。
int CCLuaEngine::executeFunctionWithBooleanData(int nHandler, bool data)
{
    lua_pushboolean(m_state, data);
    return executeFunctionByHandler(nHandler, 1);
}
//通过句柄调用函数,参数二为CCObject指针数据和其类型名称。
int CCLuaEngine::executeFunctionWithCCObject(int nHandler, CCObject* pObject, const char* typeName)
{
    tolua_pushusertype_ccobject(m_state, pObject->m_uID, &pObject->m_nLuaID, pObject, typeName);
    return executeFunctionByHandler(nHandler, 1);
}
//将一个整数数值压栈做为参数。
int CCLuaEngine::pushIntegerToLuaStack(int data)
{
	//将整数值压入堆栈
lua_pushinteger(m_state, data);
//返回参数的数量。
    return lua_gettop(m_state);
}
//将一个浮点数值压栈做为参数。
int CCLuaEngine::pushFloatToLuaStack(int data)
{
	//将数字值压入堆栈
lua_pushnumber(m_state, data);
//返回参数的数量。
    return lua_gettop(m_state);
}
//将一个布尔数值压栈做为参数。
int CCLuaEngine::pushBooleanToLuaStack(int data)
{
	//将boolean值压入堆栈
lua_pushboolean(m_state, data);
//返回参数的数量。
    return lua_gettop(m_state);
}
//将一个CCObject指针和类型名压栈做为参数。
int CCLuaEngine::pushCCObjectToLuaStack(CCObject* pObject, const char* typeName)
{
    tolua_pushusertype_ccobject(m_state, pObject->m_uID, &pObject->m_nLuaID, pObject, typeName);
    return lua_gettop(m_state);
}

// 执行单点触屏事件
int CCLuaEngine::executeTouchEvent(int nHandler, int eventType, CCTouch *pTouch)
{
CCPoint pt = CCDirector::sharedDirector()->convertToGL(pTouch->getLocationInView());
//将参数压栈后调用函数。
    lua_pushinteger(m_state, eventType);
    lua_pushnumber(m_state, pt.x);
    lua_pushnumber(m_state, pt.y);
    return executeFunctionByHandler(nHandler, 3);
}

//执行多点触屏事件。
int CCLuaEngine::executeTouchesEvent(int nHandler, int eventType, CCSet *pTouches)
{
//将类型参数压栈后调用函数。
lua_pushinteger(m_state, eventType);
//创建一个表
    lua_newtable(m_state);
//将多个触点信息参数放入表中。
    CCDirector* pDirector = CCDirector::sharedDirector();
    CCSetIterator it = pTouches->begin();
    CCTouch* pTouch;
    int n = 1;
    while (it != pTouches->end())
    {
        pTouch = (CCTouch*)*it;
        CCPoint pt = pDirector->convertToGL(pTouch->getLocationInView());
		//将位置x压入堆栈
        lua_pushnumber(m_state, pt.x);
		//将栈顶的数值放入到表中对应索引n的数值中
        lua_rawseti(m_state, -2, n++);
		//将位置x压入堆栈
        lua_pushnumber(m_state, pt.y);
		//将栈顶的数值放入到表中对应索引n的数值中
        lua_rawseti(m_state, -2, n++);
        ++it;
    }
	//以表做为第二参数压栈,调用函数。
    return executeFunctionByHandler(nHandler, 2);
}
//通过句柄调用函数,参数二为CCObject指针数据和其类型名称。
int CCLuaEngine::executeSchedule(int nHandler, float dt)
{
    return executeFunctionWithFloatData(nHandler, dt);
}
// 加入一个多线程加载LUA脚本的实时回调函数,此函数用于ANDROID
void CCLuaEngine::addLuaLoader(lua_CFunction func)
{
    if (!func) return;

//取得全局表
lua_getglobal(m_state, "package");                     
//取得全局表中的“loaders”表
    lua_getfield(m_state, -1, "loaders");                 
//将设定的函数和参数压栈
lua_pushcfunction(m_state, func);                     
//将参数压栈
    for (int i = lua_objlen(m_state, -2) + 1; i > 2; --i)
{
	//取得原"loaders"表第i-1个参数
        lua_rawgeti(m_state, -2, i - 1);                                                                              
		//将取出的值放到新"loaders"表中第i个数值
        lua_rawseti(m_state, -3, i);                      
}
//将函数设为新"loaders"表的第2个参数
    lua_rawseti(m_state, -2, 2);                           
//把“loaders” 表放到全局表中
    lua_setfield(m_state, -2, "loaders");                  
	//参数出栈,恢复堆栈。
    lua_pop(m_state, 1);
}
//将一个句柄压栈
bool CCLuaEngine::pushFunctionByHandler(int nHandler)
{
	//找出注册函数表的第nHandler个数值
lua_rawgeti(m_state, LUA_REGISTRYINDEX, nHandler);  /* stack: ... func */
//判断是否是函数。
    if (!lua_isfunction(m_state, -1))
    {
        CCLOG("[LUA ERROR] function refid '%d' does not reference a Lua function", nHandler);
        lua_pop(m_state, 1);
        return false;
    }
    return true;
}

 

 

然后我们来看一下CCScriptEngineManager,这个类被称为脚本引擎管理器,其实很简单,只是用来设定当前项目的唯一正在使用的脚本引擎。也许Cocos2d-x打算用它管理多种类型脚本引擎,比如python,js等。

 

class CC_DLL CCScriptEngineManager
{
public:
	//析构
    ~CCScriptEngineManager(void);
    //取得单例指针
    CCScriptEngineProtocol* getScriptEngine(void) {
        return m_pScriptEngine;
    }
    //设置使用的LUA管理器
void setScriptEngine(CCScriptEngineProtocol *pScriptEngine);
//移除使用的LUA管理器。
    void removeScriptEngine(void);
	//取得单例指针
static CCScriptEngineManager* sharedManager(void);
//销毁单例
    static void purgeSharedManager(void);

private:
	//构造,单例,你懂的。
    CCScriptEngineManager(void)
    : m_pScriptEngine(NULL)
    {
    }
    //使用的LUA脚本引擎
    CCScriptEngineProtocol *m_pScriptEngine;
};
其对应的CPP实现:
//全局唯一的
static CCScriptEngineManager* s_pSharedScriptEngineManager = NULL;
//析构
CCScriptEngineManager::~CCScriptEngineManager(void)
{
    removeScriptEngine();
}
//设置使用的LUA管理器
void CCScriptEngineManager::setScriptEngine(CCScriptEngineProtocol *pScriptEngine)
{
    removeScriptEngine();
    m_pScriptEngine = pScriptEngine;
    m_pScriptEngine->retain();
}
//移除使用的LUA管理器。
void CCScriptEngineManager::removeScriptEngine(void)
{
    if (m_pScriptEngine)
    {
        m_pScriptEngine->release();
        m_pScriptEngine = NULL;
    }
}

//取得单例指针
CCScriptEngineManager* CCScriptEngineManager::sharedManager(void)
{
    if (!s_pSharedScriptEngineManager)
    {
        s_pSharedScriptEngineManager = new CCScriptEngineManager();
    }
    return s_pSharedScriptEngineManager;
}
//销毁单例
void CCScriptEngineManager::purgeSharedManager(void)
{
    if (s_pSharedScriptEngineManager)
    {
        delete s_pSharedScriptEngineManager;
        s_pSharedScriptEngineManager = NULL;
    }
}

 

现在我们来实际操作一下。

打开HelloLua工程中的AppDelegate.cpp:

在AppDelegate::applicationDidFinishLaunching()函数中看这几行代码:

 //取得LUA脚本引擎
CCScriptEngineProtocol* pEngine = CCLuaEngine::engine();
//设置脚本引擎管理器使用新创建的LUA脚本引擎
    CCScriptEngineManager::sharedManager()->setScriptEngine(pEngine);
//如果是ANDROID平台,获hello.lua内存到字符串然后执行字符串
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    CCString* pstrFileContent = CCString::createWithContentsOfFile("hello.lua");
    if (pstrFileContent)
    {
        pEngine->executeString(pstrFileContent->getCString());
    }
#else
	//如果不是ANDROID平台,取得hello.lua文件全路径并执行文件。
    std::string path = CCFileUtils::sharedFileUtils()->fullPathFromRelativePath("hello.lua");
    pEngine->addSearchPath(path.substr(0, path.find_last_of("/")).c_str());
    pEngine->executeScriptFile(path.c_str());
#endif 

 

 

就这样,hello.lua中的脚本就可以被执行了。

 

现在我们将HelloLua工程目录拷出一份来,将目录和工程命名为StudyLua,并在程序运行目录中加入相关资源图片。之后我们打开hello.lua:

 

 

-- 设置内存回收
collectgarbage("setpause", 100)
collectgarbage("setstepmul", 5000)


-- 取得窗口大小
local winSize = CCDirector:sharedDirector():getWinSize()
-- 将Hello背景图加入

local function createLayerHello()
    local layerHello = CCLayer:create()

    -- 加入背景图
    local bg = CCSprite:create("Hello.png")
    bg:setPosition(winSize.width / 2 , winSize.height / 2)
    layerHello:addChild(bg)
    
   	-- 创建HelloWorld
	local label = CCLabelTTF:create("Hello Cocos2d-x", "Arial", 50)
	label:setPosition(winSize.width / 2 ,60)
    label:setVisible(true)
    layerHello:addChild(label)
     
	return layerHello
end

--将关闭按钮菜单加入
local function createExitBtn()

    local layerMenu = CCLayer:create()

	--局部函数,用于退出
    local function menuCallbackExit()
		CCDirector:sharedDirector():endToLua()
    end

    -- 创建退出按钮
    local menuPopupItem = CCMenuItemImage:create("CloseNormal.png", "CloseSelected.png")
    -- 放在居上角附近
    menuPopupItem:setPosition(winSize.width - 50, winSize.height - 50)
    -- 注册退出函数
    menuPopupItem:registerScriptHandler(menuCallbackExit)
    -- 由菜单按钮项创建菜单
    local	menuClose = CCMenu:createWithItem(menuPopupItem)
    menuClose:setPosition(0, 0)
    menuClose:setVisible(true)
    -- 将菜单加入层中
    layerMenu:addChild(menuClose)

    return layerMenu
end

--创建场景
local sceneGame = CCScene:create()
--将Hello背景图加入
sceneGame:addChild(createLayerHello())
--将关闭按钮菜单加入
sceneGame:addChild(createExitBtn())
--运行场景
CCDirector:sharedDirector():runWithScene(sceneGame)

运行一下:

Cocos2d-x之LUA脚本引擎深入分析

我只能说,一切好极了,下课!

 

分类: cocos2d, cocos2d-lua 标签: