【Cocos2d-X(1.x 2.x) 】iOS6与iphone5适相关设置随笔(解决第三方类库无法通过armv7s编译的方法、添加Default-568h@2x.png)
最后更新于:2022-04-01 10:16:05
本站文章均为[ 李华明Himi ](http://www.himigame.com/about-himi)原创,转载务必在明显处注明:
转载自[【黑米GameDev街区】](http://www.himigame.com/) 原文链接: [http://www.himigame.com/iphone-cocos2dx/1020.html](http://www.himigame.com/iphone-cocos2dx/1020.html "【Cocos2d-X(1.x")
一、很多项目使用>=4.5 version 的Xcode无法,发现很多第三方库,比如SWaves SDK、Admob SDK、91 SDK Mobage SDk等等。
1.最简单的解决方案肯定是等这些第三方库尽快更新支持ios6 armv7s 的新版SDK.
2.另外等不及的童鞋可以尝试如下两种方法解决无法通过编译的问题:
2.1:
选中项目,然后选中 TARGETS 项目,找到Build Settings 的页面,搜索: Build Active Architecture Only 然后将其设置为 YES 即可通过armv7s编译
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd0284d8b4.png "QQ20121114-1")](http://www.himigame.com/wp-content/uploads/2012/11/QQ20121114-1.png)
2.2.:
选中项目,然后选中 TARGETS 项目,找到Build Settings 的页面,搜索: Valid Architectures
删除其中armv7s删除即可。
推荐第一种解决方案。
需要注意的是,虽然有的使用如上方法可以通过ios6的armv7s的第三方编译,但是仍无法编译到iphone5真机,对于此种情况我们只能默默等待第三方对armv7s的新版本了。
二、 关于之前引擎版本的在iOS6 无法正常横屏的请参考上一章博文。
三、当使用>=4.5 Version 的Xcode 需要添加一张 1136 * 640的 Default 图片,如果是横屏则需要添加一张 640 * 1136的图,图片名统一使用“ Default-568@2x.png ” 这张图片命名方式只适用于启动logo图。
如果你没有添加最新尺寸的Default图片,系统提示警告如下:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd0286b636.png "QQ20121114-7")](http://www.himigame.com/wp-content/uploads/2012/11/QQ20121114-7.png)
双击此警告,系统将如下提示:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd0287f5e5.png "QQ20121114-8")](http://www.himigame.com/wp-content/uploads/2012/11/QQ20121114-8.png)
上面提示是提醒是否需要Xcode自动生成一张Default-568h@2x.png 尺寸默认是640*1136的一张全黑色的png图。
四、如果有童鞋使用CCDirector::sharedDirector()->getWinSize();的方式根据其width判断是否为iphone5的话,那么千万注意(横屏距离):
你千万不要判断当前屏幕尺寸的 if(size.width==1136){/*是iphone5*/} (这种是错误的)
在iphone上只有480*320的尺寸,现在iphone5 则是 568*320 !!!
因此正确应该是:
if(size.width==568){/*是iphone5*/}
【Cocos2d-X(1.x 2.x) 修复篇】iOS6 中 libcurl.a 无法通过armv7s编译以及iOS6中无法正常游戏横屏的解决方法
最后更新于:2022-04-01 10:16:02
本站文章均为[ 李华明Himi ](http://www.himigame.com/about-himi)原创,转载务必在明显处注明:
转载自[【黑米GameDev街区】](http://www.himigame.com/) 原文链接: [http://www.himigame.com/iphone-cocos2dx/1000.html](http://www.himigame.com/iphone-cocos2dx/1000.html "【Cocos2d-X(1.x")
[☞ 点击订阅 ☜](http://list.qq.com/cgi-bin/qf_invite?id=acfc24e272cc4a26debf3b3866edb626a9ea3fc80fd8893c) 本博客最新动态!及时将最新博文通知您!
iOS6 与 iphone5 已经发布一段时间了,那么对于cocos2dx在昨天发布的最新支持的Cocos2d-2.0-x-2.0.3 released版本之前的版本跑在iOS6中会出现两个问题(Himi 都已iphone5 真机调试验证并解决)
首先对于 libcurl.a 无法通过armv7s编译的问题给出解决方案:
下载最新的Cocos2d-2.0-x-2.0.3引擎包,然后找到已经支持armv7s的libcurl.a的文件,路径如下:http://cocos2d-2.0-x-2.0.3/cocos2dx/platform/third_party/ios/libraries/libcurl.a
将其替换你项目的旧libcurl.a即可。
对于iOS6中无法正常游戏横屏的解决方案如下:
1.首先在你项目根目录的ios/AppController.mm 类找到如下代码:
~~~
// Set RootViewController to window
[window addSubview: viewController.view];
~~~
将其替换成如下代码:
~~~
// Set RootViewController to window
if ( [[UIDevice currentDevice].systemVersion floatValue] < 6.0)
{
// warning: addSubView doesn't work on iOS6
[window addSubview: viewController.view];
}
else
{
// use this mehod on ios6
[window setRootViewController:viewController];
}
~~~
2.然后在你项目根目录的 ios/RootViewController.mm文件中添加如下两个函数:
~~~
- (NSUInteger) supportedInterfaceOrientations{
return UIInterfaceOrientationMaskLandscape;
}
- (BOOL) shouldAutorotate {
return YES;
}
~~~
OK,问题解决。
官方在昨天已经发布的最新的Cocos2d-2.0-x-2.0.3 released版本,已经解决如上问题,针对ios6 和iphone5的。
如果还有项目报错,要检查是否因为第三方SDK类库造成,毕竟iOS6的更新带来各大第三方库的更新。
【Cocos2d-X(2.x) 游戏开发系列之三】最新版本cocos2d­2.0­x­2.0.2使用新资源加载策略!不再沿用-hd、-ipad、-ipadhd添加后缀方式
最后更新于:2022-04-01 10:16:00
本站文章均为[ 李华明Himi ](http://www.himigame.com/about-himi)原创,转载务必在明显处注明:
转载自[【黑米GameDev街区】](http://www.himigame.com/) 原文链接: [http://www.himigame.com/cocos2dx-v2-0/997.html](http://www.himigame.com/cocos2dx-v2-0/997.html "【Cocos2d-X(2.x)")
[☞ 点击订阅 ☜](http://list.qq.com/cgi-bin/qf_invite?id=acfc24e272cc4a26debf3b3866edb626a9ea3fc80fd8893c) 本博客最新动态!及时将最新博文通知您!
前段时间cocos2dx更新了最新版本cocos2d2.0x2.0.2,也从这个版本开始对于资源加载与管理都改变了策略。
在之前的加载方式都是通过沿用与cocos2d-iphone一样的加载资源方式,对于图片名后添加-hd,-ipad,-ipadhd方式,当用户开启项目的高清视网膜后就可以默认寻找对应的资源。但是从cocos2d2.0x2.0.2版本开始,资源加载策略不在如此了。
最新资源加载策略的机制如下:
首先从CCFileUtils的setResourceDirectory设置的目录中去寻找,如果找不到则会Resources/目录下找。
例如:
1) 设置目录:
CCFileUtils::sharedFileUtils()->setResourceDirectory(“abc”);
当创建一个精灵时:
CCSprite * spr = CCSprite::create(“himi.png”);
Cocos2dx首先会到Resources/abc目录下寻找“himi.png”,如果找不到将回到Resources目录下寻找“himi.png”。
因此我们一般在AppDelegate.cpp的applicationDidFinishLaunching函数中打开高清视网膜后,添加如下代码:
~~~
TargetPlatform target = getTargetPlatform();//获取当前设备类型
if (target == kTargetIpad){//如果是Ipad
if (pDirector->enableRetinaDisplay(true)){ //如果开启高清视网膜
CCFileUtils::sharedFileUtils()->setResourceDirectory("../ipadhd");
}else {
CCFileUtils::sharedFileUtils()->setResourceDirectory("../ipad");
}
}else if (target == kTargetIphone) {//如果是iphone
if (pDirector->enableRetinaDisplay(true))
{
CCFileUtils::sharedFileUtils()->setResourceDirectory("../hd");
}
}
~~~
项目默认目录为Resources,这里Himi创建的hd、ipadhd、ipad的文件夹与Resources同一级,所以这里设置目录为”../hd” ,”../ipad”,”../ipadhd”
附:
设备枚举变量如下:
kTargetWindows: window系统
kTargetLinux: linux系统
kTargetMacOS: mac os系统
kTargetAndroid: Android系统
kTargetIphone: iOS系统-iphone&itouch
kTargetIpad: iOS系统-Ipad
kTargetBlackBerry: 黑莓系统
Himi项目目录如下图3.14.3.1所示:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd02822841.png "1")](http://www.himigame.com/wp-content/uploads/2012/09/1.png)
图3.14.3.1 项目目录
【Cocos2dX(2.x)_Lua开发之三】★重要必看篇★在Lua中使用自定义精灵(Lua脚本与自创建类之间的访问)及Lua基础讲解
最后更新于:2022-04-01 10:15:58
本站文章均为[ 李华明Himi ](http://www.himigame.com/about-himi)原创,转载务必在明显处注明:
转载自[【黑米GameDev街区】](http://www.himigame.com/) 原文链接: [http://www.himigame.com/lua-2/985.html](http://www.himigame.com/lua-2/985.html "【Cocos2dX(2.x)_Lua开发之一】★重要必看篇★在Lua中使用自定义精灵(Lua脚本与自创建类之间的访问)及Lua基础讲解")
[☞ 点击订阅 ☜](http://list.qq.com/cgi-bin/qf_invite?id=acfc24e272cc4a26debf3b3866edb626a9ea3fc80fd8893c) 本博客最新动态!及时将最新博文通知您!
本篇做起来比较累,大家请参考最新篇[【COCOS2DX-LUA 脚本开发之四】使用tolua++编译pkg,从而创建自定义类让Lua脚本使用](http://www.himigame.com/lua1/1259.html)
此篇可能会在最新的cocos2dx版本中出现如下问题:
~~~
LUA ERROR: ...24F82-1230-41FE-8A04-C445FB7D1BAB/mtet.app/hello.lua:35:
error in function 'addChild'. argument #2 is 'MySprite'; 'CCNode' expected.
~~~
最近Himi都没有更新博文了,其实也是犹豫写本cocos2d(x)引擎书籍在做准备,目录的草稿都写好了,目录中包含的大家最感兴趣的cocos2d/x动作编辑器的详细制作流程与源码! 但是遗憾的是Himi还是腾不出时间去写;
放弃去写的另外一个原因就是因为支持我的童鞋门,在7月份就说过要为大家奉上关于cocos2dx-lua的系列教程,但是一直由于时间等问题一再拖到现在,如果Himi真要准备写书估计半年内都基本很难有时间更新博客的,当然也考虑到公司项目,最终放弃;(暂时放弃,以后有可能的话还是会写的 )![](image/56fcaa6a58e7f.gif)
顺便说一句,关于Himi9个技术群,不论是cocos2d-iphone、cocos2dx、android还是以后陆续公布的Unity3D群,周期性的Himi和管理员们会定期清理(一切都是为了更多想学习的新童鞋考虑),希望大家进群冒泡,积极讨论。好了,废话就不多说了,从今天开始Himi为童鞋们出cocos2dx-lua的系列开发教程,希望大家还一如既往得支持;3Q
Himi 当前开发工具等版本如下:
mac: 10.8 xcode:4.4.1 cocos2dx :cocos2d-2.0-rc2-x-2.0.1
本篇介绍两个知识点:
1\. Lua基础
2.在lua脚本中使用我们自定义的精灵类
### 一:lua基础
关于Lua的其实很早前Himi写过一篇关于cocos2dx-Lua 的基础博文了,但是是cocos2dx 1.x版本的,对于不是很熟悉的童鞋,Himi还是建议去看一看,连接:[【iOS-cocos2d-X 游戏开发之八】在Cocos2dX游戏中使用Lua脚本进行游戏开发(基础篇)并介绍脚本在游戏中详细用途!](http://www.himigame.com/iphone-cocos2dx/681.html) Himi以后更新的Lua系列都是基于Cocos2dx 2.x版本的了。
###二:在lua脚本中使用我们自定义的精灵类
首先创建cocos2dx-lua项目,然后在项目中添加我们的自定义精灵类:这里Himi类名为:HSprite
HSprite.h:
~~~
//
// HSprite.h
// cocos2dx_lua_tests_by_Himi
//
// Created by Himi on 12-8-30.
//
//
#ifndef cocos2dx_lua_tests_by_Himi_HSprite_h
#define cocos2dx_lua_tests_by_Himi_HSprite_h
#include "cocos2d.h"
using namespace cocos2d;
class HSprite : public cocos2d::CCSprite{
public:
static HSprite* createHSprite(const char* _name);
void hspriteInit();
};
#endif
~~~
HSprite.cpp:
~~~
//
// HSprite.cpp
// cocos2dx_lua_tests_by_Himi
//
// Created by Himi on 12-8-30.
//
//
#import "HSprite.h"
HSprite* HSprite::createHSprite(const char* _name){
HSprite* sp = new HSprite();
if(sp && sp->initWithFile(_name)){
sp->hspriteInit();
sp->autorelease();
return sp;
}
CC_SAFE_DELETE(sp);
return NULL;
}
void HSprite::hspriteInit(){
CCMessageBox("create HSprite success", "Himi_Lua");
}
~~~
以上代码不做解释了,很简单,继承CCSprite,添加一个自动释放的创建函数(createHSprite)以及一个自定义初始化函数(hspriteInit)
下面我们打开LuaCocos2d.cpp 类,这个类在项目的 libs/lua/cocos2dx_support目录下,如下图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd0275416b.png)
然后开始添加我们自定义精灵类,让Lua脚本能认识它;
步骤分为3步:
1\. 注册我们的自定义类:
在LuaCocos2d.cpp类中搜索“tolua_reg_types”这个函数,然后在其中进行注册:
~~~
tolua_usertype(tolua_S,"HSprite");
~~~
如下图所示:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd0276f911.png)
第二步:声明我们自定义类的函数:
搜索“tolua_Cocos2d_open”这个函数,然后在其中添加如下代码:
~~~
tolua_cclass(tolua_S, "HSprite", "HSprite", "CCSprite", NULL);
tolua_beginmodule(tolua_S,"HSprite");
tolua_function(tolua_S,"createHSprite",tolua_Himi_HSprite_createHSrpite00);
tolua_endmodule(tolua_S);
~~~
如下图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd0279f71b.png)
这里开始解释:
首先定义能让脚本认识的类函数,遵循如下:
a) tolua_cclass(tolua_S, “HSprite”, “HSprite”, “CCSprite”, NULL);
tolua_cclass声明哪个类函数,第一个状态值默认:tolua_S
后两个参数:是自定义类类名
再往后是继承的父类类名
b)添加参数开始声明:
tolua_beginmodule(tolua_S,”HSprite”);
c) 添加自定类函数:
tolua_function(tolua_S,”createHSprite”,tolua_Himi_HSprite_createHSrpite00);
第一个参数默认,第二个参数自定义类名,第三个:实现脚本与自定义类之间的转换实现函数
注意,这里有多个函数,可以继续写;
d) 结束自定义函数:
tolua_endmodule(tolua_S);
第三步:实现我们的脚本之间转换函数 tolua_Himi_HSprite_createHSrpite00
实现如下:
~~~
/* method: create of class HSprite */
#ifndef TOLUA_DISABLE_tolua_Himi_HSprite_createHSrpite00
static int tolua_Himi_HSprite_createHSrpite00(lua_State* tolua_S)
{
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
if (
!tolua_isusertable(tolua_S,1,"HSprite",0,&tolua_err) ||
!tolua_isstring(tolua_S,2,0,&tolua_err) ||
!tolua_isnoobj(tolua_S,3,&tolua_err)
)
goto tolua_lerror;
else
#endif
{
const char* pszFileName = ((const char*) tolua_tostring(tolua_S,2,0));
{
HSprite* tolua_ret = (HSprite*) HSprite::createHSprite(pszFileName);
int nID = (tolua_ret) ? tolua_ret->m_uID : -1;
int* pLuaID = (tolua_ret) ? &tolua_ret->m_nLuaID : NULL;
tolua_pushusertype_ccobject(tolua_S, nID, pLuaID, (void*)tolua_ret,"HSprite");
}
}
return 1;
#ifndef TOLUA_RELEASE
tolua_lerror:
tolua_error(tolua_S,"#ferror in function 'create'.",&tolua_err);
return 0;
#endif
}
#endif //#ifndef TOLUA_DISABLE
~~~
如下图所示:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd027c5e60.jpg)](http://www.himigame.com/wp-content/uploads/2012/08/32.jpg)
这里Himi解释下:
童鞋们可以从第 384行的 #endif 把这个实现函数分为两部分来看,
首先是375~384之间的代码:
~~~
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
if (
!tolua_isusertable(tolua_S,1,"HSprite",0,&tolua_err) ||
!tolua_isstring(tolua_S,2,0,&tolua_err) ||
!tolua_isnoobj(tolua_S,3,&tolua_err)
)
goto tolua_lerror;
else
#endif
~~~
这里是对参数类型的判断:
tolua_isusertable 是否为”第三个参数”自定义类型
tolua_isstring 是否为字符串类型
tolua_isnoobj 结束(没有参数的判断)
然后是386~392之间的代码段:
~~~
const char* pszFileName = ((const char*) tolua_tostring(tolua_S,2,0));
{
HSprite* tolua_ret = (HSprite*) HSprite::createHSprite(pszFileName);
int nID = (tolua_ret) ? tolua_ret->m_uID : -1;
int* pLuaID = (tolua_ret) ? &tolua_ret->m_nLuaID : NULL;
tolua_pushusertype_ccobject(tolua_S, nID, pLuaID, (void*)tolua_ret,"HSprite");
}
~~~
这里是对脚本代码的解析从而调用的自定义创建的函数
最后我们修改 hello2.lua 脚本中的代码:(创建cocos2dx-lua项目默认Resources下自带的文件)
在脚本最后”– run”下代码修改如下:
~~~
-- run
local sceneGame = CCScene:create()
-- sceneGame:addChild(createLayerFram())
-- sceneGame:addChild(createLayerMenu())
sceneGame:addChild(createHimiLayer())
CCDirector:sharedDirector():runWithScene(sceneGame)
~~~
这里Himi注视了另个layer的添加,添加了自己的Layer
~~~
sceneGame:addChild(createHimiLayer())
~~~
然后将Himi自定义方法添加在脚本中,代码如下:
~~~
local function createHimiLayer()
local layerH = CCLayer:create()
local _font = CCLabelTTF:create("Himi_(cocos2dx-Lua)教程","Arial",33)
_font:setPosition(230,280)
layerH:addChild(_font)
--创建自定义类型精灵
local hsprite = HSprite:createHSprite("himi.png")
hsprite:setPosition(100,100)
hsprite:setScale(1.5)
hsprite:setRotation(45)
layerH:addChild(hsprite)
return layerH
end
~~~
创建自己定义的精灵,然后进行调用缩放,旋转,设置坐标函数。
ok,运行后的接图如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd027ec0e1.png)
好了,本篇就到这里,童鞋们感觉本站好的文章记得分享,转播散播哦
【Cocos2d-X(2.x) 游戏开发系列之二】cocos2dx最新2.0.1版本跨平台整合NDK+Xcode,Xcode编写&编译代码,Android导入打包运行即可!
最后更新于:2022-04-01 10:15:55
本站文章均为[ 李华明Himi ](http://www.himigame.com/about-himi)原创,转载务必在明显处注明:
转载自[【黑米GameDev街区】](http://www.himigame.com/) 原文链接: [http://www.himigame.com/cocos2dx-v2-0/962.html](http://www.himigame.com/cocos2dx-v2-0/962.html "【Cocos2d-X(2.x)")
[☞ 点击订阅 ☜](http://list.qq.com/cgi-bin/qf_invite?id=acfc24e272cc4a26debf3b3866edb626a9ea3fc80fd8893c) 本博客最新动态!及时将最新博文通知您!
前段时间有事情不在北京也很少上网所以一直没有更新博客,那么今天Himi向大家分享一下最新cocos2dx 2.0.1版本整合Xcode 编译运行Android的博文;
首先介绍下cocos2dx 2.0:
**主要特点:**
使用opengl es2.0支持CocosBuilder集成了一些扩展,比如CCControlExtension, CCNotification, CCListView实现javascript 绑定了,你可以参考”testjs”项目只支持iOS,Android和win32平台
**注意事项:**
请使用ndk-r7b或更高版本来编写android本地代码。Android自2.2版本起支持opengl es2.0
没有整合过之前cocos2dx版本的童鞋可以请先移步到 [【iOS-cocos2d-X 游戏开发之七】整合Cocos2dX的Android项目到Xcode项目中,Xcode编写&编译代码,Android导入打包运行即可!](http://www.himigame.com/android-game/667.html)
**详细整合步骤如下:**
步骤1. 首先Xcode安装最新cocos2dx版本:cocos2d-2.0-rc2-x-2.0.1,然后Xcode创建一个cocos2dx项目,这里Himi设定项目名:MercenaryLegend
项目路径如下图所示:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd02649685.jpg "1")](http://www.himigame.com/wp-content/uploads/2012/08/1.jpg)
步骤**2.然后通过终端创建Android项目(不会的童鞋请移步到 [【iOS-cocos2d-X 游戏开发之七】整合Cocos2dX的Android项目到Xcode项目中,Xcode编写&编译代码,Android导入打包运行即可!](http://www.himigame.com/android-game/667.html) );这里Himi设定项目名:MerLeg4Android
步骤**3.将创建的MerLeg4Android项目下的proj.android文件夹(只要这个proj.android文件夹),整个copy到你MercenaryLegend项目下,与MercenaryLegend下的Classes、ios、libs同一级目录 ,如下图所示:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd02685f45.jpg "2")](http://www.himigame.com/wp-content/uploads/2012/08/2.jpg)
步骤4.打开刚copy到MercenaryLegend下的proj.android下的build_native.sh 文件,修改3个配置如下:
NDK_ROOT= 这里是你NDK的路径
COCOS2DX_ROOT= 这里是你cocos2dx 2.0 引擎包所在的根路径
GAME_ROOT= 这里是你Xcode创建的cocos2dx项目根路径
Himi配置如下:
NDK_ROOT=/Users/Himi/Documents/HimiWork/AndroidTools/android-ndk-r7b
COCOS2DX_ROOT=/Users/Himi/Documents/cocos2d-2.0-rc2-x-2.0.1
GAME_ROOT=/Users/Himi/Documents/HimiWork/MercenaryLegend/MercenaryLegend
步骤5.打开终端,然后首先cd到你Xcode项目下的pro.android目录下,如下:
cd /Users/Himi/Documents/HimiWork/MercenaryLegend/MercenaryLegend/proj.android
cd xcode项目下的proj.android目录
步骤6.继续在终端输入:./build_native.sh
OK,如果以上步骤都正确,那么终端开始编译代码,正确编译成功的话则在最后出现如下语句:
StaticLibrary : libcocosdenshion.a
SharedLibrary : libgame.so
Install : libgame.so => libs/armeabi/libgame.so
如下图:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd026ad3b2.jpg "4")](http://www.himigame.com/wp-content/uploads/2012/08/4.jpg)
OK,编译成功后,打开你的Eclipse将你Xcode项目下的proj.android项目导入吧:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd026d0602.jpg "5")](http://www.himigame.com/wp-content/uploads/2012/08/5.jpg)
最后,真机运行(这里使用的是魅族M9 分辨率960×640)如下截图:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd02705d92.jpg "6")](http://www.himigame.com/wp-content/uploads/2012/08/6.jpg)
整合步骤来看,比cocos2dx 2.0之前的版本整合简单的很多,哈哈。好了,就先这样吧。有什么问题,童鞋们评论留言交流吧;
【Cocos2d-X(2.x) 游戏开发系列之一】cocos2dx(v2.x)与(v1.x)的一些常用函数区别讲解!在2.x版CCFileData类被去除等
最后更新于:2022-04-01 10:15:53
本站文章均为[ 李华明Himi ](http://www.himigame.com/about-himi)原创,转载务必在明显处注明:
转载自[【黑米GameDev街区】](http://www.himigame.com/) 原文链接: [http://www.himigame.com/cocos2dx-v2-0/938.html](http://www.himigame.com/cocos2dx-v2-0/938.html "【Cocos2d-X(2.x)")
[☞ 点击订阅 ☜](http://list.qq.com/cgi-bin/qf_invite?id=acfc24e272cc4a26debf3b3866edb626a9ea3fc80fd8893c) 本博客最新动态!及时将最新博文通知您!
cocos2dx v2.0版本发布一段时间了,现在最新版本是 cocos2d-2.0-rc2-x-2.0.1 ;这段时间Himi对2.x的更新版也有关注,也尝试使用过,发现不少地方都有改动,对于Himi最新项目快到尾声的考虑,所以也没有更新引擎到最新。那么今天开始Himi将陆续使用最新v2.x版本的一些东东,同步更新一些经常使用的改动以及值得注意的地方发博文出来与大家共享;
在之前我们使用cocos2dx 1.x版本中,我们都知道,创建一个CCObject类,都是类名然后::类名去除CC这个规律来创建和初始化,但是这一条在Cocos2dx 2.x版本就不行了,在cocos2dx 2.x版本中初始化和创建类基本都是 create 关键字开头创建。
首先我们来看第一个改动: CCLayer初始化
自定义Layer,类名:World
~~~
.h中:
1.x版本Layer函数
LAYER_NODE_FUNC(World);
2.x版本Layer函数
LAYER_CREATE_FUNC(World);
~~~
~~~
.cpp中:
1.x版本的重写函数:
CCScene* World::scene()
{
CCScene *scene = CCScene::node();
World *layer = World::node();
scene->addChild(layer);
return scene;
}
2.x版本的重写函数:
CCScene* World::scene()
{
CCScene *scene = CCScene::create();
World *layer = World::create();
scene->addChild(layer);
return scene;
}
~~~
然后我们看第二个常用的CCArray的初始化:
~~~
1.x版本的CCArray创建:
CCArray*array = CCArray::array();
2.x版本的CCArray创建:
CCArray*array = CCArray::create();
~~~
第三个我们看文件路径相关CCFileUtils函数使用:
~~~
1.x版本的使用:
const char* fullpath = cocos2d::CCFileUtils::fullPathFromRelativePath(patha.c_str());
2.x版本的使用:
const char* fullpath = cocos2d::CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(patha.c_str());
~~~
第四个精灵的创建:
~~~
1.x中精灵的创建:
CCSprite *sp = CCSprite::spriteWithFile("himi.png");
2.x中精灵的创建:
CCSprite *sp = CCSprite::create("himi.png");
~~~
第五个注册触屏事件监听:
~~~
1.x中注册:
CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate(this, 0, false);
2.x中注册:
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 0, false);
~~~
第六个粒子相关
~~~
1.x粒子创建和设置自动释放设置
CCParticleSystem *tempSystem = CCParticleSystem::particleWithFile("himi.plist");
tempSystem->setIsAutoRemoveOnFinish(true);
2.x粒子创建和设置自动释放设置
CCParticleSystem *tempSystem = CCParticleSystemQuad::create("himi.plist");
tempSystem->setAutoRemoveOnFinish(true);<span style="color:#ff0000;">
</span>
~~~
第七个:CCFileData 类去除了:
~~~
1.x的CCFileData的使用:
cocos2d::CCFileData fileDataClip(const char *pszFileName, const char *pszMode);
2.x中CCFileData被删除,直接使用如下函数即可代替:
CCFileUtils::sharedFileUtils()->getFileData(const char *pszFileName, const char *pszMode, unsigned long *pSize)
~~~
第八个 Action 动作使用与创建:
~~~
1.x动作的创建与使用:
this->runAction(CCSequence::actions(
CCMoveTo::actionWithDuration(ccpDistance(this->getPosition(), target) / velocity,
target),
CCCallFunc::actionWithTarget(this, callfunc_selector(Player::removeTarget))
,NULL));
2.x的动作创建和使用:
this->runAction(CCSequence::create(
CCMoveTo::create(ccpDistance(this->getPosition(), target) / velocity,
target),
CCCallFunc::create(this, callfunc_selector(Player::removeTarget))
,NULL));
~~~
其实以上这几个例子比较有代表性了,其他的一些区分我想大家也能找到不一定的规律。那么本篇对于cocos2dx v2.0版本的差异就讲述到这,后续如果Himi还发现比较重点区分的地方也一定会博文分享出来的。
【Cocoa(mac) Application 开发系列之四】动作编辑器(Cocos2dx)制作流程详解及附上响应鼠标滚轴事件、反转坐标系、导入/创建资源目录等知识点代码!
最后更新于:2022-04-01 10:15:51
本站文章均为[ 李华明Himi ](http://www.himigame.com/about-himi)原创,转载务必在明显处注明:
转载自[【黑米GameDev街区】](http://www.himigame.com/) 原文链接: [http://www.himigame.com/iphone-cocos2dx/911.html](http://www.himigame.com/iphone-cocos2dx/911.html "【Cocoa(mac)")
[☞ 点击订阅 ☜](http://list.qq.com/cgi-bin/qf_invite?id=acfc24e272cc4a26debf3b3866edb626a9ea3fc80fd8893c) 本博客最新动态!及时将最新博文通知您!
又要说好久没更新博文啦,咳咳….
其实博文中对于游戏开发,从j2me-android-ios ,从Client端-Server端 ,从jbox-cocos2d-cocos2dx 基本都囊括了,当然还有一些iap 防破解、GCC、架构等等,以及现在编辑器开发的博文,基本上貌似游戏开发不缺少什么了 = =..。当然后期应该会陆续上一些Unity3D博文吧。
到此,可以说Himi一直以来一个人能做个手机网游的目标基本实现了,废话不再这里多说了,等有时间把自己的历程更新下,在[Himi浅谈4年游戏开发de自学历程!(仅供参考)【2012年5月21号更新】](http://www.himigame.com/himistudy/382.html) 中再详细说吧,本节为一些做游戏且对编辑器不是很熟悉的童鞋简单介绍下动作编辑器的制作详细步骤,仅供参考啦;
努力分享、共同进步!
首先对于游戏编辑器,这里概述下,在游戏开发中呢 大家难免会接触到一些编辑器,例如常见的“动作编辑器(动编)”、“地图编辑器”、“脚本编辑器”、“粒子编辑器”、“物理编辑器”、“场景编辑器”、“任务编辑器”等等等等..其实这些编辑器的初衷都一样,缩短游戏开发周期,简化开发工作复杂度等,将程序工作分离出来;
这里举例“动作编辑器”,动作编辑器的作用主要有以下几点:
1.减少游戏内存 (图片复用率提高)
2.便于替换游戏角色动作图片资源(比如制作Avatar系统等)
3.工作分离
对于工作分离,比如,动作编辑器有了之后,至于游戏中每个角色的动作啦,特效啦,攻击距离啦,碰撞区域啦,帧间隔啦等等都可以直接交给非程序部门负责,程序部门直接负责拿来编辑器生成的文件进行解析操作使用即可啦;
大概叙述了一下,那么下面Himi来讲解基于Mac os 开发 Cocos2dx动编的全流程;
步骤如下: (这里Himi分为两个大步骤,1.切片视图 动画视图)
裁切区域,如下图:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd02510193.png "1")](http://www.himigame.com/wp-content/uploads/2012/06/15.png)
1.左侧“资源目录” 右侧“切片操作区”以及最下方的“切片展示区” Himi都使用的NSScrollView,原因如下:
资源目录由于你无法预知用户加载进来的资源目录图片数量,所以需要一个可滚动的视图来比较合适;
而其他两个是因为Himi加入了鼠标滚轴放大视图的功能;
接收鼠标滚轴事件分为两步:
1)让当前的View重写接收事件函数:
~~~
-(BOOL)acceptsFirstResponder{
return YES;
}
~~~
2)重写鼠标滚轴事件:
~~~
- (void)scrollWheel:(NSEvent *)theEvent{
//鼠标滚轴事件
}
~~~
一般情况下在苹果系统下的坐标系都是向右是X++,向上Y++,但是有的童鞋习惯与向下Y++吧,所以这里Himi给出反转坐标系的方法:如下:重写函数:
~~~
-(BOOL)isFlipped{
return YES;
}
~~~
话题转回来,对于切片操作区域呢,资源目录下的每张图片都应该对应着一个数组,用于存放切片的Rect,这个很容易想到不解释了;
至于展示区其实就是对应将每个图片对应的数组Rect都切出新图成为新切片放置 “切片展示区”即可;
可能大家有1个疑惑:如何显示一个界面让用户导入资源呢?
OK,其实很eazy,使用 NSOpenPanel 这个类即可实现啦。很简单给与提示即可。
当然啦 为了方便大家 给出一段使用代码吧:
~~~
NSOpenPanel *oPanel = [NSOpenPanel openPanel];
[oPanel setCanChooseDirectories:YES]; //可以打开目录
[oPanel setCanChooseFiles:NO]; //不能打开文件(我需要处理一个目录内的所有文件)
[oPanel setTitle:@"选择动编操作的文件夹"];
if ([oPanel runModal] == NSOKButton) { //如果用户点OK
}
~~~
这样会使用了吧 = =。再不会的请面壁谢谢;
效果如下:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd02569a14.png "2")](http://www.himigame.com/wp-content/uploads/2012/06/23.png)
需要主要一点,为了方便美术等进行裁切时候有误差等细节调整,那么Himi在“切片操作视图”每个切片矩形的右下角有个“小矩形”,拖动这个小矩形即可改变其大小啦 ,很方便的啦 嘿嘿
OK,切片视图貌似没有太多的东西,我们下面来看主要的“动作视图”:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd025840f8.png "1")](http://www.himigame.com/wp-content/uploads/2012/06/16.png)
基本上直接看上面这张图已经讲解的很清晰了 。。
这里说几个技术点:
1.视图没说的,最下方的还是切片视图,这里的视图都是由“切片视图”切好的直接拿过来使用的。以及上方的View都是用的NSScrollView进行使用的,Himi说过了,都是因为要做成右键缩放功能,你懂的。
2.动作序列,帧序列,以及切片序列都是使用的NSTableView 来做的;很eazy 的;
其实说实话,Himi做这个编辑器大部门时间都花在这里了。其实功能不少,都是隐藏各种右键,还有呢就是与“切片视图”的数据一定要对应并且大家一定要做的时候理清思路和架构,以及代码,类的设计要考虑清楚,否则说实话…不然会各种晕。
3.关于右侧的角度、帧率、坐标、缩放比例等就不说了,很简单的NSLable和NSTextField啦。
OK,最后,我们看下最后一个视图,这个视图貌似隐藏的比较深,是个动作播放视图的观察视图。通过“动画视图的”的播放动作触发显示的。
如下图:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd025bb0a7.png "1")](http://www.himigame.com/wp-content/uploads/2012/06/17.png)
大家要注意,这个播放动作展示的视图,Himi建议单独拿出来一个NSWindow来做,这样两者可以同时观察和修改!
这个动作查看的视图可以自动更新你在“动画视图”的调整;这个比较海皮;
而后,重点就是你数据的存储和编译出你cocos2dx或者其他平台的文件即可。这里还是要提醒大家!
c++属于小端数,Java属于大端数,这个千万别忘记了,否则你数据怎么都不对 哈哈。
好啦,再往后咱们就设计下我们这个编辑器的“ About ”,如下效果:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd026050a1.png "19 copy")](http://www.himigame.com/wp-content/uploads/2012/06/19-copy.png)[
](http://www.himigame.com/wp-content/uploads/2012/06/18.png)
设计这个相当容易,我们只需要修改一个地方即可:
找你项目下的 Supporting Files 文件夹下的Credits.rtf文件,双击打开编辑好保存,就OK啦。!
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd02629549.png "1")](http://www.himigame.com/wp-content/uploads/2012/06/110.png)
最后的最后呢,就是打包的啦。这里不上图了。(Himi Xcode Version:4.3.2)
找到Xcode菜单的Product -> Archive , 然后在Organizer界面->Distribution-> 选中 Export as 并选择Application OK,Next-> Don’t Re-sign ->Next 后面就是导出你放置的目录啦!
好啦,不写了,就这里吧,都凌晨3:29了。 咳咳。睡觉睡觉。晚安 各位;
【iOS-cocos2d-X 游戏开发之十六】配置你的Cocos2dx项目编译后的Android自动使用(-hd)高清图&设置Android自适应屏幕、缩放比例方法!
最后更新于:2022-04-01 10:15:49
本站文章均为[ 李华明Himi ](http://www.himigame.com/about-himi)原创,转载务必在明显处注明:
转载自[【黑米GameDev街区】](http://www.himigame.com/) 原文链接: [http://www.himigame.com/android-game/799.html](http://www.himigame.com/android-game/799.html "【iOS-cocos2d-X")
[☞ 点击订阅 ☜](http://list.qq.com/cgi-bin/qf_invite?id=acfc24e272cc4a26debf3b3866edb626a9ea3fc80fd8893c)** 本博客最新动态!及时将最新博文通知您!
本篇主要介绍Cocos2dx项目开发过程中或者说项目务必遇到的一些知识点(ps.貌似Himi博客写的都是务必的 ![](image/56fca5151dd1e.gif) Himi认为写别人没写的才更容易吸引人不是~)
OK,不多说废话,第一个介绍的是修改项目配置让你的Android项目支持自适应屏幕;其实关于Android项目自适应屏幕这个问题,Himi实在不想再多费口舌,一方面因为Himi之前博文有说过,另外一方面现在Android开源缘故造成分辨率泛滥也成必然。大家注意做项目尽可能使用相对位置,别写死坐标,另外一点就是针对流行分辨率做适应就好了,如果你们公司很有必要铺Android市场的量,那么只能一个一个分辨率去搞了=。 = Himi身为Kjava(J2me)一路走过来的Dev来说,我是在是对自适应虐到习惯…..
1\.咳咳,本不想说,回到正题,那么对于Cocos2dx中如何设置项目Android版自适应,其实很easy,直接在编译好的Android项目中如下路径查找:
your Project name/Jni/helloworld/main.cpp
OK,找到main.cpp后双击打开,然后看到如下代码段:
~~~
// if you want to run in WVGA with HVGA resource, set it
view->create(480, 320); // Please change it to (320, 480);if you're in portrait mode.
~~~
view->create(480,320);默认关闭的,这里打开即可;其实Himi也是从cocos2dx引擎框架中看到的,打开你的任意一个cocos2dx引擎框架的项目,然后打开AppDelegate.cpp 文件,就能看到:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd024bd237.png)](http://www.himigame.com/wp-content/uploads/2012/04/333333.png)
2\.下面继续介绍如何让你的cocos2dx-Android项目设置缩放比例,一样很easy,设置代码如下:
~~~
CCDirector::sharedDirector()->setContentScaleFactor(2.0);`
~~~
默认值是1.0,缩放2倍,从下面这两张图可以明显看出设置后的区别:(点击放大图片)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd024d0884.png)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd024eab02.png)
为了便于后续讲解更容易理解,那么这里Himi博文讲解使用的两行图片这里先给出,大家先看下:
rect.png 规格: 40 * 40 | rect-hd.png 规格:80*80
【iOS-cocos2d-X 游戏开发之十五】Cocos2dx中响应Android的Back(返回)与Menu(小房子)事件&&Cocos2dx自动释放粒子内存函数!
最后更新于:2022-04-01 10:15:46
本站文章均为[ 李华明Himi ](http://www.himigame.com/about-himi)原创,转载务必在明显处注明:
转载自[【黑米GameDev街区】](http://www.himigame.com/) 原文链接: [http://www.himigame.com/iphone-cocos2dx/792.html](http://www.himigame.com/iphone-cocos2dx/792.html "【iOS-cocos2d-X")
[☞ 点击订阅 ☜](http://list.qq.com/cgi-bin/qf_invite?id=acfc24e272cc4a26debf3b3866edb626a9ea3fc80fd8893c) 本博客最新动态!及时将最新博文通知您!
本篇介绍两个常用知识,一个是粒子的自动释放机制函数,因为不少童鞋说cocos2dx为什么没有? 其实是没找到,这里特意拿出来说下!另外一个是如何响应Android的Menu菜单和Back返回事件的方法;
首先对于Cocos2dx中对于粒子自动释放的函数Himi这里给出,不少童鞋都在群里或者论坛有问道!
~~~
//添加一个粒子特效
CCParticleSystem *tempSystem = ARCH_OPTIMAL_PARTICLE_SYSTEM::particleWithFile("test.plist");
//tempSystem.positionType=kCCPositionTypeRelative;//备注1
tempSystem->setPositionType(kCCPositionTypeFree);
this->addChild(tempSystem);
tempSystem->setIsAutoRemoveOnFinish(true);//这里就是粒子自动释放的函数啦。~
~~~
这里setIsAutoRemoveOnFinish(bool bl);函数就是自动释放内存的函数啦,那么这里细心的童鞋会看到**备注1 ,**备注函数是设置粒子的移动类型为相对位置,具体请查看:
[【iOS-Cocos2d游戏开发之十】添加粒子系统特效并解决粒子特效与Layer之间的坐标问题;](http://www.himigame.com/iphone-cocos2d/472.html)
OK,下面Himi介绍如何在Cocos2dx中处理Android os的设备的Menu和Back案件的响应处理;
其实很简单,步骤如下:
1\.在需要响应的.h 中添加如下两个虚函数:
~~~
virtual void keyBackClicked();//Android 返回键
virtual void keyMenuClicked();//Android 菜单键
~~~
2\. .cpp类中实现如上两个函数即可,如下,Himi这里测试使用,World是个Layer
~~~
void World::keyBackClicked(){
CCLog("Android- KeyBackClicked!");
}
void World::keyMenuClicked(){
CCLog("Android- keyMenuClicked!");
}
~~~
3.初始化的时候记得开启:**this->setIsKeypadEnabled(true);**
OK,下面Himi将项目编译后运行Android版的项目:
运行结果如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd02491e5f.png)
然后Himi尝试点击Menu和Back按键,打印如上图所示,正常处理到了;
【iOS-cocos2d-X 游戏开发之十四】Xcode中c++&Object-C混编,详细介绍如何在cocos2dx中访问object函数以及Apple Api
最后更新于:2022-04-01 10:15:44
本站文章均为[ 李华明Himi ](http://www.himigame.com/about-himi)原创,转载务必在明显处注明:
转载自[【黑米GameDev街区】](http://www.himigame.com/) 原文链接: [http://www.himigame.com/iphone-cocos2dx/743.html](http://www.himigame.com/iphone-cocos2dx/743.html "【iOS-cocos2d-X")
[☞ 点击订阅 ☜](http://list.qq.com/cgi-bin/qf_invite?id=acfc24e272cc4a26debf3b3866edb626a9ea3fc80fd8893c)** 本博客最新动态!及时将最新博文通知您!
Cocos2dx系列博文的上一篇详细介绍了如何在Xcode中利用jni调用Android的Java层代码,还没有看过的童鞋,请移步到如下博文:
[【iOS-cocos2d-X 游戏开发之十三】详细讲解在Xcode中利用预编译并通过Jni调用Android的Java层代码(cocos2dx里访问调用Android函数)! ](http://www.himigame.com/iphone-cocos2dx/www.himigame.com/android-game/725.html)
本篇继续介绍另外一个在Cocos2dx中必经之路:在Cocos2dx中调用苹果Api以实现后期iOS的GameCenter和iap的相关操作, 那么Himi这里就跟大家简单分享探讨下;如何在Xcode中进行c++与oc混编吧~
参考王哥说的 SimpleAudioEngine 类;
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd02437ab2.png)
首先Himi建立了两个类,一个object-c ,一个c++,详细如下:
**HSpriteOC.h**
~~~
//
// HSpriteOC.h
// Oc_Cpp
//
// Created by Himi on 12-4-10.
// Copyright (c) 2012年 Himi. All rights reserved.
//
#import <Foundation/Foundation.h>
NSString * str;
@interface HSpriteOC
+(void) testLog;
+(void) testLogWithStr:(NSString*)_str;
+(void) hMessageBox:(NSString*)pszMsg title:(NSString*)pszTitle;
@end
~~~
**HSpriteOC.m**
~~~
//
// HSprite.m
// Oc_Cpp
//
// Created by Himi on 12-4-10.
// Copyright (c) 2012年 Himi. All rights reserved.
//
#import "HSpriteOC.h"
@implementation HSpriteOC
+(void) testLog{
str = @"Himi->string is: ";
NSLog(@"HSprite: testLog");
}
+(void) testLogWithStr:(NSString*)_str{
str = [NSString stringWithFormat:@"%@%@",str,_str];
NSLog(@"%@",str);
}
+(void) hMessageBox:(NSString*)pszMsg title:(NSString*)pszTitle{
UIAlertView * messageBox = [[UIAlertView alloc] initWithTitle: pszTitle
message: pszMsg
delegate: nil
cancelButtonTitle: @"OK"
otherButtonTitles: nil];
[messageBox autorelease];
[messageBox show];
}
@end
~~~
这个类比较简单,简单定义了几个静态函数,打印和显示一个提示框,不赘述,大家大概看下就可以了;
下面来看c++的类:
**HSpriteCPP.h**
~~~
//
// HSpriteCPP.h
// Oc_Cpp
//
// Created by Himi on 12-4-10.
// Copyright (c) 2012年 Himi. All rights reserved.
//
#ifndef Oc_Cpp_HSprite_h
#define Oc_Cpp_HSprite_h
#include "cocos2d.h"
using namespace cocos2d;
class HSpriteCPP:public cocos2d::CCSprite {
public:
static HSpriteCPP* hspriteWithFile(const char *spName);
void myInit();
virtual ~HSpriteCPP();
};
#endif
~~~
**HSpriteCPP.cpp**
~~~
//
// HSpriteCPP.mm
// Oc_Cpp
//
// Created by Himi on 12-4-10.
// Copyright (c) 2012年 Himi. All rights reserved.
//
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
#include "HSpriteOC.h"
#endif
#include "HSpriteCPP.h"
HSpriteCPP* HSpriteCPP::hspriteWithFile(const char *spName)
{
HSpriteCPP *pobSprite = new HSpriteCPP();
if (pobSprite && pobSprite->initWithFile(spName))
{
pobSprite->myInit();
pobSprite->autorelease();
return pobSprite;
}
CC_SAFE_DELETE(pobSprite);
return NULL;
}
void HSpriteCPP::myInit(){
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
//iOS代码
[HSpriteOC testLog];
[HSpriteOC testLogWithStr:@"wahaha"];
[HSpriteOC hMessageBox:@"cocos2dx调用oc函数" title:@"Himi"];
#else
//Android代码
#endif
}
HSpriteCPP::~HSpriteCPP(){
}
~~~
此类是个自定义精灵类,都是简单的创建等函数,其HSprite.cpp类的导入和在 myInit() 自定义初始化函数中都加入了预编译(#if #else #endif 对预编译不太了解的自定百度下吧),主要为了区别当前手机设备的平台区分做处理;而且在myInit()中我使用了object-c语法进行调用之前OC写的HSprite类函数;
其实通过观察以上两个类童鞋们估计很容易看出在xcode中cpp和oc如何混编;其实就是两点:
1.混编的类需要将其实现类(.cpp)改成(.mm)类,那么Xcode就会智能知道这个类混编类了,不用复杂的操作;
2.混编中cpp调用oc,其实就是各自使用各自语法即可,没差异!(最好对OC和c++都比较熟悉更效率)
然后Himi在HelloWorldScene.cpp中加入以下测试代码:
~~~
HSpriteCPP * sp =HSpriteCPP::hspriteWithFile("Icon.png");
sp->setPosition(ccp(CCDirector::sharedDirector()->getWinSize().width*0.5,CCDirector::sharedDirector()->getWinSize().height*0.5-100));
this->addChild(sp);
~~~
别忘记导入对应使用的类哦~OK,看运行效果:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd0246162f.png)
【iOS-cocos2d-X 游戏开发之十三】详细讲解在Xcode中利用预编译并通过Jni调用Android的Java层代码(cocos2dx里访问调用Android函数)!
最后更新于:2022-04-01 10:15:42
本站文章均为[ 李华明Himi ](http://www.himigame.com/about-himi)原创,转载务必在明显处注明:
转载自[【黑米GameDev街区】](http://www.himigame.com/) 原文链接: [http://www.himigame.com/android-game/725.html](http://www.himigame.com/android-game/725.html "【iOS-cocos2d-X")
[☞ 点击订阅 ☜](http://list.qq.com/cgi-bin/qf_invite?id=acfc24e272cc4a26debf3b3866edb626a9ea3fc80fd8893c) 本博客最新动态!及时将最新博文通知您!
很多看过之前Himi的[【iOS-cocos2d-X 游戏开发之七】整合Cocos2dX的Android项目到Xcode项目中,Xcode编写&编译代码,Android导入打包运行即可!](http://www.himigame.com/android-game/667.html) 博文的童鞋都在问我如何在Xcode中能调用Java的函数,比如在Android端需要加入广告、支付、等第三方平台或者单独处理的一些代码;那么本篇Himi分享如何在Cocos2dx中访问调用Android的Java层函数;
首先Himi大概的介绍一个类 JniHelper ;
此类主要用于Jni与Java层之间的相互访问的作用,那么此类的常用的一些函数这里首先介绍下,否则直接上代码不太容易吸收 ;
JniHelper 类常用函数:
1\. getStaticMethodInfo (四个参数,bool 返回值)
使用示例代码:
~~~
bool isHave = JniHelper::getStaticMethodInfo(minfo,"com/ai/ommr/OhMonsterMR4Android","testFun", "()V");
~~~
此函数主要用于获取Java定义的类静态函数是否存在,返回bool;
此函数有3个参数:
第一参数: minfo ->JniMethodInfo 类型,后面详细说;
第二个参数:类的路径。
第三个参数:方法名(第一参数类中的函数)
第四个参数:(参数)返回类型
关于第一个和第二个参数都比较容易理解,那么第三个参数需要重点介绍一下;例如你在Java中定义一个如下函数 public void helloJni(){};
那么getStaticMethodInfo函数第三个参数你应该传入 “()V” 表示此函数无参无返回值!
如果有这么一个函数: public int helloJni(int _int){return 823};
那么getStaticMethodInfo函数第三个参数你应该传入 “(I)I” 表示此函数需要传入一个int类型的参数并且返回一个int类型!
大概说两个童鞋们应该基本理解了,那么下面详细写一些对照表,留给大家对照;
参数、返回值样式对照表:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd023a7e7c.png)
这里的签名指的就是getStaticMethodInfo函数第三个参数中传入的样式;
2\. getMethodInfo 类似与第一个函数,只是对应非静态函数;此函数主要用于获取Java定义的类非静态函数是否存在,返回bool;
**JniMethodInfo** 类:
此类型主要用户保存类结构体,可以通过JniHelper类的getStaticMethodInfo函数实例化JniMethodInfo对象,从而使用实例的env属性调用CallStaticVoidMethod,CallVoidMethod,CallStaticIntMethod等函数进行对保存的类结构调用函数;
常用的函数如下:(静态系列函数)
1.CallStaticVoidMethod(classID,methodID);
2.CallStaticIntMethod(classID,methodID);
3~(n-1) ……省略掉,童鞋们一看就明白;
n. CallStaticObjectMethod(classID,methodID);
带参数的函数:(如int类型)
CallStaticVoidMethod(classID,methodID,int _int);
非静态系列函数:
1\. CallVoidMethod(jobj,methodID);
2. CallIntMethod(jobj,methodID);
3~(n-1) ……省略掉,童鞋们一看就明白;
n. CallStaticObjectMethod(jobj,methodID);
带参数的函数:(如int类型)
CallVoidMethod(classID,methodID,int _int);
这里讲解下几个参数函数:
1\. classID: 类ID , JniMethodInfo 对象包含此属性;
2\. methdID: 方法ID,JniMethodInfo 对象也包含此属性;
3\. jobj : java中Object类型,如自定义类,String…
非静态函数调用的时候,需要的是对象,所以与静态函数调用的第一个参数不同;
那么既然能调用Java的函数,那就能得到函数返回值,但是这里我们得到的返回值书写类型如下:
譬如返回int 类型,在Xcode中使用 jint 这种形式,更多形式如下:
int -> jint
…省略,大家一看就懂得;
object -> jobject
估计有的童鞋已经晕了,没关系,下面Himi写了一些例子代码,结合来看就一目了然啦。
Xcode中先导入如下头文件:
~~~
//使用预编译,对当前平台为Android时才会导入如下头文件
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include <jni.h>
#include "platform/android/jni/JniHelper.h"
#include <android/log.h>
#endif
~~~
示例代码段1:
Xcode 代码:
~~~
////静态函数示例1.无参数,无返回值---------------------------------$$$$$$-----------------------------
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) //判断当前是否为Android平台
JniMethodInfo minfo;//定义Jni函数信息结构体
//getStaticMethodInfo 次函数返回一个bool值表示是否找到此函数
bool isHave = JniHelper::getStaticMethodInfo(minfo,"com/ai/ommr/OhMonsterMR4Android","testFun", "()V");
if (!isHave) {
CCLog("jni:此函数不存在");
}else{
CCLog("jni:此函数存在");
//调用此函数
minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID);
}
CCLog("jni-java函数执行完毕");
#endif
~~~
Android(Java) 代码:
~~~
// 静态函数示例1.无参数,无返回值
public static void testFun() {
Log.e("Himi", "静态函数示例1.无参数,无返回值");
}
~~~
运行截图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd023d0648.png)
示例代码段2:
Xcode 代码:
~~~
//静态函数示例2.有参数,无返回值------------------------------$$$$$$$--------------------------------
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) //判断当前是否为Android平台
JniMethodInfo minfo;//定义Jni函数信息结构体
//getStaticMethodInfo 次函数返回一个bool值表示是否找到此函数
bool isHave = JniHelper::getStaticMethodInfo(minfo,
"com/ai/ommr/OhMonsterMR4Android", "testFunWithInt","(I)V");
if (!isHave) {
CCLog("jni:此函数不存在");
}else{
CCLog("jni:此函数存在");
//调用此函数
minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID,823);
}
CCLog("jni-java函数执行完毕");
#endif
~~~
Android(Java) 代码:
~~~
// 静态函数示例2.有参数,无返回值
public static void testFunWithInt(int _int) {
Log.e("Himi", "静态函数示例1.有参数,无返回值;传入的参数int=" + _int);
}
~~~
运行截图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd023e0bc3.png)
示例代码段3:
Xcode 代码:
~~~
//静态函数示例3.有参数,有返回值--------------------------------$$$$$$$--------------------------------
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) //判断当前是否为Android平台
JniMethodInfo minfo;//定义Jni函数信息结构体
//getStaticMethodInfo 次函数返回一个bool值表示是否找到此函数
bool isHave = JniHelper::getStaticMethodInfo(minfo,
"com/ai/ommr/OhMonsterMR4Android","testFunWithIntAndRtn","(I)I");
jint _int ;
if (!isHave) {
CCLog("jni:此函数不存在");
}else{
CCLog("jni:此函数存在");
//调用此函数
_int = minfo.env->CallStaticIntMethod(minfo.classID, minfo.methodID,823);
//尝试jint是否能正常接收返回的int值
JniMethodInfo minfo_ty;
bool isHave = JniHelper::getStaticMethodInfo(minfo_ty, "com/ai/ommr/OhMonsterMR4Android", "testFunWithInt", "(I)V");
if (isHave) {
minfo_ty.env->CallStaticVoidMethod(minfo_ty.classID, minfo_ty.methodID,_int);
}
}
CCLog("jni-java函数执行完毕");
#endif
~~~
Android(Java) 代码:
~~~
// 静态函数示例3.有参数,有返回值
public static int testFunWithIntAndRtn(int _int) {
Log.e("Himi", "静态函数示例1.有参数,有返回值;传入的参数int=" + _int);
return _int+1000;
}
~~~
运行截图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd023f270a.png)
示例代码段4:
Xcode 代码:
~~~
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) //判断当前是否为Android平台
JniMethodInfo minfo;//定义Jni函数信息结构体
//getStaticMethodInfo 次函数返回一个bool值表示是否找到此函数
bool isHave = JniHelper::getStaticMethodInfo(minfo, "com/ai/ommr/OhMonsterMR4Android","testFunWithStringAndRtn","(I)Ljava/lang/String;");
jobject jobj;
if (!isHave) {
CCLog("jni:此函数不存在");
}else{
CCLog("jni:此函数存在");
//调用此函数
jobj = minfo.env->CallStaticObjectMethod(minfo.classID, minfo.methodID,823);
}
CCLog("jni-java函数执行完毕");
#endif
~~~
Android(Java) 代码:
~~~
// 静态函数示例4.有参数,有返回值(String类型)
public static String testFunWithStringAndRtn(int _int) {
Log.e("Himi", "静态函数示例4.有参数,有返回值(String类型);int=" + _int);
return "yes,return 'String' is OK --by Himi";
}
~~~
运行截图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd0240e678.png)
示例代码段5:
Xcode 代码:
~~~
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) //判断当前是否为Android平台
JniMethodInfo minfo;//定义Jni函数信息结构体
//getStaticMethodInfo 次函数返回一个bool值表示是否找到此函数
bool isHave = JniHelper::getStaticMethodInfo(minfo,
"com/ai/ommr/OhMonsterMR4Android", //类的路径
"rtnActivity", //方法名
"()Ljava/lang/Object;"); //括号里的是参数,后面的是返回值。
jobject jobj;
if (isHave) {
jobj = minfo.env->CallStaticObjectMethod(minfo.classID, minfo.methodID);
}
CCLog("正确获取到 jobj");
//
isHave = JniHelper::getMethodInfo(minfo,
"com/ai/ommr/OhMonsterMR4Android", //类的路径
"nostaticFun", //方法名
"()V"); //括号里的是参数,后面的是返回值。
if (isHave) {
minfo.env->CallVoidMethod(jobj, minfo.methodID);
}
CCLog("jni-java函数执行完毕");
#endif
~~~
Java 代码:
~~~
//---- 函数示例之非静态函数调用
//(先获取个对象)
public static Activity actInstance;//定义单例
public static Object rtnActivity() {
return actInstance;
}
//使用此对象进行调用非静态函数
public void nostaticFun() {
Log.e("Himi", "no static Function is OK - By Himi");
}
~~~
运行截图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd02420939.png)
【iOS-cocos2d-X 游戏开发之十二】自定义Cocos2dx摇杆(增强Joystick),增加摇杆跟随用户触点作为摇杆坐标,让摇杆不再死板!
最后更新于:2022-04-01 10:15:39
本站文章均为[ 李华明Himi ](http://www.himigame.com/about-himi)原创,转载务必在明显处注明:
转载自[【黑米GameDev街区】](http://www.himigame.com/) 原文链接: [http://www.himigame.com/iphone-cocos2dx/721.html](http://www.himigame.com/iphone-cocos2dx/721.html "【iOS-cocos2d-X")
[☞ 点击订阅 ☜](http://list.qq.com/cgi-bin/qf_invite?id=acfc24e272cc4a26debf3b3866edb626a9ea3fc80fd8893c)** 本博客最新动态!及时将最新博文通知您!
对于虚拟摇杆在游戏开发中必不可少,Android方面的是由Himi自己实现封装的,大家可以移步到这里查看详细实现机制:
[【Android游戏开发二十四】360°平滑游戏摇杆(触屏方向导航) ](http://www.himigame.com/android-game/384.html)
那么在Cocos2d引擎已提供此摇杆类(Joystick),所以Himi也就懒得重写了,但是Cocos2dx中并没有封装,那么这里Himi给出Cocos2dx版的Joystick(HRocker类),并且Himi对此类添加了一个跟随用户触点作为摇杆坐标的功能!
这里不多说代码结构直接贴出源码,然后重点说下使用与方法参数,具体实现可以参考源码以及Android部分Himi的实现机制;
~~~
//
// HRocker.h
// RockerPro
//
// Created by Himi on 12-3-30.
// Copyright (c) 2012年 Himi. All rights reserved.
//
#ifndef RockerPro_HRocker_h
#define RockerPro_HRocker_h
#ifndef HRocker_H
#define HRocker_H
#include "cocos2d.h"
using namespace cocos2d;
class HRocker :public CCLayer {
public :
//初始化 aPoint是摇杆中心 aRadius是摇杆半径 aJsSprite是摇杆控制点 aJsBg是摇杆背景
static HRocker* HRockerWithCenter(CCPoint aPoint ,float aRadius ,CCSprite* aJsSprite,CCSprite* aJsBg,bool _isFollowRole);
//启动摇杆
void Active();
//解除摇杆
void Inactive();
private:
HRocker * initWithCenter(CCPoint aPoint ,float aRadius ,CCSprite* aJsSprite,CCSprite* aJsBg,bool _isFollowRole);
CCPoint centerPoint;//摇杆中心
CCPoint currentPoint;//摇杆当前位置
bool active;//是否激活摇杆
float radius;//摇杆半径
CCSprite *jsSprite;
bool isFollowRole;//是否跟随用户点击
CCPoint getDirection();
float getVelocity();
void updatePos(ccTime dt);
virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);
LAYER_NODE_FUNC(HRocker);
};
#endif
~~~
~~~
//
// HRocker.cpp
// RockerPro
//
// Created by Himi on 12-3-30.
// Copyright (c) 2012年 Himi. All rights reserved.
//
#include "HRocker.h"
void HRocker::updatePos(ccTime dt){
jsSprite->setPosition(ccpAdd(jsSprite->getPosition(),ccpMult(ccpSub(currentPoint, jsSprite->getPosition()),0.5)));
}
//启动摇杆
void HRocker::Active()
{
if (!active) {
active=true;
schedule(schedule_selector(HRocker::updatePos));//添加刷新函数
CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate(this, 0,false);//添加触摸委托
}else {
}
}
//解除摇杆
void HRocker::Inactive()
{
if (active) {
active=false;
this->unschedule(schedule_selector(HRocker::updatePos));//删除刷新
CCTouchDispatcher::sharedDispatcher()->removeDelegate(this);//删除委托
}else {
}
}
//摇杆方位
CCPoint HRocker::getDirection()
{
return ccpNormalize(ccpSub(centerPoint, currentPoint));
}
//摇杆力度
float HRocker::getVelocity()
{
return ccpDistance(centerPoint, currentPoint);
}
HRocker* HRocker:: HRockerWithCenter(CCPoint aPoint ,float aRadius ,CCSprite* aJsSprite,CCSprite* aJsBg,bool _isFollowRole){
HRocker *jstick=HRocker::node();
jstick->initWithCenter(aPoint,aRadius,aJsSprite,aJsBg,_isFollowRole);
return jstick;
}
bool HRocker::ccTouchBegan(CCTouch* touch, CCEvent* event)
{
if (!active)
return false;
this->setIsVisible(true);
CCPoint touchPoint = touch->locationInView(touch->view());
touchPoint = CCDirector:: sharedDirector()->convertToGL(touchPoint);
if(!isFollowRole){
if (ccpDistance(touchPoint, centerPoint) > radius){
return false;
}
}
currentPoint = touchPoint;
if(isFollowRole){
centerPoint=currentPoint;
jsSprite->setPosition(currentPoint);
this->getChildByTag(88)->setPosition(currentPoint);
}
return true;
}
void HRocker::ccTouchMoved(CCTouch* touch, CCEvent* event)
{
CCPoint touchPoint = touch->locationInView(touch->view());
touchPoint = CCDirector:: sharedDirector()->convertToGL(touchPoint);
if (ccpDistance(touchPoint, centerPoint) > radius)
{
currentPoint =ccpAdd(centerPoint,ccpMult(ccpNormalize(ccpSub(touchPoint, centerPoint)), radius));
}else {
currentPoint = touchPoint;
}
}
void HRocker::ccTouchEnded(CCTouch* touch, CCEvent* event)
{
currentPoint = centerPoint;
if(isFollowRole){
this->setIsVisible(false);
}
}
HRocker* HRocker::initWithCenter(CCPoint aPoint ,float aRadius ,CCSprite* aJsSprite,CCSprite* aJsBg,bool _isFollowRole){
isFollowRole =_isFollowRole;
active = false;
radius = aRadius;
if(!_isFollowRole){
centerPoint =aPoint;
}else{
centerPoint =ccp(0,0);
}
currentPoint = centerPoint;
jsSprite = aJsSprite;
jsSprite->setPosition(centerPoint);
aJsBg->setPosition(centerPoint);
aJsBg->setTag(88);
this->addChild(aJsBg);
this->addChild(jsSprite);
if(isFollowRole){
this->setIsVisible(false);
}
this->Active();//激活摇杆
return this;
}
~~~
创建使用方法很eazy,如下函数:
HRocker* HRocker:: HRockerWithCenter(CCPoint aPoint ,float aRadius ,CCSprite* aJsSprite,CCSprite* aJsBg,bool _isFollowRole);
第一个参数aPoint:摇杆中心点的坐标;
第二个参数aRadius: 摇杆的半径
第三个参数:aJsSprite :摇杆的图片资源精灵
第四个参数:aJsBg: 摇杆背景图片资源精灵
第五个参数:isFollowRole:是否让摇杆永远跟随用户触屏点(Himi新添加的功能)
这里对于最后一个参数可能很多童鞋不太理解,那么这里大概描述下:
对于手机游戏而言,虚拟的摇杆并不是一个很好的操作方式,但是为了满足游戏的必要操作无疑必须使用,但是虚拟摇杆存在两方面问题:
1.没有实体感觉,对于用户来说不能触觉上明显分清当前自己有没有触摸在虚拟摇杆上或者当前是按下还是按上等;
2.遮挡部分游戏画面,这一点不仅仅式虚拟摇杆的存在造成遮挡画面,用户使用虚拟摇杆时更加的造成游戏画面被挡住;
3.不容易操作,过于死板,不小心就触发了虚拟摇杆区域之外;
对于虚拟摇杆存在的第一方面没有实体感我们没法改进,但是,是否触摸到虚拟键盘这个可以使用手机震动提示;第二,三方面的问题在当前iOS手机游戏上很多公司采用了让虚拟摇杆跟随用户触屏点为摇杆中心的方式!并且用户不触摸屏幕默认不显示虚拟摇杆;这么一来不仅让游戏画面能在不需要操作的时候尽可能的完美展示外,还能有效避免用户触摸不到摇杆判断区域的问题;摇杆跟随功能就是Himi封装Rocker类创建时第五个参数 isFollowRole,传入true即可跟随!
如果还有童鞋听的不是很清楚,那么将Himi这个Rocker类进行拷贝自己项目中,然后使用以下代码进行创建使用尝试下吧:
~~~
CCSprite *spRocker=CCSprite::spriteWithFile("CloseSelected.png");//摇杆
CCSprite *spRockerBG=CCSprite::spriteWithFile("rockerBg.png");//摇杆背景
HRocker *rocker=HRocker::HRockerWithCenter(ccp(210.0f,130.0f),50.0f ,spRocker ,spRockerBG,false);//创建摇杆
this->addChild(rocker);//摇杆添加到layer中
//this 是个layer
CCSprite *spRocker2=CCSprite::spriteWithFile("CloseSelected.png");//摇杆
CCSprite *spRockerBG2=CCSprite::spriteWithFile("rockerBg.png");//摇杆背景
HRocker* rocker2=HRocker::HRockerWithCenter(ccp(210.0f,130.0f),50.0f ,spRocker2 ,spRockerBG2,true);//创建摇杆
this->addChild(rocker2);//摇杆添加到layer中
~~~
截图如下:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd023766ca.png)](http://www.himigame.com/wp-content/uploads/2012/03/31123.png)
更多的自定义大家可以自行尝试,Himi就不添加了,毕竟每款游戏都有不同需求~
【iOS-cocos2d-X 游戏开发之十一】New CCSprite()带来的错误&使用CCUserDefault及pvr.ccz在Cocos2dx中要注意!
最后更新于:2022-04-01 10:15:37
本站文章均为[ 李华明Himi ](http://www.himigame.com/about-himi)原创,转载务必在明显处注明:
转载自[【黑米GameDev街区】](http://www.himigame.com/) 原文链接: [http://www.himigame.com/iphone-cocos2dx/703.html](http://www.himigame.com/iphone-cocos2dx/703.html "【iOS-cocos2d-X")
[☞ 点击订阅 ☜](http://list.qq.com/cgi-bin/qf_invite?id=acfc24e272cc4a26debf3b3866edb626a9ea3fc80fd8893c)** 本博客最新动态!及时将最新博文通知您!
本章讲解的是几个细节问题,但是此细节有可能导致一系列问题,那么今天Himi与童鞋们共同交流分享下;
一. 对于CCSprite 与 CCNode 是都很常用的类,那么基础使用方法这里不赘述,但是CCSprite 与 CCNode在进行添加子类时可能有些童鞋遇到过问题,下面我们详细看下:
首先给出代码段1:
~~~
CCSprite* sp = CCSprite::spriteWithFile("Icon.png");
sp->setPosition(ccp(200,200));
sp->addChild(CCSprite::spriteWithFile("Icon.png"));
this->addChild(sp);
//this 是个Layer
~~~
索引图片名直接创建个精灵A,然后A add 子类精灵,最后将A添加Layer中,没说的,eazy,不解释;
那么再看下面代码段2:
~~~
CCSprite* sp = new CCSprite();
sp->setPosition(ccp(200,200));
sp->addChild(CCSprite::spriteWithFile("Icon.png"));
this->addChild(sp);
//this 是个Layer
~~~
顺序和功能跟代码段1一样,但当你运行后发现程序异常!原因是sp 中并没有真正的初始化,而仅仅简单的申请了一块内存而已;
解决方案:仍然调用CCSprite公开的几个构造函数即可;
~~~
CCSprite* sp = new CCSprite();
//CCSprite构造下
sp->initWithFile("Icon.png");
sp->setPosition(ccp(200,200));
sp->addChild(CCSprite::spriteWithFile("Icon.png"));
this->addChild(sp);
~~~
此问题比较容易理解,但是当我们出现以上问题时,肯定是想不想创建一个精灵时传入资源,所以会使用new方式来使用,那么如果你想创建一个没有贴图的精灵可以按照如下方式实现:
~~~
CCSprite* sp = (CCSprite*)CCNode::node();
sp->setPosition(ccp(200,200));
sp->addChild(CCSprite::spriteWithFile("Icon.png"));
this->addChild(sp);
~~~
利用Node::node初始化数据后强转给CCSprite子类即可;话说回来既然如此到不如使用CCNode来的方便?!没错,如果你想封装一个精灵,并往里添加子精灵,那么创建这个精灵如不想使用资源创建,那么推荐使用CCNode,(自定义类型继承CCNode),另一方面如果创建这个精灵可以索引到资源,可以继承CCSprite,没问题;
值得注意的是:
当你自定义A类继承精灵类CCSprite,使用new形式创建A类一个精灵后并addChild子精灵,一般不会造成程序异常,而且正常运行在iOS上,但是当你编译后运行Android上之后会发现精灵都是白色块的情况,如下前后对比图:
iOS运行截图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd023436e5.png)
编译Android运行截图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd0236113f.png)
二. 在cocos2dx中使用CCUserDefault的时候要注意,官方微薄也已提示,在当你setStringxxx、setBoolxxx等数据后要调用CCUserDefault的flush()函数,只有当你flush()了,才会将数据以xml格式保存本地App->Documents目录中!
三.在cocos2dx中使用pvr.ccz格式的图片时候跟cocos2d的使用有一点区别,在cocos2d中加载pvr.ccz的图片资源不用去管大小和尺寸(当然为了兼容老代机器,推荐1024*1024),但是在Cocos2dx中当你的pvr.ccz的资源大小宽高不是正方形的话(2 的N次幂),会在控制台提示警告,并且!虽然程序能正常运行,但是在编译后Android上无法正常显示,是显示图片为白色块!所以这点也要注意;
【iOS-cocos2d-X 游戏开发之十】自定义CCSprite/Layer/CCNode及静态类模版&自定义类细节说明&Cocos2dx触屏事件讲解
最后更新于:2022-04-01 10:15:35
本站文章均为[ 李华明Himi ](http://www.himigame.com/about-himi)原创,转载务必在明显处注明:
转载自[【黑米GameDev街区】](http://www.himigame.com/) 原文链接: [http://www.himigame.com/iphone-cocos2dx/699.html](http://www.himigame.com/iphone-cocos2dx/699.html "【iOS-cocos2d-X")
[☞ 点击订阅 ☜](http://list.qq.com/cgi-bin/qf_invite?id=acfc24e272cc4a26debf3b3866edb626a9ea3fc80fd8893c) 本博客最新动态!及时将最新博文通知您!
这里Himi又来了一篇基础篇…..不是因为充博文数量,而是Cocos2dX一个超级群里不少童鞋都有问过很基础的问题,Himi估计这些童鞋要么之前对C++不熟悉,要么就是之前没有接触过Cocos2d,而直接转入了Cocos2dx的开发行列中仂,这里首先给这些童鞋三个建议:
1.Cocos2dx的教程确实不多,但是因为Cocos2dx是Cocos2d一个繁衍的引擎,基本API没有大的变化,你只要将Cocos2d熟悉仂,那么X对你来说不会难的….
2.还是再次提醒大家多看看引擎自带的例子,对于很多基础问题,源码完全能照顾到的,不管是cocos2d的,还是cocos2dX的,但是如果你还不知道Cocos2d/X的引擎示例在哪….无语下…(在引擎包下哦)
3.还有不少童鞋抱着“赶快做完这个功能”的态度来工作来求助。。。这样不好的说,学开发跟学习一样,不思考就来答案的东西真的是浮云。。。建议大家遇到瓶颈的时候考虑下吧,禁忌饭来张口的童鞋;
入正题,开头就说仂本篇主要为了照顾下基础的童鞋,但是本篇也会介绍些在cocos2dx引擎中一些自定义类的模版的创建和使用;介绍过程中顺便把基础也说下,这样一举两得;
首先我们先看一个自定义精灵类如何来做:(先上代码,然后细说)
~~~
自定义精灵模版:
SpriteClassModel.h
//
// SpriteClassModel.h
// Oh!MonsterMR
//
// Created by Himi on 12-3-7.
// Copyright (c) 2012年 Augustimpression. All rights reserved.
//
#ifndef Oh_MonsterMR_SpriteClassModel_h
#define Oh_MonsterMR_SpriteClassModel_h
#include "cocos2d.h"
using namespace cocos2d;
class SpriteClassModel : public cocos2d::CCSprite ,public CCTouchDelegate
{
public:
static SpriteClassModel* spriteModelWithFile(const char *spName); //静态创建(内存自动释放)
void myInit();//自定义初始化函数
virtual ~SpriteClassModel();
//重写触屏相关函数----
virtual void onEnter();
virtual void onExit();
virtual bool ccTouchBegan(CCTouch* touch, CCEvent* event);
virtual void ccTouchMoved(CCTouch* touch, CCEvent* event);
virtual void ccTouchEnded(CCTouch* touch, CCEvent* event);
};
#endif
~~~
~~~
SpriteClassModel.cpp
//
// SpriteClassModel.cpp
// Oh!MonsterMR
//
// Created by Himi on 12-3-7.
// Copyright (c) 2012年 Augustimpression. All rights reserved.
//
#include "SpriteClassModel.h"
using namespace cocos2d;
SpriteClassModel* SpriteClassModel::spriteModelWithFile(const char *spName)
{
SpriteClassModel *pobSprite = new SpriteClassModel();
if (pobSprite && pobSprite->initWithFile(spName))//备注1
{
pobSprite->myInit();
pobSprite->autorelease();
return pobSprite;
}
CC_SAFE_DELETE(pobSprite);
return NULL;
}
void SpriteClassModel::myInit(){
CCLOG("----测试init函数");
}
//---SpriteClassModel 按键-----------------
// 别忘记加入 TouchDispatcher ,TouchDispatcher一般放在onenter 绑定,onexit 删除
bool SpriteClassModel::ccTouchBegan(CCTouch* touch, CCEvent* event)
{
CCLog("%s","精灵111~ccTouchBegan");
return true;
}
void SpriteClassModel::ccTouchMoved(CCTouch* touch, CCEvent* event)
{
//获取当前用户触屏点坐标并将3d坐标映射2d坐标系
CCPoint touchPoint = touch->locationInView( touch->view() );
touchPoint = CCDirector::sharedDirector()->convertToGL( touchPoint );
//touchPoint.x, getPosition().y
if(touchPoint.x> CCDirector::sharedDirector()->getWinSize().width/2){
CCLog("%s","精灵22~~ccTouchMoved");
}
}
void SpriteClassModel::ccTouchEnded(CCTouch* touch, CCEvent* event)
{
CCLog("%s","精灵33~~~ccTouchEnded");
}
//---SpriteClassModel 起始结束的生命周期函数----
void SpriteClassModel::onEnter()
{
//注册监听 1.dele类,2.优先级,3.YES为阻碍其他类的move 和 end
CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate(this, 0, true);
CCSprite::onEnter();
}
void SpriteClassModel::onExit()
{
//移除监听
CCTouchDispatcher::sharedDispatcher()->removeDelegate(this);
CCSprite::onExit();
}
SpriteClassModel::~SpriteClassModel(){
CCLOG("---析构函数");
}
~~~
这个自定义精灵类的模版重点介绍3个点:
1.构造函数;
static SpriteClassModel* spriteModelWithFile(const char *spName);
此构造函数的实现机制,Himi仿制Cocos2dX引擎来做的,一方面使用方便,另外一方面内存自动释放;
2.在构造函数中,有个备注1,还没有看到的童鞋请回头看下,这里我使用的父类CCSprite的索引文件名的创建方式,如下:
initWithFile(<#const char *pszFilename#>);
那么这里可以根据需要继续自定义使用父类的索引帧缓存文件名的方式,如下:
initWithSpriteFrameName(<#const char *pszSpriteFrameName#>);
当然仂为了方便大家可以在自定义中多写几种构造,留好接口使用即可,那么这里Himi还自定义仂一个myInit()的函数,这个函数主要用途两点:1.用以清晰化操作,代码易读 2.当此类多种构造函数并在的时候可以将此自定义精灵类的独有属性设置等都放入此函数中,代码复用;
3.关于触屏方面的,代码都有注释,不多说,唯一要提醒大家的就是当前Himi的这个精灵是采用触屏事件分发的机制进行处理的,而不是直接开启触屏开关(此方法太过于局限),那么如果对于这两种不太熟悉的童鞋请移步到如下文章:(cocos2d与cocos2dx类似)
【iOS-Cocos2d游戏开发之五】多触点与触屏事件详解(单一监听、事件分发)
关于CCNode的自定义这个不多说仂,类似自定义精灵只是继承父类有所变化,模版如下:
~~~
NodeClassModel.h
//
// NodeClassModel.h
// Oh!MonsterMR
//
// Created by Himi on 12-3-7.
// Copyright (c) 2012年 Augustimpression. All rights reserved.
//
#ifndef Oh_MonsterMR_NodeClassModel_h
#define Oh_MonsterMR_NodeClassModel_h
#include "cocos2d.h"
class NodeClassModel : public cocos2d::CCNode
{
public:
static NodeClassModel* NodeModelWithInit();
void myInit();
virtual ~NodeClassModel();
};
#endif
~~~
~~~
NodeClassModel.cpp
//
// NodeClassModel.cpp
// Oh!MonsterMR
//
// Created by Himi on 12-3-7.
// Copyright (c) 2012年 Augustimpression. All rights reserved.
//
#include "NodeClassModel.h"
using namespace cocos2d;
NodeClassModel* NodeClassModel::NodeModelWithInit()
{
NodeClassModel *mynode = new NodeClassModel();
if (mynode)
{
mynode->myInit();
mynode->autorelease();
return mynode;
}
CC_SAFE_DELETE(mynode);
return NULL;
}
void NodeClassModel::myInit(){
CCLOG("----测试init函数");
}
NodeClassModel::~NodeClassModel(){
CCLOG("---析构函数");
~~~
下面介绍下自定义CCLayer的模版:(先上代码,后剖析)
~~~
LayerClassModel.h
//
// LayerClassModel.h
// Oh!MonsterMR
//
// Created by Himi on 12-3-7.
// Copyright (c) 2012年 Augustimpression. All rights reserved.
//
#ifndef Oh_MonsterMR_LayerClassModel_h
#define Oh_MonsterMR_LayerClassModel_h
#include "cocos2d.h"
using namespace cocos2d;
class LayerClassModel : public cocos2d::CCLayer
{
public:
static LayerClassModel*sharedLayerClassModel(void);
virtual bool init();
static cocos2d::CCScene* scene();
virtual ~LayerClassModel();
LAYER_NODE_FUNC(LayerClassModel);//用于允许CCScene::node();
};
#endif
~~~
~~~
//
// LayerClassModel.cpp
// Oh!MonsterMR
//
// Created by Himi on 12-3-7.
// Copyright (c) 2012年 Augustimpression. All rights reserved.
//
#include "LayerClassModel.h"
using namespace cocos2d;
static LayerClassModel* shredStatic;//备注1
static bool s_bFirstRun = true;//备注2
LayerClassModel* LayerClassModel::sharedLayerClassModel(void)
{
if (s_bFirstRun)//备注3
{
s_bFirstRun=false;
}
return shredStatic;
}
CCScene* LayerClassModel::scene()
{
// 'scene'自动释放内存
CCScene *scene = CCScene::node();
// 'layer' 自动释放内存
LayerClassModel *layer = LayerClassModel::node();
scene->addChild(layer);
return scene;
}
bool LayerClassModel::init(){
if ( !CCLayer::init() )
{
return false;
}
shredStatic=this;
//初始化数据
return true;
}
LayerClassModel::~LayerClassModel(){
CCLOG("---析构函数");
}
~~~
关于自定义CCLayer模版中,我这里将触屏相关代码删除了,与自定义精灵的触屏实现一样没区别;
重点介绍3点:
1.CCLayer和CCScene 两个类都是自动释放内存,这个提示在创建cocos2dx引擎模版默认HelloWorldScene类中已经有提示,可能大家没有注意到,所以这里强调下;
2.CCLayer 经常会用于整个大的UI设计或者游戏中,他都作为主要的父类来使用,将游戏元素CCSprite,CCMenu,粒子等都添加到Layer中,所以此类在游戏中的交互性应该最强,所以在LayerClassModel模版中我添加了一个静态函数用于方便访问和其他类进行数据交互用(备注1),如下函数:
static LayerClassModel*sharedLayerClassModel(void);
好处不用多说,如CCDirector一样使用,如:CCDirector::sharedDirector()->getWinSize();
3.备注2和备注3的作用防止当前类被重复创建,这个提醒下,不多介绍仂;
自定义的静态类与将CCLayer静态出来的方式类似,只是一个继承CCLayer一个不需要继承任何父类,模版如下:
~~~
StaticClassModel.h
//
// StaticClassModel.h
// Oh!MonsterMR
//
// Created by Himi on 12-3-8.
// Copyright (c) 2012年 Augustimpression. All rights reserved.
//
#ifndef Oh_MonsterMR_StaticClassModel_h
#define Oh_MonsterMR_StaticClassModel_h
#include "cocos2d.h"
class StaticClassModel
{
public:
static StaticClassModel* sharedStaticClassModel(void);
void myInit();
virtual ~StaticClassModel();
//
void testFun();
};
#endif
StaticClassModel.cpp
//
// StaticClassModel.cpp
// Oh!MonsterMR
//
// Created by Himi on 12-3-8.
// Copyright (c) 2012年 Augustimpression. All rights reserved.
//
#include "StaticClassModel.h"
using namespace cocos2d;
static StaticClassModel shredStatic;
static bool s_bFirstRun = true;
StaticClassModel* StaticClassModel::sharedStaticClassModel(void)
{
if (s_bFirstRun)
{
shredStatic.myInit();
s_bFirstRun=false;
}
return &shredStatic;
}
void StaticClassModel::myInit(){
1
shredStatic=this;
CCLOG("----测试Static init函数");
}
void StaticClassModel::testFun(){
CCLOG("-测试fstatic fun-");
}
StaticClassModel::~StaticClassModel(){
CCLOG("---析构函数");
}
~~~
基本代码都该有的注释都已经写仂,很容易理解,本篇供Cocos2dx的新童鞋一个参考吧
【iOS-cocos2d-X 游戏开发之九】Cocos2dx利用CCSAXParser解析xml数据&CCMutableDictionary使用与注意!
最后更新于:2022-04-01 10:15:32
本站文章均为[ 李华明Himi ](http://www.himigame.com/about-himi)原创,转载务必在明显处注明:
转载自[【黑米GameDev街区】](http://www.himigame.com/) 原文链接: [http://www.himigame.com/iphone-cocos2dx/694.html](http://www.himigame.com/iphone-cocos2dx/694.html "【iOS-cocos2d-X")
[☞ 点击订阅 ☜](http://list.qq.com/cgi-bin/qf_invite?id=acfc24e272cc4a26debf3b3866edb626a9ea3fc80fd8893c) 本博客最新动态!及时将最新博文通知您!
本章Himi给大家分享如何在Cocos2dX中解析xml数据;对于数据存取有很多方式,流文件,plist,xml等,那么为了跨平台更好的支持,Himi想到之前写的CCUserDefault 存储数据一节,Cocos2dx自带的存储类,一旦存入数据都会以xml格式进行保存,适用于iOS、Android等平台,所以这里Himi使用xml进行游戏的一些数据录入 = =.. 另外一方面Himi本章节也是基于Cocos2dx引擎代码进行的一次简单对xml数据解析的封装;
为了更保险的去考虑跨平台,所以对于xml存储这块的解析,也做了些搜索,最后发现王哥(王哲-cocos2dx引擎作者)也有给我们提示过,内容如下:
~~~
cocos2dx里面集成了libxml2,ios上会调用sdk里面内置的,
android和win32上则带了已经编译好的静态/动态库。
你可以参考CCSAXParser里面的代码来使用libxml2
~~~
那么既然如此,就对Cocos2dx引擎源码的CCSAXParser类进行了剖析,那么这里Himi,先给出代码,然后再详细讲解下:
Himi封装的HXmlParse类:
~~~
HXmlParse.h
//
// HXmlParse.h
// HAnimation
//
// Created by Himi on 12-3-22.
// Copyright (c) 2012年 Augustimpression. All rights reserved.
//
#ifndef HAnimation_HXmlParse_h
#define HAnimation_HXmlParse_h
#include "cocos2d.h"
#include "CCSAXParser.h"
#include "CCObject.h"
#include "CCMutableDictionary.h"
using namespace cocos2d;
class CC_DLL HXmlParse :public CCObject, public CCSAXDelegator
{
public:
static HXmlParse * parserWithFile(const char *tmxFile);
bool initHXmlParse(const char* xmlName);
// 使用 CCSAXDelegator 重写3个回调函数
void startElement(void *ctx, const char *name, const char **atts);
void endElement(void *ctx, const char *name);
void textHandler(void *ctx, const char *ch, int len);
std::string root_name;
bool isJumpHeadData;
CCMutableDictionary<std::string,CCString*> *mDic;
private:
std::string startXmlElement;//用来记录每个key前字段
std::string endXmlElement;//用来记录每个key后字段
std::string currString;//记录每个value的值
};
#endif
~~~
~~~
HXmlParse.cpp
//
// HXmlParse.cpp
// HAnimation
//
// Created by Himi on 12-3-22.
// Copyright (c) 2012年 Augustimpression. All rights reserved.
//
#include "HXmlParse.h"
#include "CCSAXParser.h"
HXmlParse * HXmlParse::parserWithFile(const char *tmxFile)
{
HXmlParse *pRet = new HXmlParse();
if(pRet->initHXmlParse(tmxFile))
{
pRet->autorelease();
return pRet;
}
CC_SAFE_DELETE(pRet);
return NULL;
}
bool HXmlParse::initHXmlParse(const char* xmlName)
{
mDic = new CCMutableDictionary<std::string,CCString*>();
CCSAXParser _par;
if (false == _par.init("UTF-8") )
{
CCLog("-----请使用utf-8格式!");
return false;
}
_par.setDelegator(this);
const char* _path =CCFileUtils::fullPathFromRelativePath(xmlName);
return _par.parse(_path);
}
//回调函数
void HXmlParse::startElement(void *ctx, const char *name, const char **atts)
{
CC_UNUSED_PARAM(ctx);
startXmlElement = (char*)name;
if(!isJumpHeadData){//跳过数据头
CCLog("------跳过root name");
isJumpHeadData=true;
root_name=startXmlElement;
return;
}
// CCLog("-startElement----%s",startXmlElement.c_str());
}
void HXmlParse::endElement(void *ctx, const char *name)
{
CC_UNUSED_PARAM(ctx);
endXmlElement = (char*)name;
if(endXmlElement==root_name){//数据尾
CCLog("读取xml结束");
isJumpHeadData=false;
root_name="";
return;
}
// CCLog("-endElement----%s",endXmlElement.c_str());
}
//键值对的结束字段
void HXmlParse::textHandler(void *ctx, const char *ch, int len)
{
CC_UNUSED_PARAM(ctx);
currString=string((char*)ch,0,len);
CCString *ccStr =new CCString();//备注3
ccStr->m_sString=currString;
if(root_name!=""){
mDic->setObject(ccStr,startXmlElement);
CCLog("-----key:%s, value:%s",startXmlElement.c_str(),mDic->objectForKey(startXmlElement)->m_sString.c_str());
}
// CCLog("-textHandler----%s",currString.c_str());
}
~~~
OK,代码呢我们先从.h中来说,首先我们使用CCSAXDelegator,为了让CCSAXParser解析数据后将数据回调给如下三个函数:
~~~
// 使用 CCSAXDelegator 重写3个回调函数
void startElement(void *ctx, const char *name, const char **atts);
void endElement(void *ctx, const char *name);
void textHandler(void *ctx, const char *ch, int len);
~~~
startElement 函数解析的是xml的每个key前字段
textHandler 函数解析出来的是xml每个key对应的value值
endElement 函数解析出来的是xml的每个key后字段
这里Himi随便写了一个xml来做测试,himi.xml,如下:
~~~
<?xml version="1.0" encoding="utf-8"?><himiTestData><key1>1000</key1><key2>娃哈哈</key2><key3>82.3</key3><key4>4000</key4><key5>himi</key5><key6>true</key6></himiTestData>
~~~
那么CCSAXParser类解析第一次回调 startElement 是读取的是root name(xml数据头标识名称->“<himiTestData>”),然后才读取正式数据key和value,最后读取的也是xml数据尾标识名称“</himiTestData>”
当然在Himi封装的HXmlParse类中对于数据标识的读取都跳过了,使用变量isJumpHeadData来处理的;
其他的都很容易没有什么可说的,主要要说还有一点就是关于CCMutableDictionary的使用,对于此类主要结构是形成map&NSMutableDictionary类似是个键值对容器,key-value;那么使用时候要注意4点:
1.比如Himi解析数据后都会默认将key和value数据存放在CCMutableDictionary中,那么这里我肯定传入的是两个string,但是细心的童鞋会发现代码中第二个并不是std::string,而是CCString对象,嗯 没错,CCMutableDictionary要求传入的是CCObject对象而不是基本类型!CCString中有m_sString这个属性,所以转换起来也是很方便的;
2.使用CCMutableDictionary进行添加数据setObject的时候要注意此函数的两个参数:
~~~
bool setObject(_ValueT pObject, const _KeyT& key)
{
pair<CCObjectMapIter, bool > pr;
pr = m_Map.insert( Int_Pair(key, pObject) );
if(pr.second == true)
{
pObject->retain();
return true;
}
return false;
}
~~~
上面这个是cocos2dx引擎中源码中setObject函数实现代码,这里可以很清晰的看到,第一个参数表示《CCObject》,第二个参数才是《Key》!这点对于之前做过java开发的我来说比较郁闷,因为一般都是key在第一个参数。。。。。
3.请大家仔细看HXmlParse.cpp类中的备注3 ,当你使用CCMutableDictionary的setObject函数的时候,务必要注意,此函数的存入的CCObject参数,引擎中实现代码是对你这个CCObject进行retain()的一个内存地址引用!也就是说这里不要使用一个成员变量来使用,否则你从CCMutableDictionary取出来的数据全部是你最后一个CCObject数据!
4.CCMutableDictionary是cocos2d-x自己封装的类,功能类似NSMutableDictionary。但是Himi通过测试发现!它有一点和NSMutableDictionary是不一样的。NSMutableDictionary的setObjectForKey方法是:如果发现这个key已经存在于字典中的时候,它会自动用新的object覆盖掉原有的object。而CCMutableDictionary由于它是使用map实现的字典功能,而在map里面,如果key已存在,是不会用新的object覆盖掉原有object的。在使用CCMutableDictionary的时候需要特别注意这一点。
HXmlParse解析xml类使用方法很简单:
~~~
#include "HXmlParse.h"
HXmlParse::parserWithFile("himi.xml");
~~~
然后Himi为了证实此解析类在Android也可以正常运行,那么这里Himi将读出的数据展示在画面上,iOS运行截图如下:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd022d8f69.png "11111")](http://www.himigame.com/wp-content/uploads/2012/03/111112.png)
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd023148d6.png "2")](http://www.himigame.com/wp-content/uploads/2012/03/24.png)
【COCOS2DX-LUA 脚本开发之一】在Cocos2dX游戏中使用Lua脚本进行游戏开发(基础篇)并介绍脚本在游戏中详细用途!
最后更新于:2022-04-01 10:15:30
本站文章均为[ 李华明Himi ](http://www.himigame.com/about-himi)原创,转载务必在明显处注明:
转载自[【黑米GameDev街区】](http://www.himigame.com/) 原文链接: [http://www.himigame.com/iphone-cocos2dx/681.html](http://www.himigame.com/iphone-cocos2dx/681.html "【iOS-cocos2d-X")
[☞ 点击订阅 ☜](http://list.qq.com/cgi-bin/qf_invite?id=acfc24e272cc4a26debf3b3866edb626a9ea3fc80fd8893c)** 本博客最新动态!及时将最新博文通知您!
对于游戏公司而言,采用游戏脚本lua、python等进行开发也很常见,但是很多童鞋对脚本并没有很熟悉的概念,本篇则向大家简单介绍脚本的用途以及在Cocos2dx基础用法;
Lua和python这些详细介绍的话,请不太熟悉的童鞋自行百度百科哈,那么对于lua和python则是两个常用的脚本语言,lua相对于python而言,lua比较轻量级罢了,而其他区别就不多说了,但是为什么本章要讲解lua的原因则有两点,首先第一:cocos2dx 游戏引擎内嵌lua,第二点:自从“令人愤怒的小鸟”火起来之后,国内很多都偏向于使用lua了=。 =
那么对于脚本的用途这里也大概说两点:
1\. 脚本在手游中是类于“大脑”的功能,所有游戏相关的逻辑代码一般都放在脚本中,而客户端(前台)的代码都则属于“肢体”,也可以说是“播放器”,作用只是用户展示出UI界面的功能;那么脚本的作用那么不仅仅如此,比如地图数据等都可以利用脚本使用;
2\. 脚本在手机网游中的作用尤为重要,比如一款网游“Himi”没有使用脚本,如果“Himi”1.0版本在发布后突然发现客户端出现一些棘手的bug需要修复,那么你想修改那么也要等待再次更新客户端重新提交发布才可以解决,这样会流失一大批用户,而且游戏每次更新也会流失掉部分用户,这是肯定的;但是如果“Himi”这款网游使用脚本的话,那么解决此类问题很eazy,比如我在“Himi”游戏中的逻辑代码都放在脚本a.lua 中,那么如果a.lua逻辑中哪里出现了问题,我们直接可以将修复后的a.lua脚本更新至服务器中,因为一般脚本都会定义version号,比如a.lua有bug的version:1.0,那么我们修复后的a.lua version改成1.1,当用户每次启动游戏的时候,客户端都会将脚本的version与服务器脚本version做对比,当server端脚本version号比当前脚本新,那么自动下载并覆盖当前脚本,OK,问题解决;不仅仅如此,比如游戏中做个活动呀,换个图片呀等等都可以即使更新,而不是每次修改前端代码都要重新发布新的游戏版本,造成一些损失!
OK,不再多说了,下面我们来介绍在Cocos2dx中对于lua脚本的一些简单使用,首先我们通过新建一个Cocos2dx-lua模版项目,默认此模版中有个示例,童鞋们可以直接运行项目看效果,但是大家可能会郁闷在class中完全找不到任何相关的代码?!?那就对了,因为所有逻辑代码都放置在了lua脚本中,项目启动后直接解析的一个名称为hello.lua的脚本!
打开项目的Resources仔细找下,有没有发现有 hello.lua 合hello2.lua两个脚本文件?!OK,就是这里拉。 那么对于cocos2dx_lua demo的例子脚本我这里不多说比较容易,但是肯定不太熟悉的童鞋比较疑惑,那么Himi这里重新整理了一份简单的示例脚本代码,大家可以直接将如下代码直接复制到hello.lua中看效果;代码如下:
~~~
require "hello2" -- 包含hello2这个脚本
-- 注视语句
-- 基本上调用的cocos2dx函数和类的时候就是以cocos2d.*这样子来用
-- 注意2:function 关键字定义函数,end结束函数
-- 打印
cocos2d.CCLuaLog("脚本hello开始运行... " .. myadd(3, 5))
-- 创建一个Scene
sceneForWorld = cocos2d.CCScene:node()
-- 创建一个Layer
layerForWorld = cocos2d.CCLayer:node()
sceneForWorld:addChild(layerForWorld)
-- 创建一个精灵
spriteForWorld = cocos2d.CCSprite:spriteWithFile("Icon.png")
layerForWorld:addChild(spriteForWorld)
-- 获取屏幕宽高
screenSize=cocos2d.CCDirector:sharedDirector():getWinSize()
-- 设置精灵坐标
spriteForWorld:setPosition(cocos2d.CCPoint(screenSize.width*0.5,screenSize.height*0.5))
-- 设置精灵缩放2倍
spriteForWorld:setScale(2)
-- 添加一个CCLabelTTF (!!!!!!备注!!!!!!)
myLableTTF =cocos2d.CCLabelTTF:labelWithString("Himi- Lua 基础","Helvetica-Bold",24)
myLableTTF:setPosition(cocos2d.CCPoint(screenSize.width*0.5,screenSize.height*0.5+100))
sceneForWorld:addChild(myLableTTF)
-- 添加一个CCLabelTTF
myLableTTF2 =cocos2d.CCLabelTTF:labelWithString("上面icon跟随用户触屏位置","Helvetica-Bold",24)
myLableTTF2:setPosition(cocos2d.CCPoint(screenSize.width*0.5,screenSize.height*0.5-100))
sceneForWorld:addChild(myLableTTF2)
-- @@@@@@@@@@触摸事件
--开启触摸
layerForWorld:setIsTouchEnabled(true)
-- 注册触摸事件
layerForWorld.__CCTouchDelegate__:registerScriptTouchHandler(cocos2d.CCTOUCHBEGAN, "btnTouchBegin")
layerForWorld.__CCTouchDelegate__:registerScriptTouchHandler(cocos2d.CCTOUCHMOVED, "btnTouchMove")
layerForWorld.__CCTouchDelegate__:registerScriptTouchHandler(cocos2d.CCTOUCHENDED, "btnTouchEnd")
-- touch handers
pointBegin = nil
function btnTouchBegin(e)
cocos2d.CCLuaLog("btnTouchBegin")
local v = e[1]
local pointMove = v:locationInView(v:view())
pointMove = cocos2d.CCDirector:sharedDirector():convertToGL(pointMove)
spriteForWorld:setPosition(cocos2d.CCPoint(pointMove.x,pointMove.y))
end
function btnTouchMove(e)
cocos2d.CCLuaLog("btnTouchMove")
local v = e[1]
local pointMove = v:locationInView(v:view())
pointMove = cocos2d.CCDirector:sharedDirector():convertToGL(pointMove)
spriteForWorld:setPosition(cocos2d.CCPoint(pointMove.x,pointMove.y))
end
function btnTouchEnd(e)
cocos2d.CCLuaLog("btnTouchEnd")
end
-- @@@@@@@@@@触摸结束
--动态小狗
winSize = cocos2d.CCDirector:sharedDirector():getWinSize()
FrameWidth = 105
FrameHeight = 95
textureDog = cocos2d.CCTextureCache:sharedTextureCache():addImage("dog.png")
frame0 = cocos2d.CCSpriteFrame:frameWithTexture(textureDog, cocos2d.CCRectMake(0, 0, FrameWidth, FrameHeight))
frame1 = cocos2d.CCSpriteFrame:frameWithTexture(textureDog, cocos2d.CCRectMake(FrameWidth*1, 0, FrameWidth, FrameHeight))
spriteDog = cocos2d.CCSprite:spriteWithSpriteFrame(frame0)
spriteDog:setPosition(cocos2d.CCPoint(100, winSize.height/4*3))
layerForWorld:addChild(spriteDog)
animFrames = cocos2d.CCMutableArray_CCSpriteFrame__:new(2)
animFrames:addObject(frame0)
animFrames:addObject(frame1)
animation = cocos2d.CCAnimation:animationWithFrames(animFrames, 0.5)
animate = cocos2d.CCAnimate:actionWithAnimation(animation, false);
spriteDog:runAction(cocos2d.CCRepeatForever:actionWithAction(animate))
--自定义函数
function prForHimi()
cocos2d.CCLuaLog("reFresh function")
--取消选择器
--cocos2d.CCScheduler:sharedScheduler():unscheduleScriptFunc("prForHimi")
end
--使用选择器进行函数更新
--cocos2d.CCScheduler:sharedScheduler():scheduleScriptFunc("prForHimi", 1, false)
--循环语句
for i=0,4,1 do
for j=0,4,2 do
cocos2d.CCLuaLog("for loop",i)
end
end
-- 避免内存泄漏
collectgarbage( "setpause", 100)
collectgarbage( "setstepmul", 5000)
-- 播放背景音乐
--CocosDenshion.SimpleAudioEngine:sharedEngine():playBackgroundMusic("background.mp3", true)
-- 播放音效
--CocosDenshion.SimpleAudioEngine:sharedEngine():preloadEffect("effect1.wav")
-- run整个scene
cocos2d.CCDirector:sharedDirector():runWithScene(sceneForWorld)
cocos2d.CCLuaLog("脚本hello正常执行结束... " .. myadd(3, 5))
~~~
运行效果图如下:[![](image/56fc9a9d6b0ca.png)](http://www.himigame.com/wp-content/uploads/2012/03/11113.png)
对于Himi上面给出的自己修改后的代码注视写的狠清楚了 =。 = 所以不多加赘述,但是Himi这里需要还要详细说一点;
脚本lua等一般都示通过中间层(解析)进行与前端代码(Cocos2dX封装的引擎类库)交互,所以很多方法名称可能发生了改变,那么对于不太熟悉的童鞋我们如何下手?
OK,如刚才的代码中有个“备注”,不知道细心童鞋们看到没有,这里是添加了一个CCLabelTTF ,假如我们不知道它的构造函数是否有修改,或者说参数忘记都是什么了,那么请打开你项目的libs文件夹,然后打开lua文件夹,继续打开cocos2dx_support文件夹找到 LuaCocos2d.cpp文件打开,(注意这个文件代码很多,打开较慢)然后你会看到很多方法的定义与实现!
那么假如我们来找 CCLabelTTF的构造方法,那么搜一下如下语句:
~~~
tolua_beginmodule(tolua_S,"CCLabelTTF");
~~~
你将发现此类下方一大批类似的代码:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd022a93dc.png)
没错这里就是此类的所有是lua-cocos2dx之间的转换函数定义,比如常用的CCLabelTTF构造函数:
~~~
tolua_function(tolua_S,"labelWithString",tolua_Cocos2d_cocos2d_CCLabelTTF_labelWithString01);
~~~
此函数第一参数大家不用理会,第二个参数表示我们使用cocos2d/x时调用的函数名称,后面则是lua-cocos2dx之间的转换函数实现代码,大家可以继续搜索第三个参数或者按住command然后点击第三个参数找到其函数实现代码:
~~~
/* method: labelWithString of class cocos2d::CCLabelTTF */
#ifndef TOLUA_DISABLE_tolua_Cocos2d_cocos2d_CCLabelTTF_labelWithString01
static int tolua_Cocos2d_cocos2d_CCLabelTTF_labelWithString01(lua_State* tolua_S)
{
tolua_Error tolua_err;
if (
!tolua_isusertable(tolua_S,1,"cocos2d::CCLabelTTF",0,&tolua_err) ||
!tolua_isstring(tolua_S,2,0,&tolua_err) ||
!tolua_isstring(tolua_S,3,0,&tolua_err) ||
!tolua_isnumber(tolua_S,4,0,&tolua_err) ||
!tolua_isnoobj(tolua_S,5,&tolua_err)
)
goto tolua_lerror;
else
{
const char* label = ((const char*) tolua_tostring(tolua_S,2,0));
const char* fontName = ((const char*) tolua_tostring(tolua_S,3,0));
float fontSize = ((float) tolua_tonumber(tolua_S,4,0));
{
cocos2d::CCLabelTTF* tolua_ret = (cocos2d::CCLabelTTF*) cocos2d::CCLabelTTF::labelWithString(label,fontName,fontSize);
tolua_pushusertype(tolua_S,(void*)tolua_ret,"cocos2d::CCLabelTTF");
}
}
return 1;
tolua_lerror:
return tolua_Cocos2d_cocos2d_CCLabelTTF_labelWithString00(tolua_S);
}
#endif //#ifndef TOLUA_DISABLE
~~~
在这里看到此函数转换过程,并可以很清楚看到:
~~~
cocos2d::CCLabelTTF* tolua_ret = (cocos2d::CCLabelTTF*) cocos2d::CCLabelTTF::labelWithString(label,fontName,fontSize);
~~~
到这里大家会很清楚需要的参数都是哪些,如果还不清楚,继续按住Command然后点击labelWithString进入cocos2dx引擎代码 CCLabelTTF.cpp中的此函数实现啦!
可能这部分有童鞋看得比较迷茫 =。 = 那么Himi来简化这些复杂来说:
解析lua脚本中的一句代码->通过解析层代码->将其转换并转到前端代码进行使用
那么当然此过程也可逆:
前端代码->通过解析层代码->使用lua脚本中东东
这里由于Himi对cocos2dx 中的lua还没有深入了解,所以不知是否过程可逆;
OK,基本就是这样,对于脚本的熟悉,主要还是在公司进行使用然后慢慢熟悉和熟练掌握的,本章主要需要童鞋们记住的是脚本的概念和作用!
【iOS-iap防护】验证用户付费收据!拒绝iap Cracker!拒绝iap Free!让iphone越狱用户无从下手!【2012年5月2日更新防护iap Free的方法】
最后更新于:2022-04-01 10:15:27
本站文章均为[ 李华明Himi ](http://www.himigame.com/about-himi)原创,转载务必在明显处注明:
转载自[【黑米GameDev街区】](http://www.himigame.com/) 原文链接: [http://www.himigame.com/iphone-cocos2d/673.html](http://www.himigame.com/iphone-cocos2d/673.html "【iOS-iap防护】验证用户付费收据!拒绝iap")
[☞ 点击订阅 ☜](http://list.qq.com/cgi-bin/qf_invite?id=acfc24e272cc4a26debf3b3866edb626a9ea3fc80fd8893c) 本博客最新动态!及时将最新博文通知您!
对于iOS的应用安全这块主要有两块是我们开发者可以避免的,一个是存储数据加密,这个在上一篇文章Himi介绍了base64加密算法;另外一个就是付费产品防护!那么本篇Himi来分享如何防护越狱用户的iap Cracker!
对于iap Cracker这个插件,Himi简单介绍下!
iap Cracker可以说是iOS越狱用户的终极利器阿,当今app Store的所有内置收费的游戏,基本使用此插件进行秒购买无压力!(对于那些收费下载的游戏,对于越狱用户来说,安装个XX助手<你懂得~>就可以免费体验app store的所有游戏,不管你下载收费还是内置收费!)
iap Cracker能绕过appstore的付费流程,其方式是当用户点击付费产品进行购买后,iap Cracker模拟返回一个购买成功的消息(无需联网,说白了,连post 数据给App store都没有!),然后我们应用中收到这个“假的”交易成功的消息直接给用户加钱,加装备,加各种….
OK,对于iap Cracker就不再多介绍了,下面Himi来分享如何防护iap Cracker吧;
对于越狱用户使用付费破解插件进行付费这个问题,其实Apple并没有不管,而是已经在文档中清晰的说明,只是很多童鞋并没有发现,如下截图:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd02249693.png "222")](http://www.himigame.com/wp-content/uploads/2012/03/222.png)
apple提示开发者付费要进行验证付费收据! 原文apple dev官方文档连接:
[https://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/StoreKitGuide…](https://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/StoreKitGuide/VerifyingStoreReceipts/VerifyingStoreReceipts.html#//apple_ref/doc/uid/TP40008267-CH104-SW1)
下面Himi就详细讲解如何在我们付费流程中加入iap防护,步骤如下:
1.首先将 json类库和NSData+Base64类导入你的项目中,下载:
“json_base.zip” 下载地址: [http://vdisk.weibo.com/s/hq1Qk](http://vdisk.weibo.com/s/hq1Qk)
2.然后将Himi封装的如下函数拷贝到你付费代码所在的类中:
~~~
.h中:
-(BOOL)putStringToItunes:(NSData*)iapData;
.m中:
#import "NSData+Base64.h"
#import "NSString+SBJSON.h"
#import "JSON.h"
-(BOOL)putStringToItunes:(NSData*)iapData{//用户购成功的transactionReceipt
NSString*encodingStr = [iapData base64EncodedString];
NSString *URL=@"https://sandbox.itunes.apple.com/verifyReceipt";
//https://buy.itunes.apple.com/verifyReceipt
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];// autorelease];
[request setURL:[NSURL URLWithString:URL]];
[request setHTTPMethod:@"POST"];
//设置contentType
[request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
//设置Content-Length
[request setValue:[NSString stringWithFormat:@"%d", [encodingStr length]] forHTTPHeaderField:@"Content-Length"];
NSDictionary* body = [NSDictionary dictionaryWithObjectsAndKeys:encodingStr, @"receipt-data", nil];
SBJsonWriter *writer = [SBJsonWriter new];
[request setHTTPBody:[[writer stringWithObject:body] dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]];
NSHTTPURLResponse *urlResponse=nil;
NSError *errorr=nil;
NSData *receivedData = [NSURLConnection sendSynchronousRequest:request
returningResponse:&urlResponse
error:&errorr];
//解析
NSString *results=[[NSString alloc]initWithBytes:[receivedData bytes] length:[receivedData length] encoding:NSUTF8StringEncoding];
CCLOG(@"-Himi- %@",results);
NSDictionary*dic = [results JSONValue];
if([[dic objectForKey:@"status"] intValue]==0){//注意,status=@"0" 是验证收据成功
return true;
}
return false;
}
~~~
接着说下此方法的使用,一般付费代码中,童鞋们肯定会有如下函数:
~~~
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions//交易结果
{
for (SKPaymentTransaction *transaction in transactions)
{
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased://交易完成
if([self putStringToItunes:transaction.transactionReceipt]){
//这里给用户添加钱阿,装备阿什么的
}
break;
......代码省略
}
}
}
~~~
上面这个函数当获取交易成功的消息都会进入到SKPaymentTransactionStatePurchased这个case中(不管是iap cracker模拟的还是appstore真的反馈的消息), 那么我们一般不做iap防护情况下,会直接在此case中给用户添加金币阿,什么的! 但是如上所说因为iap cracker也会模拟返回交易成功的消息,也会进入到这个case中,因此我们在此与appstore再次进行一次收据验证!
另外说一点当交易完成时appstore传回来的transaction(SKPaymentTransaction)类中的transactionReceipt属性里包含AppStore返回经过签名的收据信息!OK,我们要的就是这个收据并将此收据post给appstore 的server进行收据验证,所以在SKPaymentTransactionStatePurchased这个交易成功的case中再调用Himi封装的函数if([self putStringToItunes:transaction.transactionReceipt]){} 进行再次确认下购买是否付费流程正确!
那么下面详细说下Himi封装的这个putStringToItunes函数:
此函数中,首先我们将传入的收据data类型变量进行base64转换成string类型,然后将此收据以json的形式发送给appstore进行验证!这里注意!一定要以json形式发送,否则appstore server端不识别!
最后再次利用json对appstore server返回的字段(json数据)进行解析,我们只需要解析出 status 这个key的value即可!
当appstore验证收据正确时我们解析出来的 status 这个key的value值为0(零)!
下面是appstore返回json数据的两种形式:
1.收据无效的情况:
~~~
{"status":21002, "exception":"java.lang.NullPointerException"}
~~~
2.收据正确的情况,如下图(点击放大):
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd022751f3.png "11112")](http://www.himigame.com/wp-content/uploads/2012/05/11112.png)[
](http://www.himigame.com/wp-content/uploads/2012/03/11112.png)
最后大家需要注意的一点是,Himi封装的函数中post的地址这里要记得发布的时候修改!
因为当你沙盒测试的时候地址是:https://sandbox.itunes.apple.com/verifyReceipt
但是正式发布后post的地址应该是: https://buy.itunes.apple.com/verifyReceipt
千万不要发布应用的时候别忘记修改这里!
OK,本篇就介绍到这里,希望对还没有做iap防护的童鞋有所帮助!
—————2012年5月2日更新:
对于iap 的防护,现在除了iap cracker之外,那么最主要的就是国人的iap free这个插件了,那么对于iap free的防护,如果我们仅仅只是跟apple的服务器进行验证收据,那么iap free照样能破解我们的游戏/软件!
我这里先大概说下iap free:
iap free的功能与iap cracker功能类似,只是更加强大的iap free能在你与apple服务器进行验证收据的步骤中进行截取,并返回一个模仿apple返回的假收据!这么一说大家就很清楚了,我们上面说的iap 验证收据变得毫无意义,但是不要着急,这里Himi将iap free的假收据形式打印了出来,大家对比看下就知道该如何来做iap free的防护了:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd0228c0c2.jpg "111")](http://www.himigame.com/wp-content/uploads/2012/05/111.jpg)
上图就是iap free制作的假收据啦,那么至于如何继续做防护,我想这里不需要再多解释了,毕竟有政策就有对策;大家发挥吧~
另外说一点,当用户在无网的情况下购买任何产品,肯定只有两种情况,1.越狱机器想破解。2.忘记;
那么我们可以使用下面这段代码判断当前ios设备是否联网了:
~~~
-(BOOL)isNetworkOK{
struct sockaddr_in zeroAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
zeroAddress.sin_len = sizeof(zeroAddress);
zeroAddress.sin_family = AF_INET;
SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress);
SCNetworkReachabilityFlags flags;
BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags);
CFRelease(defaultRouteReachability);
if (!didRetrieveFlags)
{
NSLog(@"Error. Could not recover network reachability flags");
return NO;
}
BOOL isReachable = flags & kSCNetworkFlagsReachable;
BOOL needsConnection = flags & kSCNetworkFlagsConnectionRequired;
return (isReachable && !needsConnection) ? YES : NO;
}
~~~
项目中添加 SystemConfiguration.framework
然后在导入 :
#import <SystemConfiguration/SCNetworkReachability.h>
#import <netinet/in.h>
【iOS-cocos2d-X 游戏开发之七】整合Cocos2dX的Android项目到Xcode项目中,Xcode编写&编译代码,Android导入打包运行即可!
最后更新于:2022-04-01 10:15:25
本站文章均为[ 李华明Himi ](http://www.himigame.com/about-himi)原创,转载务必在明显处注明:
转载自[【黑米GameDev街区】](http://www.himigame.com/) 原文链接: [http://www.himigame.com/android-game/667.html](http://www.himigame.com/android-game/667.html "【iOS-cocos2d-X")
此篇针对较早的-x引擎讲解的,最新的可以参考:[ 【Cocos2d-X(2.x) 游戏开发系列之二】cocos2dx最新2.0.1版本跨平台整合NDK+Xcode,Xcode编写&编译代码,Android导入打包运行即可!](http://www.himigame.com/cocos2dx-v2-0/962.html)
之前有两节介绍了mac下配置Android NDK并搭建Cocos2dX以及如何创建Cocos2d-X-Android项目,那么可能看过这两节的童鞋就很多疑问,问的最多的就是如何让Xcode中的iOS代码与Eclipse-Android代码通用?那么今天Himi就为大家详细解决这个问题,让我们只需在Xcode中编写代码,然后编译后直接导入Android即可运行打包!
废话不多说了,正题,整合步骤如下:(为了不让童鞋们混淆,许多文件的具体修改省略,Himi直接给出文件)
1.使用终端创建一个Android-Cocos2dx项目先;具体步骤参照如下博文:
[【iOS-cocos2d-X 游戏开发之四】Cocos2dX创建Android NDK新项目并编译导入Eclipse中正常运行!](http://www.himigame.com/android-game/645.html)
2.创建好之后,然后Xcode新创建一个Cocos2dX模版的项目(已有Cocos2dX模版项目的省略此步骤)
注意:下面的讲解为了避免童鞋们的路径混淆,那么这里Himi说下自己项目的相关路径和文件名:
Himi创建的Xcode项目路径:(项目名称:OhMonsterMRiOS)
/Users/Himi/Documents/HimiWork/OhMonsterMRiOS
Himi创建的Android项目名称:(项目名称:OhMonsterMRAndroid)路径后面用不到;
3.拷贝新创建的Android项目下的整个“android”文件夹,路径: OhMonsterMRAndroid/android
然后拷贝到 /Users/Himi/Documents/HimiWork/OhMonsterMRiOS/OhMonsterMRiOS
上面这个路径就是你Xcode项目下与 ios、libs、Classes同一级别的地方!
注意!这里只要新创建的Android项目下名为“android”文件夹,其他的都不要!!!!
4.打开你的“终端”,先 cd 到你Xcode的项目下刚拷贝过来的android文件夹路径,这里Himi输入的命令如下:
cd /Users/Himi/Documents/HimiWork/OhMonsterMRiOS/OhMonsterMRiOS/android 回车!
继续在终端输入 pico makefile 回车! 然后根据如下图进行输入:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd021844d2.png "1")](http://www.himigame.com/wp-content/uploads/2012/03/12.png)
这里要注意:
clean 要顶格不留空格! 两个 rm 语句前都是 tab 缩进!不要弄错!
输入后,control+x 保存,然后 y ,最后回车! OK!
不要关闭终端,继续输入 make clean 回车! 当终端输出如下提示,表示你正确配置了:
~~~
rm -rf libs/
rm -rf obj/
~~~
5.下载如下四个 Android.mk 文件和一个Application.mk以及一个build_native.sh文件 ;
[mk.rar](http://www.himigame.com/?dl_id=69) (126 字节, 9 次)
下载解压后你可看到如下几个文件,( mk 表示你下载解压出的mk文件夹)
~~~
android/build_native.sh
android/jni/Android.mk
android/jni/Application.mk
android/jni/helloworld/Android.mk
Classes/Android.mk
libs/cocos2dx/Android.mk
~~~
这六个文件分别放入你Xcode项目如下路径,这里使用Himi的项目路径来说明,大家按照自己项目路径放入即可!
~~~
/Users/Himi/Documents/HimiWork/OhMonsterMRiOS/OhMonsterMRiOS/android/build_native.sh
/Users/Himi/Documents/HimiWork/OhMonsterMRiOS/OhMonsterMRiOS/android/jni/Android.mk
/Users/Himi/Documents/HimiWork/OhMonsterMRiOS/OhMonsterMRiOS/android/jni/Application.mk
/Users/Himi/Documents/HimiWork/OhMonsterMRiOS/OhMonsterMRiOS/android/jni/helloworld/Android.mk
/Users/Himi/Documents/HimiWork/OhMonsterMRiOS/OhMonsterMRiOS/Classes/Android.mk
/Users/Himi/Documents/HimiWork/OhMonsterMRiOS/OhMonsterMRiOS/libs/cocos2dx
~~~
这四个路径当中如提示文件已经存在,果断不用多想,直接覆盖即可!
6.OK,四个文件放置完成之后,童鞋们需要修改刚才下载中的 build_native.sh 文件,就是android/build_native.sh文件!
打开后只需要修改: NDK_ROOT 路径即可!别跟我说你的NDK_ROOT路径你不知道 =。 =不知道的童鞋请自动面壁~开玩笑拉,不知道的童鞋可以参考之前我介绍如何配置Android SDK 的博文哦~
7.最后一步了,拷贝两个文件就大功告成了!
首先到你存放Cocos2dX引擎目录路径下(如有看过之前文章,其实就是$COCOS2DX_ROOT路径),
然后找到CocosDenshion文件夹下的“android”文件夹,然后拷贝整个“android”文件夹到你Xcode项目下libs/CocosDenshion下,Himi的路径如下:
/Users/Himi/Documents/HimiWork/OhMonsterMRiOS/OhMonsterMRiOS/libs/CocosDenshion
最后找到存放Cocos2dX引擎目录路径下的cocos2dx下的“platform”文件夹,将整个“platform”文件夹拷贝到你Xcode项目下libs/cocos2dx 下,Himi的路径如下:
/Users/Himi/Documents/HimiWork/OhMonsterMRiOS/OhMonsterMRiOS/libs/cocos2dx
——(此步骤为加入cocos2dx-box2d步骤,如果不使用box2d请童鞋省略此步骤)——
(此步骤为加入cocos2dx-box2d步骤,如果不使用box2d请童鞋省略此步骤)从Cocos2dX引擎目录路径下/Box2D/拷贝Android.mk到Xcode项目下/libs/Box2D/下面去。
8.打开你的“终端”,先 cd 到你Xcode的项目下刚拷贝过来的android文件夹路径,这里Himi输入的命令如下:
cd /Users/Himi/Documents/HimiWork/OhMonsterMRiOS/OhMonsterMRiOS/android 回车!
继续输入 make clean 回车!
继续输入 ./build_native.sh 回车!
如果之前的步骤都没有出现任何问题,OK,终端会输出一大堆的信息,最后显示如下类似内容!
~~~
Install : libcocosdenshion.so => libs/armeabi/libcocosdenshion.so
Compile++ thumb : game_logic_static <= HelloWorldScene.cpp
StaticLibrary : libgame_logic_static.a
SharedLibrary : libgame.so
Install : libgame.so => libs/armeabi/libgame.so
~~~
OK,到此证明之前的配置正确完成!如果出现了 …stop类似的信息,说明其中有些地方配置出现问题!
别着急,还没结束!最后说二点:
一. 你拷贝到Xcode项目中的android相关文件等没必要导入你的Xcode中,如果导入反而让你的Xcode项目编译错误无法运行!
二. 当你以后在Xcode添加了一些类,那么你需要到 Classes/Android.mk 修改一下,将你新添加的类添加进去,如下图所示:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd0219ca2f.png "4321")](http://www.himigame.com/wp-content/uploads/2012/03/4321.png)
注意:新添加的类在其他类中“include”**使用**的时候一定要将文件的路径全部写完整,例如新添加有个类 StaticClassModel.cpp StaticClassModel.h 放在dev文件夹下,那么:
其他类中使用时: #include “dev/StaticClassModel.h”
在classes文件下 Android.mk中,也一样写完整路径:dev/StaticClassModel.cpp
否则编译android时会找不到,而且提醒如下错误:
~~~
Compile++ thumb : game_logic_static <= AppDelegate.cpp
Compile++ thumb : game_logic_static <= HelloWorldScene.cpp
jni/../../Classes/HelloWorldScene.cpp:3:30: error: StaticClassModel.h: No such file or directory
jni/../../Classes/HelloWorldScene.cpp: In member function 'virtual bool HelloWorld::init()':
jni/../../Classes/HelloWorldScene.cpp:56: error: 'StaticClassModel' has not been declared
make: *** [obj/local/armeabi/objs-debug/game_logic_static/HelloWorldScene.o] Error 1
~~~
——(此步骤为加入cocos2dx-box2d步骤,如果不使用box2d请童鞋省略此步骤)——
Classes/Android.mk中找到:
LOCAL_SHARED_LIBRARIES := cocosdenshion_shared
在下面继续添加一句:
LOCAL_SHARED_LIBRARIES += box2d_shared
对了忘记说如何导入到Eclipse中了!这里将Xcode项目导入Eclipse中很容易:
一: 第一次将Xcode中的Android项目导入到Eclipse中,要先build!
打开你的“终端”,先 cd 到你Xcode的项目下刚拷贝过来的android文件夹路径,这里Himi输入的命令如下:
cd /Users/Himi/Documents/HimiWork/OhMonsterMRiOS/OhMonsterMRiOS/android 回车!
继续输入 make clean 回车!
继续输入 ./build_native.sh 回车!
二:Eclipse直接将你Xcode下的“android”路径直接导入你Eclipse中即可!
备注:不仅仅第一次将Xcode中项目导入Eclipse中需要编译,以后只要在Xcode中有代码修改和添加都记得要build!然后eclipse Clean下运行项目就OK拉!
下面是Himi配置之后在Xcode以及Eclipse下运行的iOS和Android截图:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd021c247e.png "11111")](http://www.himigame.com/wp-content/uploads/2012/03/111111.png)
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd0220fc02.png "222222")](http://www.himigame.com/wp-content/uploads/2012/03/222222.png)
【iOS-cocos2d-X 游戏开发之六】使用Base64算法对Cocos2dX自带CCUserDefault游戏存储数据编码!
最后更新于:2022-04-01 10:15:23
本站文章均为[ 李华明Himi ](http://www.himigame.com/about-himi)原创,转载务必在明显处注明:
转载自[【黑米GameDev街区】](http://www.himigame.com/) 原文链接: [http://www.himigame.com/iphone-cocos2dx/659.html](http://www.himigame.com/iphone-cocos2dx/659.html "【iOS-cocos2d-X")
上一篇介绍了,如何使用Cocos2dX自带的CCUserDefault 存储游戏数据,那么Himi也提到此方式保存的为xml格式并且数据明文显示=。 =,这个对于我们开发者来说太暴漏了有木有! so~本篇Himi分享如何使用经典Base64算法对CCUserDefault进行数据编码!
OK,关于base64的介绍,Himi不再赘述,还不太熟悉的童鞋请点击以下连接阅读:
[http://baike.baidu.com/view/469071.htm](http://baike.baidu.com/view/469071.htm) (百度百科的 base64详细解释)
如果对于Cocos2dX自带的CCUserDefault类不太熟悉的请点击如下连接:
[【iOS-cocos2d-X 游戏开发之五】游戏存储之Cocos2dX自带CCUserDefault类详解;](http://www.himigame.com/iphone-cocos2dx/653.html)
对Base64有了基础认识后我们就开始进入正题,首先,我们针对Cocos2dX使用c++版的base64编码文件:
base64.h以及base64.cpp 文件下载导入项目中:
[DataBase64Himi.zip](http://www.himigame.com/?dl_id=63) (647 字节, 11 次)
然后Himi封装好了两个函数:(这里我们假设存储游戏金币)
首先.h中,定义枚举变量(备注1)
~~~
enum{
DATA_GOLD,//金币
};
~~~
然后.h定义两个函数:(备注2)
~~~
//存储数据
void saveDataByKeyValue(int iKey, string sValue);
//读取数据
int loadDataByKey(int iKey);
~~~
最后.cpp中函数具体实现代码:(备注3)
~~~
//存储
void HelloWorld::saveDataByKeyValue(int iKey, string sValue){
char buffer[32];
sprintf(buffer, "%s%d", "Himi",iKey);
CCLog("----------存储");
CCLog("存储之前数据 key:index: Himi%i, value: %s ",iKey,sValue.c_str());
string sKey = himiSaveData(reinterpret_cast<const unsigned char*>(sValue.c_str()), sValue.length());
CCLog("存储编码后的数据 key:index: Himi%i, value: %s ",iKey,sKey.c_str());
CCUserDefault::sharedUserDefault()->setStringForKey(buffer, sKey);
CCUserDefault::sharedUserDefault()->flush();
}
//读取
int HelloWorld::loadDataByKey(int iKey){
CCLog("----------读取");
char buffer[32];
sprintf(buffer, "%s%d", "Himi",iKey);
string s = CCUserDefault::sharedUserDefault()->getStringForKey(buffer);
CCLog("解码前的数据: %s ",s.c_str());
string parseKey = himiParseData(s);
CCLog("解码后的数据: %s ",parseKey.c_str());
return atoi(parseKey.c_str());
}
~~~
OK,这里开始逐步解释一下:
首先说下备注2,这里定义的两个方法,第一个函数:
~~~
//存储数据
void saveDataByKeyValue(int iKey, string sValue);
~~~
其中第一个函数传入一个int值,为什么要这样,原因是这样方便与结合枚举变量(备注1)来进行存储,这样使用的时候非常简化你的代码和步骤;第二个函数是值,这里我封装成string,这样为了更好的结合我们的base64编码!这点在(备注3)中体现!
第二个函数:
~~~
//读取数据
int loadDataByKey(int iKey);
~~~
这个函数没什么好说的,传入的参数int,枚举变量即可!至于返回值是int 是方便程序中使用!
OK,那么备注3对于两个封装函数中的实现需要说明的有3点:
1.将传入的int值(key)转化成char*类型,因为cocos2dx中并没有itoa这样的方法(原因上一篇有介绍),所以这里我采用 char buffer[32]; sprintf(buffer, “%s%d”, “Himi”,iKey); 方式进行将int转化char*类型;
2.存储的时候我们步骤是:string值进行base64编码,然后存储到xml中;
3.将int转化string的时候我在枚举变量前加入”Himi”,大家不喜欢可以换成自己喜欢的,但是千万不要删除!因为一旦你删除掉,然后将存储后的形式为: <1>数据</1> 这样会造成你读取数据出错!
读取数据步骤:根据key读取xml的编码过的string,然后base64解码出原始值,最终atoi转化成int扔出!
这其中应该说没什么难于理解的,所以这里不再赘述了;
那么下面我们来看这两个函数如何使用:
~~~
//base64编码存储
this->saveDataByKeyValue(DATA_GOLD, "10000");
//base64读取数据
this->loadDataByKey(DATA_GOLD);
~~~
代码是不是用起来很方面呢!哈哈,这就对啦,不能因为编码弄得乱78糟~OK,运行项目,控制台你将看到如下显示:
~~~
Cocos2d: cocos2d: cocos2d-1.0.1-x-0.12.0
Cocos2d: cocos2d: GL_VENDOR: Imagination Technologies
Cocos2d: cocos2d: GL_RENDERER: PowerVR SGX 543
Cocos2d: cocos2d: GL_VERSION: OpenGL ES-CM 1.1 IMGSGX543-63.14.2
Cocos2d: cocos2d: GL_MAX_TEXTURE_SIZE: 4096
Cocos2d: cocos2d: GL_MAX_MODELVIEW_STACK_DEPTH: 16
Cocos2d: cocos2d: GL supports PVRTC: YES
Cocos2d: cocos2d: GL supports BGRA8888 textures: NO
Cocos2d: cocos2d: GL supports NPOT textures: YES
Cocos2d: cocos2d: GL supports discard_framebuffer: YES
Cocos2d: cocos2d: compiled with NPOT support: NO
Cocos2d: cocos2d: compiled with VBO support in TextureAtlas : NO
Cocos2d: 此项目已存在保存数据的xml文件
Cocos2d: 存储之前数据 key:index: 0, value: 10000
Cocos2d: 存储编码后的数据 key:index: 0, value: MTAwMDA=
Cocos2d: 解码前的数据: MTAwMDA=
Cocos2d: 解码后的数据: 10000
~~~
存储的文件内容截图如下:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd021655fd.png "123")](http://www.himigame.com/wp-content/uploads/2012/03/1232.png)
OK,最后呢,我要说一点,虽然我们使用base64可以对数据进行编码,但是对于不懂程序的用户来说基本无法修改,但是对于稍微有了解base64编码的童鞋,那么直接可以在线解码你的这个编码,所以呢!最后Himi提醒大家,使用base64当中,大家可以书写一些算法结合base64来使用!这样即使明文也不怕,至于怎么做,Himi就不再多说了 哈哈!
本篇源码下载:
[base64SaveDataForCocos2dx.zip](http://www.himigame.com/?dl_id=64)(691 字节, 6 次)
【iOS-cocos2d-X 游戏开发之五】游戏存储之Cocos2dX自带CCUserDefault类详解;
最后更新于:2022-04-01 10:15:20
本站文章均为[ 李华明Himi ](http://www.himigame.com/about-himi)原创,转载务必在明显处注明:
转载自[【黑米GameDev街区】](http://www.himigame.com/) 原文链接: [http://www.himigame.com/iphone-cocos2dx/653.html](http://www.himigame.com/iphone-cocos2dx/653.html "【iOS-cocos2d-X")
本篇跟大家分享下Cocos2dX中的存储,其中也介绍些细节容易犯错的问题;
在Cocos2dX中提供了自带存储类:CCUserDefault ,当然了这里Himi强调一点,如果你的数据量比较大,建议使用SQL存储比较适合,另外一点要注意的是,尽可能不要在Cocos2dX中使用与平台相关的api进行开发,例如Xcode使用Cocos2dX进行开发游戏时不小心使用了iOS的控件/组件在项目中,那么当移植到Android等平台的时候就肯定异常费劲,估计连正常运行都不可能,因为其他平台不可能正好有iOS的这些控件,即使有也肯定底层实现不一样!换句话而言,神马功能都使用Cocos2dX api实现,尽量都向X靠拢吧,所以这里的存储我也使用X自带的CCUserDefault;至少使用Cocos2dX自带的对于跨平台这一块肯定支持的比较好啦;
言归正传,先大致介绍一下这个类的API:
~~~
Public Member Functions
~CCUserDefault ()
bool getBoolForKey (const char *pKey, bool defaultValue=false)
Get bool value by key, if the key doesn't exist, a default value will return.
int getIntegerForKey (const char *pKey, int defaultValue=0)
Get integer value by key, if the key doesn't exist, a default value will return.
float getFloatForKey (const char *pKey, float defaultValue=0.0f)
Get float value by key, if the key doesn't exist, a default value will return.
double getDoubleForKey (const char *pKey, double defaultValue=0.0)
Get double value by key, if the key doesn't exist, a default value will return.
std::string getStringForKey (const char *pKey, const std::string &defaultValue="")
Get string value by key, if the key doesn't exist, a default value will return.
void setBoolForKey (const char *pKey, bool value)
Set bool value by key.
void setIntegerForKey (const char *pKey, int value)
Set integer value by key.
void setFloatForKey (const char *pKey, float value)
Set float value by key.
void setDoubleForKey (const char *pKey, double value)
Set double value by key.
void setStringForKey (const char *pKey, const std::string &value)
Set string value by key.
void flush ()
Save content to xml file.
Static Public Member Functions
static CCUserDefault * sharedUserDefault ()
static void purgeSharedUserDefault ()
static const std::string & getXMLFilePath ()
~~~
从以上可以一目了然CCUserDefault的使用和功能,哈希表结构,Key -Value,key索引Value值;
提供的存储都是些基础类型,bool,int,string,double,float,方法很容易懂:存储使用set ,获取使用get !
那么最后static方法中可以看到CCUserDefault类留出了一个sharedUserDefault作为接口供开发者使用,那么大概介绍后,下面我们来写几段代码验证下:
~~~
//我们这里简单存储条数据
CCUserDefault::sharedUserDefault()->setStringForKey("key", "himi");
CCUserDefault::sharedUserDefault()->flush();//这里一定要提交写入哦,否则不会记录到xml中,下次启动游戏你就获取不到value了。
//这里随便定义一个string为了验证我们的存储
string str= "wahaha";
//取出我们刚存储的himi,然后赋值给str验证下;
str= CCUserDefault::sharedUserDefault()->getStringForKey("key");
CCLog("打印str=:%s",str.c_str());
~~~
这里要注意, CCUserDefault中有个 flush()的函数,这个用来将数据写入xml文件中,也就是说当你使用setXX的一些函数后记得提交(调用一下flush函数)
OK,下面是控制台输入的结果:
~~~
Cocos2d: cocos2d: cocos2d-1.0.1-x-0.12.0
Cocos2d: cocos2d: GL_VENDOR: Imagination Technologies
Cocos2d: cocos2d: GL_RENDERER: PowerVR SGX 543
Cocos2d: cocos2d: GL_VERSION: OpenGL ES-CM 1.1 IMGSGX543-63.14.2
Cocos2d: cocos2d: GL_MAX_TEXTURE_SIZE: 4096
Cocos2d: cocos2d: GL_MAX_MODELVIEW_STACK_DEPTH: 16
Cocos2d: cocos2d: GL supports PVRTC: YES
Cocos2d: cocos2d: GL supports BGRA8888 textures: NO
Cocos2d: cocos2d: GL supports NPOT textures: YES
Cocos2d: cocos2d: GL supports discard_framebuffer: YES
Cocos2d: cocos2d: compiled with NPOT support: NO
Cocos2d: cocos2d: compiled with VBO support in TextureAtlas : NO
Cocos2d: 打印str=:himi
~~~
最后一句验证了我们的存储没问题,那么我们现在验证是否真的存在xml中了,首先停止当前运行的项目,然后删除刚才代码替换如下代码:
~~~
CCLog("打印str=:%s",CCUserDefault::sharedUserDefault()->getStringForKey("key").c_str());
~~~
然后重新运行此项目,观察控制台打印如下:
~~~
Cocos2d: cocos2d: cocos2d-1.0.1-x-0.12.0
Cocos2d: cocos2d: GL_VENDOR: Imagination Technologies
Cocos2d: cocos2d: GL_RENDERER: PowerVR SGX 543
Cocos2d: cocos2d: GL_VERSION: OpenGL ES-CM 1.1 IMGSGX543-63.14.2
Cocos2d: cocos2d: GL_MAX_TEXTURE_SIZE: 4096
Cocos2d: cocos2d: GL_MAX_MODELVIEW_STACK_DEPTH: 16
Cocos2d: cocos2d: GL supports PVRTC: YES
Cocos2d: cocos2d: GL supports BGRA8888 textures: NO
Cocos2d: cocos2d: GL supports NPOT textures: YES
Cocos2d: cocos2d: GL supports discard_framebuffer: YES
Cocos2d: cocos2d: compiled with NPOT support: NO
Cocos2d: cocos2d: compiled with VBO support in TextureAtlas : NO
Cocos2d: 打印str=:himi
~~~
通过刚才的key->”key”,正常获取到“himi”这个字符串了,OK,监测没问题;
那么一般情况下我们会需要一个方法就是判定当前项目是否已经有存储数据的xml文件存在了,那么Himi这里说下,Cocos2dX默认源码中有这个方法,但是并没有提供给开发者使用,因为此函数被private私有了,此函数源码如下图所示:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd020f09a3.png "111")](http://www.himigame.com/wp-content/uploads/2012/03/1111.png) 那么既然如此Himi这里就自定义了一个检测是否已存在数据xml的函数提供大家使用:(提醒:很多童鞋该说啦,为什么不直接修改源码将其public呢?!其实Himi也这么想,但是如果你后期使用了新的Cocos2dX的版本,或者同事机器的Cocos2dX并没有这么修改源码都会产生错误,反过来说,既然能很容易的写出一个判断的方法何必去动它呢,不是么?哈哈!)
~~~
.h文件:
bool isHaveSaveFile();
.cpp文件:
//当前项目是否存在存储的xml文件
bool HelloWorld::isHaveSaveFile(){
if(!CCUserDefault::sharedUserDefault()->getBoolForKey("isHaveSaveFileXml"))
{
CCUserDefault::sharedUserDefault()->setBoolForKey("isHaveSaveFileXml", true);
CCUserDefault::sharedUserDefault()->flush();//提交
// CCLog("存储文件不存在,头次开始加载游戏");
return false;
}else{
// CCLog("存储文件已存在");
return true;
}
}
~~~
备注:当存储数据的xml不存在的时候,你的第一次存储数据的时候默认会创建,路径在你的app下的documents,如下图所示:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd021136d5.png "2222")](http://www.himigame.com/wp-content/uploads/2012/03/22221.png)
那么这里Himi强调一点!大家要注意setXX的函数的参数,例如以下这个函数:
setStringForKey (const char *pKey, const std::string &value)
第一个参数是const char*类型,不是string!!!!(Himi因为这个原因浪费不少时间,悲剧阿。)
Himi当时存储写了如下代码,造成错误,如下:
~~~
CCUserDefault::sharedUserDefault()->setStringForKey(""+823, sKey);
~~~
错误截图如下:(存储的key变成了路径。。。。《数据是Himi加密后的》)
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd0212cae1.png "error")](http://www.himigame.com/wp-content/uploads/2012/03/error.png)
哎,郁闷,这里Himi犯错希望童鞋们不要再范此错误,之前Himi一直想找 itoa 找个函数,但是怎么都找不到!(c++ 应该存在的整形转字符串),但是Cocos2dX中没有,并且最后Himi使用了与Cocos2dX引擎中的实现itoa的源码,发现如下:
[![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcd0214653e.png "33333")](http://www.himigame.com/wp-content/uploads/2012/03/33333.png)
Cocos2dX自带的这个CCUserDefault并不是加密的,而是明文并且是.xml格式的,所以后续Himi准备写一篇使用base64来进行加密的文章供大家参考;
本篇源码下载:
[SaveDataForCocos2dx.zip](http://www.himigame.com/?dl_id=62) (667 字节, 1 次)