cocos2d-x-2.2.4 (四) 将MyGame在Android上跑起来

继续上一篇,我接着在MyGame工程干活,这次要将MyGame运行在Android设备上。


要将cocos2dx的项目在Android上跑起来需要NDK和eclipse

NDK用来编译cocos2dx的cpp文件,将其打包成动态库文件,例如libgame.so;

由于google被封了,NDK的下载页面是打不开的,不过我们可以直接通过链接下载NDK:

dl.google.com/android/ndk/android-ndk-r8e-darwin-x86.tar.bz2

我用的是mac版本的NDK r8e版本,需要其他版本可以手动修改上面的链接,也有人整理出来下载链接了:

http://www.cnblogs.com/yaotong/archive/2011/01/25/1943615.html


eclipse用来读取cocos2dx建立的工程,打包出apk或者直接运行。但是使用eclipse配置出Android环境是非常折腾的事情,尤其是在大陆连接google特别费力的情况(ˇˍˇ)

现在很多人使用的都是adt-bundle,这个其实就是继承了ADT的eclipse,链接如下:

http://www.cnblogs.com/bjzhanghao/archive/2012/11/14/android-platform-sdk-download-mirror.html


下载好了NDK和adb-bundle以后就可以开始干活了~

首先我们还是假设MyGame的路径是/Users/zf/MyGame/,NDK的路径是/Users/zf/android-ndk-r8e。

可以注意到,在MyGame目录下面,有个proj.android文件夹,这个就是cocos2dx给我们生成的android工程,我们接下来的任务就是在这个目录进行了~


要将MyGame在Android平台运行起来,主要有两步:

1.使用NDK编译生成动态库;

2.使用eclipse建立Android工程,将MyGame运行起来;


其实对于很多总是使用IDE的程序员来说,使用NDK编译是一件非常难的事情,这里需要修改Android.mk文件和build_native.sh脚本。我对这两个其实不是很熟悉,尤其是shell脚本,这个我是很陌生的。

使用NDK编译这一过程中,我们主要要涉及到的文件我先列出来:

1. /Users/zf/MyGame/proj.android/build_native.sh:这个shell脚本主要是设置工程路径,cocos2d库的路径,复制游戏使用的资源文件,然后使用NDK编译出动态库;


2. /Users/zf/MyGame/proj.android/jni/Android.mk:

关于Android.mk文件,在NDK的帮助文档是这么描述的:

An Android.mk file is a small build script that you write to describe yoursources to the NDK build system. Its syntax is described in details inthe file docs/ANDROID-MK.html.

简单来说,Android.mk文件就是一个小脚本,用来告诉NDK你的源文件该怎么去编译,关于他的语法可以去看ANDROID-MK.html。


3. /Users/zf/MyGame/proj.android/jni/Application.mk:

Application.mk是这么说的:

TheApplication.mk file describes your application itself. See thedocs/APPLICATION-MK.html document to understand what this file allows youto do.

Application.mk是描述你的程序本身的。


上面两个文件都是NDK编译是需要的文件,关于他们在NDK目录下都有详细的帮助文档,不过是英文的,目录是/Users/zf/android-ndk-r8e/docs/。


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

下面先看Application.mk:

APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -DCC_ENABLE_CHIPMUNK_INTEGRATION=1 -DCOCOS2D_DEBUG=1


我们一个个开始看,看明白这个就行,至于Application.mk文件里面没有的语法,大家就自行参阅帮助文档,不过基本上来说,Application.mk是不用做什么改动的~

APP_STL:
By default, the NDK build system provides C++ headers for the minimal C++ runtime library (/system/lib/libstdc++.so) provided by the Android system.
However, the NDK comes with alternative C++ implementations that you can use or link to in your own applications.

Define APP_STL to select one of them. Examples are:
APP_STL := stlport_static --> static STLport library
APP_STL := stlport_shared --> shared STLport library
APP_STL := system --> default C++ runtime library

APP_STL就是用来告诉NDK使用哪一套stl的库来编译你的程序的,默认ndk使用最简化的c++库。
gnustl和stlport是不同的c++ stl的实现,stlport是完全免费的小巧的实现;
gnu也是免费的,但是gnu似乎不允许对其代码的修改。
一般是使用gnu,官方提供的stlport是不支持rtti和exception的,所以不能使用-frtti和-fexception这两个编译选项的。

