cocos2d-x 从win32到android移植的全套解决方案
引言:我们使用cocos2d-x引擎制作了一款飞行射击游戏,其中创新性地融入了手势识别功能。但是我们在移植过程中遇到了很多的问题,同时也发现网上的资料少而不全。所以在项目行将结束的时候,我们特地写了这篇文章来完整记录我们整个移植的过程,纪念我们项目的成功完成,更以此来表达对帮助过我们的人的感谢。移植过程中我们在网上得到了很多帮助,更要感谢黄杨学长在最后时刻帮助我们突破难关!
0、开发平台
系统:win8 profession 64bit
IDE:vs2012 rtm, eclipse
cocos2d-x版本:2.1.2
1、移植准备
在windows下移植我们需要如下环境支持:Android SDK、 NDK、 Eclipse、 Cygwin.
1.1下载Cygwin安装程序
在Cygwin官网上找(http://cygwin.com/install.html)到在线安装程序,下载对应版本。这里我选择 windows 64bit版本。
1.2 Cygwin安装过程
打开下载好的setup-x86_64.exe,可一路单击下一步(篇幅所限,太过细节的问题就不一一阐述了),直到如下页面:
选择下载的站点,正确选择站点之后下载会很快。我们(在北航)选择了ftp://mirrors.neusoft.edu.cn,速度还是挺快的。这里要提醒大家注意,并不是所有站点的下载都是完整的!Rylynn(博客名)选择的好几个站点都下载不完整,大家如果遇到这个问题,可尝试不同的站点。
选择完站点之后将出现如上界面,这是要选择安装包。为了使Cywin能够编译程序,我们需要选择一些必要的组件。在不改动默认选择的前提下,我们需要保证在Devel分支里如下包已经被选中:binutils, gcc-c++,gcc-core,gdb,make,mingw-gcc-core,mingw-gcc-c++。安装成功后,运行Cygwin,可以在Cygwin窗口中分别输入gcc+ -v, g++ -version, make -v, gdb -v等命令并回车,如果看到窗口输出了各组件的版本信息,那么说明安装成功。如果输入某条命令,窗口输出 command not found,那么就需要重新打开安装程序并安装对应的组件,不需要全部重新安装。
1.3 安装Eclipse、Android SDK、 Android NDK
这里提供网站链接,大家可自行下载和安装。NDK下载后解压即可。Eclipse的安装和环境配置,请大家参考相应的文章,在此不再赘述。
Android SDK:http://developer.android.com/sdk/index.html
Android NDK:http://developer.android.com/tools/sdk/ndk/index.html
Eclipse:http://www.eclipse.org/downloads/
1.4 编辑create-android-project脚本
在cocos2d-x引擎的根目录下,可以找到create-android-project脚本。在windows下,对应的是"create-android-project.bat",这个脚本用于生成游戏的android工程。
使用任何支持unix风格换行的编辑器(我们选择了Ultra Editor)打开脚本文件,找到如下语句,并进行修改。
set _CYGBIN = e:\cygwin\bin
set _ANDROIDTOOLS =e:\android\andoird-sdk\tools
set _NDKROOT = e:\android\android-ndk-r8d
其中将_CYGBIN设置为Cygwin安装目录下的"bin"文件夹路径,_ANDROIDTOOLS设置为Andoird SDK安装路径下"tools"文件夹路径,_NDKROOT设置为NDK的解压路径。
完成设置后,在Cygwin中运行"create-android-project"脚本(可直接将文件拖入Cygwin窗口后回车)。若提示如下,则修改成功:
Input package path. For example: org.cocos2dx.example:
2、生成项目
接下来我们执行create-android-project脚本。用Cygwin运行"create-android-project.bat",看到如下提示:
Input package path. For example: org.cocos2dx.example:
此时输入我们想要创建的程序包名(程序包的命名应遵守Andoird命名规范)。我们输入:"org.cocofish.skyline"(我们这个项目的队名叫cocofish,项目叫做skyline。大家可随意选择,只要记住就好)。回车后,窗口输出:
Input project name:
我们输入了"Skyline"
回车后,窗口会列出计算机中安装的所有Android SDK版本,大家可以根据需要自行选择。我们输入了"1",即选择android 4.1.2的版本。在Lmeng(博客名)的计算机里,整个过程窗口输出的内容贴在下方:
Please enter your package path. For example: org.cocos2dx.example:org.cocofish.skyline Please enter your project name:Skyline "Now cocos2d-x suppurts Android 2.1-update1, 2.2, 2.3 & 3.0" "Other versions have not tested." Available Android targets: ---------- id: 1 or "android-16" Name: Android 4.1.2 Type: Platform API level: 16 Revision: 4 Skins: HVGA, QVGA, WQVGA400, WQVGA432, WSVGA, WVGA800 (default), WVGA854, W XGA720, WXGA800, WXGA800-7in ABIs : armeabi-v7a, mips, x86 ---------- id: 2 or "Google Inc.:Google APIs:16" Name: Google APIs Type: Add-On Vendor: Google Inc. Revision: 3 Description: Android + Google APIs Based on Android 4.1.2 (API level 16) Libraries: * com.google.android.media.effects (effects.jar) Collection of video effects * com.android.future.usb.accessory (usb.jar) API for USB Accessories * com.google.android.maps (maps.jar) API for Google Maps Skins: WVGA854, WQVGA400, WSVGA, WXGA800-7in, WXGA720, HVGA, WQVGA432, WVGA 800 (default), QVGA, WXGA800 ABIs : armeabi-v7a ---------- id: 3 or "android-17" Name: Android 4.2.2 Type: Platform API level: 17 Revision: 2 Skins: HVGA, QVGA, WQVGA400, WQVGA432, WSVGA, WVGA800 (default), WVGA854, W XGA720, WXGA800, WXGA800-7in ABIs : armeabi-v7a, mips, x86 ---------- id: 4 or "Google Inc.:Google APIs:17" Name: Google APIs Type: Add-On Vendor: Google Inc. Revision: 3 Description: Android + Google APIs Based on Android 4.2.2 (API level 17) Libraries: * com.google.android.media.effects (effects.jar) Collection of video effects * com.android.future.usb.accessory (usb.jar) API for USB Accessories * com.google.android.maps (maps.jar) API for Google Maps Skins: WVGA854, WQVGA400, WSVGA, WXGA800-7in, WXGA720, HVGA, WQVGA432, WVGA 800 (default), QVGA, WXGA800 ABIs : armeabi-v7a Please input target id:1
完成之后,脚本会在根目录下创建"Skyline"的目录,并在此目录里生成Android项目文件。新创建的项目文件中有Helloworld的项目,大家接着可直接跳到从"执行build_native.sh"继续后续操作,通过完成这样一个默认的项目的移植来检测整个配置和操作过程的正确性。这里将继续自己项目的移植。
打开新创建的项目目录(我们这里是"Skyline"),可以看到"Classes"和"Resources"目录,脚本会在这两个目录下创建默认的Helloworld代码和资源文件,我们把它们全部删除。接着,把我们自己的项目源码和资源复制到这两个目录里。通常,我们将源代码(.cpp和.h)复制到"Classes"目录下,把资源文件(图片,音乐等)复制到"Resources"目录下。操作完成后,进入下一步。
重要!在新建的项目后,proj.android\jni\hellocpp下,有个main.cpp,是整个项目的入口。直接点击,会弹出"拒绝访问"。如果想要查看里面的内容的话,要先修改它的属性-安全-高级,在权限条目里将权限设置为"完全控制"(双击后可修改)。main.cpp的内容,一般情况下不需要做修改。
3、编译
完成上述操作后,我们要对项目进行编译,并生成.so文件(unix的动态链接库,与windows的.dll类似)。
3.1 修改"Android.mk"
"Android.mk"文件位于项目的"proj.android\jni"目录下,它记录了项目所包含的源码文件信息。利用编辑器(如Ultra Editor)打开此文件,参照下面的文件进行修改。/*...*/里的内容是笔者加上的注释
LOCAL_PATH := $(call my-dir) /*不改动*/ include $(CLEAR_VARS) /*不改动*/ LOCAL_MODULE := sky_shared /*默认为game_sharred,如果改动,请一定记住*/ LOCAL_MODULE_FILENAME := libsky /*默认为libgame,如果改动,请一定记住。*/ LOCAL_SRC_FILES := hellocpp/main.cpp \ ../../Classes/AppDelegate.cpp \ ../../Classes/其他源文件.cpp /*添加上自己项目里所有的源文件,结尾的'\'是连接符,如果下一行还有内容,请不要漏掉.而且'\'后应直接回车*/ LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes \ $(LOCAL_PATH)/../../../cocos2dx/platform/third_party/win32 \ $(LOCAL_PATH)/../../../extensions /*添加上自己项目里头文件所在的目录。后两行是笔者因为项目而单独添加的,前一个是为线程,后一个是为 控件类引入的头文件所在的目录*/ LOCAL_WHOLE_STATIC_LIBRARIES := cocos2dx_static cocosdenshion_static cocos_extension_static /*以下均不作改动*/ include $(BUILD_SHARED_LIBRARY) $(call import-module,CocosDenshion/android) \ $(call import-module,cocos2dx) \ $(call import-module,extensions)
重要!出错点!笔者在这一步遇到很多问题,所以也请读者加以重视。
(1)对LOCAL_MODULE与LOCAL_MODULE_FILENAME修改后请务必记住自己所修改的名字。比如笔者将它们改为skyline_shared和libskyline。因为这里的skyline将会在导入eclipse那一步之后用到。我们需要将在通过执行“create-android-project”创建的项目文件夹里的"<你的项目名>.java”(比如"skyline.java")这个文件里System.loadLibrary("game")的“game”改成自己所修改的名字,在笔者这里就是"skyline",否则就是黑屏。估计是无法加载生成的libskyline.so(后面会提到)。笔者因为这个问题纠结了很久!最后才发现原来就一个字符串的事情!!如果不改动前面的LOCAL_MODULE和LOCAL_MODULE_FILENAME,因为默认的是"game",那就不会有问题。
(2)如果你在项目里引入了线程和控件类,可参考笔者这个代码框里的LOCAL_C_INCLUDES。对于线程,请注意:将你引入线程的那个文件里"#include 'pthread/pthread.h'"改为"#include'pthread.h'",以使编译能够通过。(笔者不知道原因,也许没有这么麻烦,但是在笔者计算机里成功了。也希望明白的人告知~)
3.2 执行"build_native.sh"
接下来就启动编译了。进入android项目路径下,使用Cygwin执行"build_native.sh"脚本(tip:可直接将文件拖入Cygwin窗口),如果顺利,我们可以在Cygwin窗口输出的最后发现“Install : libsky.so => libs/armeabi/libsky.so”,并且在libs/armeabi目录下找到编译获得的"libskyline.so"(注意到了吗,这是上一步设置的LOCAL_MODULE_FILENAME)。
然而,事情往往不那么顺利!
重要!出错点!很大可能你会看到Cygwin输出一大堆乱七八糟的东西,而且在中途停下。这时候就是编译出错了。但是千万不要着急,一点点调。虽然出错了,但是报错点还是很准确的。所以请大家从最上面往下一行一行找。发现有"error"的这行,这就是报错点。仔细看这行的内容,它很明确地告诉你是哪个文件哪一行出了错。这时候去找到那个出错的位置,根据报错的信息,查找一些资料,加上自己的判断和尝试,把错误处理掉。然后再编译一遍。如果有错误,继续处理。不要看一堆字母感觉烦躁(看着就像乱码的感觉),这时候你最需要的就是慢慢来。而且这个错误可能来自于引擎代码本身!!
比如笔者遇到这样几个错误:
(1)使用了未声明的"itoa"。于是我去查Classes目录下的props_number_shower.cpp第27行,确实调用了itoa这个方法。虽然这个在vs2012里运行没问题,但是这里并不支持。于是我从网上找了一个替代itoa功能的函数源代码,加入到这个源文件中,编译就通过了。网友涵曦(博客名)提示,这是包含文件的问题。在此表示感谢!
(2)格式问题。同样的思路,找到CocosDenshion/android/SimpleAudioEngine.cpp第77行。发现了LOGD(deviceModel)(如图)。上网查了下发现好像linux对格式要求比较严格,于是笔者尝试把这行改为LOGD("%s", deviceModel);发现编译通过。在编译的过程中发现非常多类似的报错,大家如果看到报错信息如下图,找到指定位置修改即可。
(3)文件流。笔者对c++文件流是否能在Cygwin中编译通过没有做足够的测试。但是笔者将fstream换成c的FILE操作的时候就不报错了,如果大家在这里遇到问题,可以尝试着改改。但是对于移植到android上,还应采CCFileUtils。范例如下:
string fullPath=CCFileUtils::sharedFileUtils()->getWritablePath()+"rank.txt"; /*"rank.txt"是自己使用的资源文件,其他的不用改动。此行用于获得文件位置*/ unsigned long file_size; /*声明变量,在下一行中使用,用于接收文件内容大小*/ char *filedata=(char*)CCFileUtils::sharedFileUtils()->getFileData(fullPath.c_str(),"r",&file_size); /*取得文件指针*/ if(filedata==NULL)... /*对于文件指针的用法,与C++一样*/
如果遇到其他的错误,读者可按照以上解决问题的思路自行解决问题。
4、导入Eclipse
4.1新建项目
打开Eclipse,一次打开"File"-"New"-"Project"-"Android"-"Android Project from Existing Code"项目,在随后出现的界面中点击browser,后找到游戏项目的"proj.android"并确认,此时即从原有代码中新建了安卓项目。
4.2复制必要文件
4.2.1将cocos2d-2.1rc0-x-2.1.2(根目录)\cocos2dx\platform\android\java\src下的文件复制到Sky(游戏项目根目录)\proj.android\src里,并在eclipse中手动导入
4.2.2如果Sky(游戏项目根目录)\proj.android\assets里没有资源文件,将Sky(游戏项目根目录)\Resources下你的所有资源文件复制到assets中,并在eclipse中手动导入
4.3 做必要的修改
4.3.1 在Sky(游戏根目录)\src\<项目名>里的Sky.java文件里,下图所标示的那行中,请记得将loadLibrary("xxx");里的字符串改为先前修改过的LOCAL_MODULE_FILENAME的值(去掉lib),如笔者改为sky。注意!这在上面已经强调过,如果这里的字符串和LOCAL_MODULE_FILENAME(去掉lib)的不匹配,结果很惨烈!看不见界面!笔者在这里费了很多时间!
4.3.2 其他简单的错误,比如下图所示:找不到drawable/icon,可在eclipse工程中res里加入一个icon.png文件即可。当然也可将icon改名成其他以存在于工程的图片文件(一般为png文件)
5、调试
虚拟机十分卡,而且好像并不支持opengl,所以建议使用真机进行调试(使用方法这里就不详细展开,大家可在网上查阅相关资料)。调试过程中难免发生问题,这里将列举出笔者在调试过程中遇到的问题,探讨解决的思路。
5.1 文件权限问题。在调试过程中我们在logcat message窗口看到这样一个错误,意思是某个文件权限不够。如果这时候大家复制这么长串的看起来像目录的东西去搜索,是找不到的。因为他们毕竟不是目录名。这个时候,大家可以选去关键词去搜索。比如这张图片里,我们认为"shader"是关键词,果然在项目安卓工程里找到了"shaders"文件夹,将里面所有文件的权限修改成“完全控制”,发现调试不再报错,可以认为修改成功!
5.2文件读写问题
某种意义上笔者并不愿意把它称作文件读写问题。后面将做解释。
如果读者还记得的话,上面已经提到过读写问题。如果单纯采用C的文件读写写法,工程就会找不到资源文件。因此将它改成上述的CCFileUtils,将解决此类问题。
string fullPath=CCFileUtils::sharedFileUtils()->getWritablePath()+"rank.txt"; /*"rank.txt"是自己使用的资源文件,其他的不用改动。此行用于获得文件位置*/ unsigned long file_size; /*声明变量,在下一行中使用,用于接收文件内容大小*/ char *filedata=(char*)CCFileUtils::sharedFileUtils()->getFileData(fullPath.c_str(),"r",&file_size); /*取得文件指针*/ if(filedata==NULL)...
5.3 如何调试
之所以说读者不愿意把“5.2文件读写问题”称作“文件读写问题”,是因为读者在解决这类问题的时候使用的方法其实是告诉了我们如何进行调试。笔者在完成5.1那一步之后发现真机上还是没有显示出任何内容。黑屏!这个时候我们着急了,曾经已经将helloword显示在手机上了,为什么这个项目就不可以?!学长提示我们是不是主菜单加载不成功,并知道我们对每一个关键部分加上一个输出,通过在控制台的输出查看哪一步出了问题!比如我们在主菜单"mainmenu.cpp"的"init()"里每一个加载操作后加上了CCLOG("%s","init() xxx;--From MainMenu")("xxx"这里改成特定的值,用于确定出错位置)。也是通过这个方法,我们才确定了所有的“文件读写问题”。因此,如果大家遇到问题,请大家不要气馁,一点点地去判断出错位置,然后修改,尝试。
6、分辨率适配
最后一个问题就是分辨率适配了。在网上翻看了相关资料后,总算解决了问题。其实就是一行代码的事情。
在 AppDelegate.cpp的 bool AppDelegate::applicationDidFinishLaunching() 方法里,加上
CCEGLView::sharedOpenGLView()->setDesignResolutionSize(576,324,kResolutionNoBorder);即可解决分辨率适配问题。
其中,576,324 是笔者在win32设计游戏时采用的宽度和高度,大家改为自己所设置的宽高即可。kResolutionNoBorder是指在屏幕拉伸时采用的其中一种策略,保证拉伸后无边界,但是设计时界面的边缘部分可能在最后被遮盖。笔者通过vs2012的 "go to definition"操作找到了如下的定义。以下即是全部的适配策略了。
enum ResolutionPolicy { // The entire application is visible in the specified area without trying to preserve the original aspect ratio. // Distortion can occur, and the application may appear stretched or compressed. kResolutionExactFit, // The entire application fills the specified area, without distortion but possibly with some cropping, // while maintaining the original aspect ratio of the application. kResolutionNoBorder, // The entire application is visible in the specified area without distortion while maintaining the original // aspect ratio of the application. Borders can appear on two sides of the application. kResolutionShowAll, kResolutionUnKnown, };
参考资料:参考的资料较为零碎,多为网上的文章。因为比较繁乱,没有记录下来。另外参考了《Cocos2d-x高级开发教程》的移植部分。
总结:
笔者和队友在整个移植过程中备受煎熬,几近绝望。但是始终相信既然别人能够移植成功,我们也一定可以!最终我们用了两天多的时间终于让我们的游戏成功运行在手机上!那个兴奋感、那种绝处逢生的快感到现在还十分难忘!所以请各位cocos2d-x的战友们不要气馁,问题都是可以解决的,一点一点来,不要着急,给自己信心。先把helloworld显示出来,然后再把自己的项目慢慢调出来。坚持下去,总会成功的!
Here is our game <SkyLine Beta1.0>, Please download it to support us!!!
希望这篇文章能够帮到困扰在cocosd-x移植android过程中的朋友们。如果文章内容与错误或者某些部分有更好的解法,也请留言。我们很希望和大家交流经验!谢谢!