quick-cocos2d-x源文件编译及加密详解


  • quick-cocos2d-x是用lua脚本来写的,而lua是明文形式,如果不对脚本进行处理,那么我们所写的代码将可能暴露给别人(apk和ipa都是简单的zip包装)。
    quick-cocos2d-x框架为我们提供了一个可编译和加密代码的工具compile_scripts.sh(
    目前只提供了mac版 ),它位于quick-cocos2d-x/bin/compile_scripts.sh 。下面将对其进行详细的介绍。 

    一、安装luajit 
    lua脚本的编译是通过luajit来执行的,如果你之前没有安装过luajit的话,运行compile_scripts.sh会出现下面的提示: 
     
    下面将介绍如何安装luajit。 
    1、运行<path>/quick-cocos2d-x/bin/install_luajit.sh <path>为你具体的路径,请自行替换 这个时候会执行luajit的构建操作。 
    2、完成构建之后将进行安装,安装之前会叫你输入系统账户密码,如下图: 
     
    3、之后就是安装过程了,完成之后会出现下图这样的界面: 
     
    恭喜你完成了第一步。 

    二、示例 
    我们先来运行一个示例吧。这个将帮助你了解编译的过程。 
    我们做的很简单,就是把代码编译成一个zip包,并把它放置到res目录下(framework_precompiled.zip也在此目录)。 
    1、在bash下,设置当前工作目录到你游戏的目录下,如:

    cd /Users/myname/mygames/game01

    2、运行 compile_scripts.sh 

    /Users/myname/lua/tools/quick-cocos2d-x/bin/compile_scripts.sh -i scripts -o res/game.zip

    生成会显示出具体的信息,如参数是什么、源代码文件夹地址、生成了什么文件,生成文件的位置等等,如下图: 
     
    之后就会看到game.zip在res目录下了,解压(不是必须操作,只是为了看看里面有什么)之后发现: 
     
    它生成了一个个的二进制文件,文件名已经包含了包名。 
    到这里你也一定会问,命令中-i,-o这些参数是什么意思啊?还有哪些其它的参数啊 ?下面我们将对其进行详解。 

    三、参数详解 
    compile_scripts.sh中带有不少的参数,详情如下: 
    1、 -h 帮助。显示参数的解释及使用,英文的。 
    2、-i 源码目录 
    3、-o 输出文件或目录 
    4、-p 包前缀 
    5、-x 不包含在内的包。如果你有些源文件不想被编译进去的话,将会用到这个参数,只能设置包(对应文件夹),不能指定到文件多个包的话用,(逗号)隔开。示例:-x app.test,server,指的是app/test/.*,server/.* 这两个目录下的所有文件都不会被编译。
    注:经测试,目前这个参数没有作用,已报告给作者,请等待修复。
     
    6、-m 编译模式。有两个值  
    -m zip 默认,生成的是zip格式文件; 
    -m files 生成的是一个个文件,不打包,这个时候-o参数指的就是输出的目录。 
    7、-e 加密模式。可以对编译后的文件再进行 XXTEA 加密,有两个值: 
    -e xxtea_zip 对应-m zip,对打包后的zip格式文件进行加密,之后再打包成zip格式。 
    -e xxtea_chunk 对编译后的文件加密,最后打不打包取决于-m。 
    注:xxtea_zip一定要与-m zip编译模式对应,不然会提示出错 
    8、-ek 加密密钥。对-e有效,且设置了-e之后一定要设置-ek。 
    9、-es 加密签名。对-e有效,默认值为"XXTEA",这个只是为了让引擎知道文件是否被加密过,意义不大,所以可不设置。 
    下面是一个编译及加密的例子:

    compile_scripts.sh -i scripts -o res/game.zip -e xxtea_zip -ek aaa -es XT

    10、-ex 编译后的文件的后缀名。对-m files有效,默认值为"lua"。 
    11、-c 从一个文件加载参数列表 
    12、-q 生成过程不输出信息 

    四、修改 AppDelegate .cpp 
    我们编译了代码,并且把main.lua文件也包含进去了,我们要修改c++中的代码才能让程序正常运行。这个在 AppDelegate .ccp文件中修改,把applicationDidFinishLaunching ()方法改成如下:

    bool AppDelegate::applicationDidFinishLaunching()
    {
        ...
    
        CCLuaStack *pStack = pEngine->getLuaStack();
    
        // 如果设置了 -e 和 -ek 要加上下面这句
        // pStack->setXXTEAKeyAndSign("aaa", 3);
        // 如果设置了 -e 和 -ek -es 则要加上下面这句
        pStack->setXXTEAKeyAndSign("aaa", 3, "XT", 2);
        // load framework
        pStack->loadChunksFromZip("res/framework_precompiled.zip");
        pStack->loadChunksFromZip("res/game.zip");
        pStack->executeString("require 'main'");
    
        return true;
    }

    注意:一直有朋友不太注意,
    pStack->setXXTEAKeyAndSign("aaa", 3, "XT", 2);
    这一句的第二和第四个参数都是前一个参数的长度,并不是固定值,如可以这样
    pStack->setXXTEAKeyAndSign("abcd", 4, "SIGNCODE", 8);

    最后需要注意的是:编译发布程序的时候要记得把源代码文件夹从项目中移除(不是删除,只是不包含进项目里),不然一切都白费了

    到此整个过程结束,我们的代码已经被很好的保护起来了。


    发布于 4个月前, 阅读(3851) | 评论(16) | 投票(4) | 收藏(18)  阅读全文...
  • 022013-11

    上一篇讲了quick-cocos2d-x使用chipmunk的使用流程,介绍了World,Body,Shape的建立,这一篇将讲解如何处理物体之间的碰撞。

    一、监听事件
    quick-cocos2d-x中的chipmunk碰撞处理是通过监听事件来实现的。

    -- 设置物体的碰撞类别,默认所有物体都是类别0
    Body:setCollisionType(type)
    -- handle是一个回调函数,碰撞处理将在这个函数中执行
    -- collisionTypeA和collisionTypeB是两个int值,表示要监听的是哪两种物体
    -- 这两个值在body中设置(上面所示的方法),两个值相同时为监听同种物体之间的碰撞
    World:addCollisionScriptListener(handler, collisionTypeA, collisionTypeB)
    -- handler回调函数,这个函数一定要返回true,不然碰撞后两个物体将会重合,即像是不碰撞一样
    -- eventType为碰撞的类别,有四种值:
    --   begin 开始碰撞
    --   preSolve 持续碰撞(相交),可以设置相交时独自的摩擦,弹力等
    --   postSolve 调用完preSolve后,做后期处理,比如计算破坏等等
    --   separate 分开,当删除一个shape时也会调用
    -- event为CCPhysicsCollisionEvent实例
    onCollisionListener(eventType, event)
    示例:
    body:setCollisionType(1)
    self.world:addCollisionScriptListener(handler(self, self.onCollisionListener), 1, 2)
    function PhysicsScene:onCollisionListener(eventType, event)
        print(eventType)
        return true
    end

    二、  CCPhysicsCollisionEvent 
    在回调函数中,有个重要的参数就是event了,CCPhysicsCollisionEvent中包含很多碰撞时的信息
    -- 获取CCPhysicsWorld
    event:getWorld()
    -- 获取两个碰撞的body
    event:getBody1()
    event:getBody2()
    -- 是否是两种物体第一次接触
    event:isFirstContact()
    -- 弹性系数,在preSolve之前设置可改变当前值
    event:setElasticity(elasticity)
    event:getElasticity()
    -- 摩擦系数,在preSolve之前设置可改变当前值
    event:setFriction(friction)
    event:getFriction()
    -- 碰撞时两物体的接触点数
    event:getCount()
    -- 获取接触点
    event:getPoint(index)

    三、分组碰撞 
    我们在做一款游戏的时候,可能里面有很多的物体类型,我们希望某一物体只与特定的其它物体碰撞,而不是全部都碰撞,这种情况下就需要对物体进行分组了,在quick-cocos2d-x中可通过以下方法来实现
    -- body所在的碰撞层,默认值为0,同层的物体才会发生碰撞
    -- layers可以表示多个层,具体怎么达到的呢
    -- 当我们对一个物体分层时,所用的数字应该为[1,2,4,8,16,32,...]这样形式(倍增)来分
    -- 如A为1,B为2,C为4,即为基本层(layer),如果要求C可与A碰撞,则C的layers为1+4=5
    Body:setCollisionLayers(layers)
    -- body所在组,默认值为0,同个组的将不发生碰撞(除了0组)
    -- 优先级高于layer,当group不一样的时候才会考虑layers
    Body:setCollisionGroup(group)
    从上面我们可以得出:
    两个物体要想发生碰撞,就要
    1、group不一样或都为0。
    2、至少有一个layer(注意,非layers)是一样的
     
    下面将通过一个具体的案例来讲解。
     
    假如我们设计一个游戏,内容为上古部落之间的战争,有A,B,C,D四个部落,A和B为结盟关系,C,D则是各自独立的,这是一个三角混战的关系,那么就有以下的规则: 
    1)A和B分别与C,D碰撞 
    2)A和B之间不发生碰撞 
    3)C与A,B,D之间都碰撞 
    4)D与A,B,C之间都碰撞 
    5)A,B,C,D之间各自(即A与A,B与B,...)不碰撞 
    我们设计的基本层为:A=1,B=2,C=4,D=8 
     
    可由上图得出它们各自的layers为 
    A.layers=1 
    B.layers=2 
    C.layers=1+2+4+8 
    D.layers=1+2+4+8 
    上面有一些是重复的层,可以进行简化 
    A.layers=1 
    B.layers=2 
    C.layers=1+2+4+8 
    D.layers=1+2  +8 
    还可以把C,D放在同一层4进行简化 
    A.layers=1 
    B.layers=2 
    C.layers=1+2+4 
    D.layers=1+2+4 
    具体的分法并没有一个统一的方法,只要合乎上面的原则即可,而且物体多时会比较麻烦,大家要多加注意。 
    设置layers只解决了1)——4)而已,5)还是没有解决,这就用到了group这一属性,我们把A,B,C,D设为不同的group即可 
    A.  group  =1 
    B.  group  =2 
    C.  group  =3 
    D.  group  =4 
    最后的代码如下:
    -- 为了程序清晰,layers不用求和
    A:setCollisionLayers(1)
    B:setCollisionLayers(2)
    C:setCollisionLayers(1+2+4+8)
    D:setCollisionLayers(1+2+4+8)
    A:setCollisionGroup(1)
    B:setCollisionGroup(2)
    C:setCollisionGroup(3)
    D:setCollisionGroup(4)

    发布于 5个月前, 阅读(733) | 评论(0) | 投票(1) | 收藏(2)  阅读全文...
  • 012013-11

    chipmunk是一个用C写成的物理引擎,quick-cocos2d-x已经用C++对其进行封装,导出到lua的类有:

    CCPhysicsWorld
    CCPhysicsBody
    CCPhysicsShape
    CCPhysicsDebugNode
    CCPhysicsCollisionEvent
    CCPhysicsVector

    可以看出,quick-cocos2d-x没有对joint(连接)进行封装,下面将就如何在quick-cocos2d-x中使用chipmunk作详细的介绍。 

    一、简介 
    1、World,Body,Shape可用下图说明其关系

    可以看出,World由Body组成,而Body又由Shape组成。就像我们所处的世界一样,世界由房屋,树木等等构成,而房屋由门窗,墙壁等组成,树木由叶子,树干,树枝等组成。
    2、CCPhysicsDebugNode
    是用于显示各个物体的边框等等信息,主要用于调试。
    3、CCPhysicsCollisionEvent
    是物体之间发生碰撞时产生的事件,事件中携带有碰撞时的信息。
    4、
    CCPhysicsVector则是向量,物体受力时将会用到。

    二、使用流程

    1、建立World
    我们要使用chipmunk,首选要建立一个世界(CCPhysicsWorld)

    -- 建立没有引力的世界
    CCPhysicsWorld:create()
    -- 指定两个方向上的引力
    CCPhysicsWorld:create(gravityX, gravityY)
    示例: 
    -- 引力向下的世界(跟地球类似)
    self.world=CCPhysicsWorld:create(0,-200)
    self:addChild(self.world)
    2、建立Body 
    在chipmunk里,Body分为两种:static和非static(普通),static是固定不动的;形状上可以分为长(正)方形、圆形、多边形和自定义类型,建立它们的方法也很多。 
    1)可以通过Word里的方法创建: 
    -- 当mass<=0时会创建一个StaticBody
    World:createBoxBody(mass, width, height)
    -- offset为偏移量
    World:createCircleBody(mass, radius, offsetX, offsetY)
    -- vertexes格式为{x1, y1, x2, y2, x3, y3},目前的版本有bug,不可以设置offset
    World:createPolygonBody(mass, vertexes, offsetX, offsetY)
    示例:
    local body = self.world:createBoxBody(10, 100, 100)
    local body = self.world:createCircleBody(0, 20, 50, 50)
    -- 创建一个三角形
    local vertexes = {0,0,50,50,100,0}
    local body=self.world:createPolygonBody(0, vertexes)
    2)通过Body里的方法创建:
    CCPhysicsBody:createStaticBody(world)
    CCPhysicsBody:create(world, mass, moment)
    示例:
    local body = CCPhysicsBody:createStaticBody(self.world)
    -- 这种创建方式要记得把body放到world里
    self.world:addBody(body)
    3)重要属性或方法:
    -- 摩擦系数 0-1.0
    body:setFriction(friction)
    -- 反弹系数 0-1.0
    body:setElasticity(elasticity)
    -- 是否是感应
    body:setIsSensor(isSensor)
    body:isSensor()
    -- 速度
    body:setVelocity(velocityX, velocityY)
    -- 角速度
    body:setAngleVelocity(velocity)
    -- 推力
    body:applyForce(forceX, forceY, offsetX, offsetY)
    body:applyForce(force, offsetX, offsetY)
    body:applyImpulse(forceX, forceY, offsetX, offsetY)
    body:applyImpulse(force, offsetX, offsetY)
    4)删除
    -- unbind=true时将解除绑定的CCNode,但不会从场景里删除node,只是执行CC_SAFE_RELEASE_NULL(node);
    -- unbind=false时CCNode将继续绑定在该Body上,默认为true
    body:removeSelf(unbind)
    World:removeBody(body, unbind)
    World:removeBodyByTag(tag, unbind)
    3、建立Shape 
    Shape要放置于Body中,一般情况下是不需要单独再建立Shape的,上面创建Body的时候很多就已经创建有一个Shape了
    当一个Body由多个独立的部分组成时(比如一个人由头部,手,脚,身体组成),则要通过创建多个Shape来完成了。

    1)Shape
    的创建都是由Body的方法来完成。
    -- 线段,lowerLeft和lowerRight为CCPoint,thickness为粗细
    Body:addSegmentShape(lowerLeft, lowerRight, thickness)
    Body:addCircleShape(radius, offsetX, offsetY)
    Body:addBoxShape(width, height)
    -- 目前好像不能运行,已发issue到官方仓库
    Body:addPolygonShape(vertexes, offsetX, offsetY)

    2)Shape的属性

    -- 摩擦系数 0-1.0
    shape:setFriction(friction)
    -- 反弹系数 0-1.0
    shape:setElasticity(elasticity)
    -- 是否是感应
    shape:setIsSensor(isSensor)
    shape:isSensor()
    3)删除
    body:removeShape(shapeObject)
    body:removeShapeAtIndex(index)
    body:removeAllShape()
    4、绑定 
    之前我们都只是建立物理世界里的物体,并没有把它们同外观联系起来,比如把一个Box同一张箱子的图片结合在一起,这个就叫做绑定,在quick-cocos2d-x里可以通过
    -- node为CCNode类型
    Body:bind(node)
    Body:unbind()
    来完成绑定和解绑。 
    5、调试 
    很简单,只要加入以下代码即可:
    self.worldDebug=self.world:createDebugNode()
    self:addChild(self.worldDebug)


    以上就是quick-cocos2d-x中使用chipmunk的过程,有些成员方法没有一一指出,大家可自行翻看源码来了解。下一篇我将会讲到碰撞部分

    发布于 5个月前, 阅读(1222) | 评论(0) | 投票(2) | 收藏(6)  阅读全文...
  • 302013-10

    GameState是quick-cocos2d-x中特有的一个用户信息存储类,相对于CCUserDefault而言,它增加了数据校验的功能,当数据被人为的改变之后,会被检测出来,下面介绍它的主要用法。

    一、初始化
    因为GameState并没有在framework中加载,所以我们要在开始的代码中去加载,如在MyApp.lua的开头位置加载
    GameState=require(cc.PACKAGE_NAME .. ".api.GameState")

    二、提供的方法
    1、GameState.init(eventListener_, stateFilename_, secretKey_)
    这个方法是初始化GameState,在场景初始化之前调用一次即可,如在MyApp.lua的MyApp:ctor()中调用。
    eventListener_是载入或保存时调用的函数,可以写个匿名函数,后面会重点介绍;
    stateFilename_是保存的文件名,如果留空或非字符串(string)则是默认的state.txt,该文件会被保存到device.writablePath下;
    secretKey_是 校验文件时所用到的密钥,GameState保存的数据格式为{h = hash, s = s},s是我们要保存的数据(一个table),h则是要校验的一个md5码。如果secretKey_留空或为非字符串(string)则不加校验码, 直接保存数据,跟CCUserDefault一样了。
    2、GameState.load()
    载入并返回数据,一般此方法只调用一些就行,在游戏加载前调用并保存到一个全局变量GameData。
    3、GameState.save(newValues)
    保存数据,newValues是一个table。GameState.init对应于保存一个文件,此文件的内容就是newValues,所以我们需要更新数据的时候应该改变上面的GameData,然后保存GameData。
    4、GameState.getGameStatePath()
    保存的文件的完整路径。

    三、eventListener(value)
    此函数就是GameState.init中的第一个参数,value为一个table,此函数在载入或保存时都会被调用,相当于一个监听器。不同的情况下value的值会不一样。
    注意:eventListener一定要有返回值。
    1、GameState.load()中用到的eventListener
    1)
      如果在这个函数中载入数据时有异常发生,value值为{name = "load", errorCode = errorCode},name有两种值,"load"和"save",分别对应载入和保存;errorCode分为三种:
    GameState.ERROR_INVALID_FILE_CONTENTS //不合法的文件内容,即取出来的内容不是一个table
    GameState.ERROR_HASH_MISS_MATCH //文件被人为更改过
    GameState.ERROR_STATE_FILE_NOT_FOUND //文件不存在
    这个时候eventListener可返回nil。
    2)  如果载入的数据是正确的,value值为

    {
            name   = "load",
            values = values,
            encode = encode,
            time   = os.time()
     }

    values就是取出的数据;encode数据是否加密过;time当前取出的时间。这个时候eventListener返回value.values即可。
    2、GameState.save(newValues)中用到的eventListener
    这时value值为

    {
            name   = "save",
            values = newValues,
            encode = type(secretKey) == "string"
    }

    values就是要保存的数据;encode数据是否要加密。这个时候eventListener返回value.values即可。
    当然,以上的情况你都可以做一些其它的事,如弹出错误的窗口,或打印出已加载或已保存的数据。

    四、加密数据
    GameState保存的还是明文数据,只是加了校验码,如果我们不想被用户知道保存了什么信息,可加密了再保存,这个在eventListener中“过滤”一下就行了。

    五、示例
    下面是一个完整的加载、初始化、载入,保存及加解密的过程代码:

    //MyApp.lua(加载、初始化、载入,加解密)

    ...
    
    require("framework.cc.init")
    GameState=require(cc.PACKAGE_NAME .. ".api.GameState")
    
    -- global var
    GameData={}
    ...
    
    function MyApp:ctor()
        -- init GameState
        GameState.init(function(param)
            local returnValue=nil
            if param.errorCode then
                CCLuaLog("error")
            else
                -- crypto
                if param.name=="save" then
                    local str=json.encode(param.values)
                    str=crypto.encryptXXTEA(str, "abcd")
                    returnValue={data=str}
                elseif param.name=="load" then
                    local str=crypto.decryptXXTEA(param.values.data, "abcd")
                    returnValue=json.decode(str)
                end
                -- returnValue=param.values
            end
            return returnValue
        end, "data.txt","1234")
        GameData=GameState.load()
        if not GameData then
            GameData={}
        end
    
    .....
    end
    
    ....

    //保存的时候

    GameData.aaa="bbb"
    GameState.save(GameData)
  • 出处:http://my.oschina.net/lonewolf/blog?disp=2&p=1&catalog=417040
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值