APP_CPPFLAGS
    A set of C++ compiler flags passed when building C++ sources *only*.
APP_CPPFLAGS就简单了,这个就是c++编译是的一些编译开关。
-frtti 是打开rtti。(RTTI(Run-Time Type Information,通过运行时类型信息),不明白的可以自行搜索一下)。
-DCC_ENABLE_CHIPMUNK_INTEGRATION=1 这是设置CC_ENABLE_CHIPMUNK_INTEGRATION=1,是使用chipmunk物理引擎需要的,不用chipmunk的话可以删除
-DCOCOS2D_DEBUG=1 这是设置COCOS2D_DEBUG=1,表示是debug包,调试完毕可以删除。

(这里注意,rtti的开头是-f,而COCOS2D_DEBUG=1的开头是-D,我开始一直很迷惑这是什么意思,搜索也没有搜索到。
我估计-f应该是开启某个编译开关,我理解相当于 #define rtti
而-D应该是定义一个常量,#define COCOS2D_DEBUG 1)

至此,Application.mk就看完了,这个是比较简单的。

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

现在轮到Android.mk了:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := cocos2dcpp_shared

LOCAL_MODULE_FILENAME := libcocos2dcpp

LOCAL_SRC_FILES := hellocpp/main.cpp \
                   ../../Classes/AppDelegate.cpp \
                   ../../Classes/HelloWorldScene.cpp

LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes

LOCAL_WHOLE_STATIC_LIBRARIES += cocos2dx_static
LOCAL_WHOLE_STATIC_LIBRARIES += cocosdenshion_static
LOCAL_WHOLE_STATIC_LIBRARIES += box2d_static
LOCAL_WHOLE_STATIC_LIBRARIES += chipmunk_static
LOCAL_WHOLE_STATIC_LIBRARIES += cocos_extension_static

include $(BUILD_SHARED_LIBRARY)

$(call import-module,cocos2dx)
$(call import-module,cocos2dx/platform/third_party/android/prebuilt/libcurl)
$(call import-module,CocosDenshion/android)
$(call import-module,extensions)
$(call import-module,external/Box2D)
$(call import-module,external/chipmunk)

其实这个在NDK的帮助文档ANDROID-MK.html里面都有相应的解释,我这里在捋一捋:



LOCAL_PATH := $(call my-dir)

An Android.mk file must begin with the definition of the LOCAL_PATH variable.It is used to locate source files in the development tree. In this example,the macro function 'my-dir', provided by the build system, is used to returnthe path of the current directory (i.e. the directory containing theAndroid.mk file itself).

每一个Android.mk文件必须首先定义LOCAL_PATH变量,这个变量是用来在开发目录中查找源文件的。

“my-dir”是NDK系统提供的一个宏函数,能返回当前Android.mk所在的目录。




include $(CLEAR_VARS)

The CLEAR_VARS variable is provided by the build system and points to aspecial GNU Makefile that will clear many LOCAL_XXX variables for you(e.g. LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, etc...),with the exception of LOCAL_PATH. This is needed because all buildcontrol files are parsed in a single GNU Make execution context whereall variables are global.

CLEAR_VARS也是NDK提供的,它指向了一个特殊的GNU Makefile,用来清除几乎所有LOCAL_开头的变量(除了LOCAL_PATH)。

调用CLEAR_VARS是因为所有的编译控制文件都在一个GNU Make执行上下文中解析的,所以所有的这些LOCAL_开头的变量都是全局的,因此必须在编译前全部clear。




LOCAL_MODULE := cocos2dcpp_shared

The LOCAL_MODULE variable must be defined to identify each module youdescribe in your Android.mk. The name must be *unique* and not containany spaces. Note that the build system will automatically add properprefix and suffix to the corresponding generated file. In other words,a shared library module named 'foo' will generate 'libfoo.so'.


LOCAL_MODULE是用来定义每个Android.mk模块名称的,这个名称必须是唯一的。

NDK会添加适当的前缀和后缀到生成的文件。比如,“foo”模块编译后生成“libfoo.so”。

这个我们应该进行修改,因为我们不能用系统默认生成的名字,那样人家不是一眼就看出来了么,太肤浅了~

我们修改成game_shared,cocos2dx 2.2以前都是这个名字。。。。。

LOCAL_MODULE := game_shared




LOCAL_MODULE_FILENAME := libcocos2dcpp

This variable is optional, and allows you to redefine the name of generated files. By default, module <foo> will always generate a static library named lib<foo>.a or a shared library named lib<foo>.so, which are standard Unix conventions. You can override this by defining LOCAL_MODULE_FILENAME, For example: LOCAL_MODULE := foo-version-1 LOCAL_MODULE_FILENAME := libfoo


LOCAL_MODULE_FILENAME用来重新定义你生成的文件名,而不是按照默认的那样。

这个我们也改改,注意,不用将.so或者.a的拓展名写进去:

LOCAL_MODULE_FILENAME := libgame




LOCAL_SRC_FILES := hellocpp/main.cpp \
                   ../../Classes/AppDelegate.cpp \
                   ../../Classes/HelloWorldScene.cpp

LOCAL_SRC_FILES

This is a list of source files that will be built for your module. Only list the files that will be passed to a compiler, since the build system automatically computes dependencies for you. Note that source files names are all relative to LOCAL_PATH and you can use path components, e.g.: LOCAL_SRC_FILES := foo.c \ toto/bar.c NOTE: Always use Unix-style forward slashes (/) in build files. Windows-style back-slashes will not be handled properly.


LOCAL_SRC_FILES就是将需要编译的源文件列出来告诉编译器。需要注意的是,这里所有相对路径都是根据LOCAL_PATH来查找的。

我想修改一下main.cpp所在文件夹,不能叫hellocpp,太俗了!

我们工程叫MyGame,就将hellocpp修改成mygame吧,注意hellocpp就和Android.mk在同一个目录下。

由于Classes下面我们要单独建立一个模块,所以我直接将后面两行删除即可,至于怎么将Classes下面的模块引入进来一起编译后面会提到。

修改后:

LOCAL_SRC_FILES := mygame/main.cpp \
../../Classes/AppDelegate.cpp \
../../Classes/HelloWorldScene.cpp



LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes

LOCAL_C_INCLUDES

An optional list of paths, relative to the NDK *root* directory, which will be appended to the include search path when compiling all sources (C, C++ and Assembly).


LOCAL_C_INCLUDES定义了编译LOCAL_SRC_FILES中文件需要的头文件查找路径,注意这里的相对路径是根据NDK所在的目录进行查找的。

在上面的LOCAL_SRC_FILES中我们只保留了main.cpp,main.cpp里面包含的头文件:

#include "AppDelegate.h"
#include "cocos2d.h"
#include "CCEventType.h"
#include "platform/android/jni/JniHelper.h"
#include <jni.h>
#include <android/log.h>

其中#include <jni.h>和#include <android/log.h>是去NDK编译路径里面查找,因为它是<>包起来的,可以知道是优先去NDK的编译目录里面去查找。你可以在NDK所在目录的platform下找到这些文件。所以这个头文件查找路径不用我们添加。

而像#include "cocos2d.h"、#include "CCEventType.h"、#include "platform/android/jni/JniHelper.h"全是在/Users/zf/MyGame/libs/cocos2dx目录下,是在/Users/zf/MyGame/Classes目录下,这个在编译这些相应模块是会用到LOCAL_EXPORT_C_INCLUDES,将它们需要被引用的头文件目录导出来。简单理解就是LOCAL_EXPORT_C_INCLUDES会将后面的目录导出到使用该模块的Android.mk中去。

#include "AppDelegate.h"就是在/Users/zf/MyGame/Classes下面,上面是用相对路径写的LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes,这样无论怎么移动Mygame文件夹都不需要修改这个参数了。



LOCAL_WHOLE_STATIC_LIBRARIES += cocos2dx_static
LOCAL_WHOLE_STATIC_LIBRARIES += cocosdenshion_static
LOCAL_WHOLE_STATIC_LIBRARIES += box2d_static
LOCAL_WHOLE_STATIC_LIBRARIES += chipmunk_static
LOCAL_WHOLE_STATIC_LIBRARIES += cocos_extension_static


LOCAL_WHOLE_STATIC_LIBRARIES

A variant of LOCAL_STATIC_LIBRARIES used to express that the corresponding library module should be used as "whole archives" to the linker. See the GNU linker's documentation for the --whole-archive flag. This is generally useful when there are circular dependencies between several static libraries. Note that when used to build a shared library, this will force all object files from your whole static libraries to be added to the final binary. This is not true when generating executables though.

LOCAL_WHOLE_STATIC_LIBRARIES += cocos2dx_static:这就是说告诉linker,cocos2dx_static这个模块要座位“whole archives”。这个主要是用于多个库之间出现环形依赖的情况。当编译一个shared动态库的时候,所有被声明为LOCAL_WHOLE_STATIC_LIBRARIES的库最后都会添加到最终的包里面。



include $(BUILD_SHARED_LIBRARY)

The BUILD_SHARED_LIBRARY is a variable provided by the build system thatpoints to a GNU Makefile script that is in charge of collecting all theinformation you defined in LOCAL_XXX variables since the latest'include $(CLEAR_VARS)' and determine what to build, and how to do itexactly. There is also BUILD_STATIC_LIBRARY to generate a static library.

这一行就是实际执行生成最后的shared library的命令了。当然也可以用BUILD_STATIC_LIBRARY命令生成static library。


因为我也不知道shared和static library的区别,这里记录一下:

External libraries are usually provided in two forms: static libraries andshared libraries. Static libraries are the ‘.a’ files seen earlier. When a program is linked against a static library, the machine code from the object files for any external functions used by the program is copied from the library into the final executable.

Shared libraries are handled with a more advanced form of linking, which makes the executable file smaller. They use the extension‘.so’, which stands for shared object.

简单理解,.so的是shared library,这种库能使最终生成的可执行文件小一些。(我觉得,应该能在不同程序间共享使用,就是说操作系统载入一个a.so,那么所有需要a.so的程序不需要重复载入这个so文件了)

.a是static library,这种库最后会被复制一份添加到最终的可执行文件里面去。



$(call import-module,cocos2dx)
$(call import-module,cocos2dx/platform/third_party/android/prebuilt/libcurl)
$(call import-module,CocosDenshion/android)
$(call import-module,extensions)
$(call import-module,external/Box2D)
$(call import-module,external/chipmunk)

import-module

A function that allows you to find and include the Android.mk of another module by name. A typical example is: $(call import-module,<name>) And this will look for the module tagged <name> in the list of directories referenced by your NDK_MODULE_PATH environment variable, and include its Android.mk automatically for you. Read docs/IMPORT-MODULE.html for more details.

终于到了最后了,import-module就是根据名字查找其他模块的Android.mk文件,找到以后将那个Android.mk自动包含进去。也就是说,如果你的模块需要使用其他模块,并且其他模块需要使用NDK编译它的Android.mk的话,需要使用该命令。

需要注意的是,这个的查找目录是从你的NDK_MODULE_PATH目录还是查找的,这个目录在build_native.sh脚本里面设置。我到时候会将NDK_MODULE_PATH设置为/Users/zf/MyGame/libs。

$(call import-module,cocos2dx),这个是编译cocos2dx模块,这个模块的Android.mk文件是在/Users/zf/MyGame/libs/cocos2dx/目录下面,因为NDK_MODULE_PATH设置为/Users/zf/MyGame/libs,所以不用修改。

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

接下来我们修改/Users/zf/MyGame/proj.android/build_native.sh脚本,这个脚本是最后用来编译生成动态库的脚本文件。

这个脚本我就不一行一行解释了,只说重点,因为我对sh脚本的语言也不太熟悉。


首先需要在脚本最前面添加NDK_ROOT的定义,因为我们使用NDK编译,需要找到ndk-build这个命令文件,这个文件就在NDK的目录下面。

还记得之前说过,我将NDK放在目录/Users/zf/android-ndk-r8e/下面了么,所以添加:

NDK_ROOT=/Users/zf/android-ndk-r8e

这里在提一下,可以看到sh脚本最后使用了NDK_ROOT的语句:

"$NDK_ROOT"/ndk-build -C "$APP_ANDROID_ROOT" $* \
        "NDK_MODULE_PATH=${COCOS2DX_ROOT}:${COCOS2DX_ROOT}/cocos2dx/platform/third_party/android/source"
这里面"$NDK_ROOT"/ndk-build就是执行你设置的NDK_ROOT下面的ndk-build命令。


接下来设置COCOS2DX_ROOT、APP_ROOT、APP_ANDROID_ROOT三个参数,这三个参数我们都使用相对路径,这样MyGame以后无论怎么移动都不需要修改。


APP_ROOT是指MyGame程序的根目录,设置为:

APP_ROOT="$DIR/.."

$DIR就是当前build_native.sh所在的目录,所以我们将APP_ROOT设置为proj.android的上级目录,即:/Users/zf/MyGame/。


APP_ANDROID_ROOT是指android工程所在的目录,这个就是当前build_native.sh所在的目录:

APP_ANDROID_ROOT="$DIR"

这个APP_ANDROID_ROOT、APP_ROOT目录是用来复制MyGame资源文件到android目录下面使用的,可以看到后面的命令:

for file in "$APP_ROOT"/Resources/*
do
if [ -d "$file" ]; then
    cp -rf "$file" "$APP_ANDROID_ROOT"/assets
fi

if [ -f "$file" ]; then
    cp "$file" "$APP_ANDROID_ROOT"/assets
fi
done

这一段shell命令就是将APP_ROOT/Resources/下面的资源文件一个个复制到APP_ANDROID_ROOT/assets下面。

只是因为android工程编译运行后,需要将所有资源、代码打包成一个apk,这个apk就是一个压缩包。这个压缩包就是android系统的游戏安装文件。

android系统好像是在运行时才会让加载器解压apk读取资源,所以如果大型游戏,资源文件特别多,读取起来会非常慢,我之前碰到过这个问题。

一般采用的方法就是,将游戏中所有的资源文件打成一个zip压缩包,然后复制到APP_ANDROID_ROOT/assets下面,当游戏程序第一次运行的时候,将这个zip包解压到一个指定目录,以后所有资源都从那个目录读取即可。


COCOS2DX_ROOT指的是cocos2dx文件的目录,这个主要是NDK编译的时候查找cocos2dx文件使用。

还记得之前提到的import-module么?那里面我们提到的查找需要import的模块,都是从NDK_MODULE_PATH这个路径开始查找。

而这个NDK_MODULE_PATH是怎么设置的呢?注意看sh脚本这一行:

"$NDK_ROOT"/ndk-build -C "$APP_ANDROID_ROOT" $* \
        "NDK_MODULE_PATH=${COCOS2DX_ROOT}:${COCOS2DX_ROOT}/cocos2dx/platform/third_party/android/source"

看到了么,这里设置了NDK_MODULE_PATH为COCOS2DX_ROOT路径已经他的一个子路径。

由于我们之前引入的import命令设置成:$(call import-module,cocos2dx),所以这里我们将COCOS2DX_ROOT设置成cocos2dx的上级目录即可:

COCOS2DX_ROOT="$DIR/../libs"

至此,build_native.sh脚本已经修改完毕了。

接下来,使用term进入/Users/zf/MyGame/proj.android/下,执行./build_native.sh命令即可。

执行成功的话,应该是如下情况:


如果编译有错误,就要根据不同的错误进行处理了。

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

接下来打开ADT,选择File->New->Project




找到/Users/zf/MyGame/proj.android/这个目录,点击Finish。

如果你现在build刚建立好的MyGame工程,他会报错,找不到Cocos2dxActivity等类,这些java的类在/Users/zf/MyGame/libs/cocos2dx/platform/android/java/src下面的org文件夹里面,你只需要将org复制一份到/Users/zf/MyGame/proj.android/src下面即可。

对了,由于我们修改了生成的库文件的名称,从libcocos2dcpp.so改成了libgame.so,所以我们需要修改MyGame.java的一行代码:

System.loadLibrary("cocos2dcpp");修改成System.loadLibrary("game");

然后刷新工程,重新编译现在没有问题了。终于将程序跑起来:




这样就都搞定了,以后添加了cpp,修改一下Android.mk即可。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值