cocos2dx在安卓平台的分析
⼀、引擎安装
试验环境:
Ubuntu 12.04.1 x86_64
gcc 4.6.3
javac 1.7.0_21
java "1.7.0_21" HotSpot 64-bit Server VM
adt-bundle-linux-x86_64-20131030.zip
android-ndk-r9d-linux-x86_64.tar.bz2
Cocos2d-x官⽹⽬前提供2.2.2稳定版以及3.0beta2版的下载(当然你也可以下载到更⽼的版本)。由于3.0改变较⼤,资料不 多,且对编译器等版本的要求较⾼(需要⽀持),因此这⾥依旧以2.2.2版本作为学习⽬标。Cocos2d-x-2.2.2下载后解压到某个⽬录:⽐
如/home1/tonybai/android-dev/cocos2d-x-2.2.2。 如果仅是⽤Cocos2d-x开发Android版本游戏,则不需要做什么编译⼯作。Android Game Project会在Project build时⾃动⽤NDK的编译器编译C++代码,并与NDK链接。如果你想早点看看Cocos2d-x sample中的例⼦运⾏起来到底是什么样⼦的,你可以在下编译出Linux版本的游戏:在cocos2d-x-2.2.2下执⾏make-all-linux-project.sh即可。编译需要⼀段时间,编译成 功后,我们可以进⼊到“cocos2d-x-2.2.2/samples/Cpp/HelloCpp/proj.linux/bin/release” 下执⾏“HelloCpp”这个可执⾏⽂件,⼀个最简单的Cocos2d-x游戏就会展现在你的⾯前了。
Android sample project的构建稍微复杂些:
⾸先在Eclipse中添加libcocos2dx Library project from existed code(注意:不Copy到workspace,原地建⽴)。该Project的代码路径为cocos2d-x-2.2.2//platform /android/java。在project.properties和l适当修改你所使⽤的api版本, 以让编译通过。我这⾥⽤的是 target=android-19。
然后,设置NDK_ROOT环境变量(⽐如export NDK_ROOT='/home1/tonybai/android-dev/adt-bundle-linux-x86_64/android-ndk-r9c'), 供build_native.sh使⽤。
最后添加游戏project。在Eclipse中添加HelloCpp project from existed code,位置cocos2d-x-
2.2.2/samples/Cpp/HelloCpp/proj.android(注 意:不Copy到Workspace中,原地建⽴)。在HelloCpp的project.properties中添
加“ference.1=../../../../cocos2dx/platform/android /java”。同样别忘了在project.properties和l适当修改你所使⽤ 的api版本,以让编译通过。
如果⼀切顺利的话,你会在Console窗⼝看到“**** Build Finished ****”。Problems窗⼝显⽰“0 errors“。 启动Android模拟器,Run Application,同样的HelloCpp画⾯会呈现在模拟器上。
Cocos2d-x是建构在OpenGL技术之上的。对于Android平台⽽⾔,Android SDK已经完全封装了opengl es 1.1/2.0的
API(android.opengl.*;javax.l.*;javax.microedition.khronos.opengles.*), 引擎完全可以建⽴在这个之上,⽆需
C++代码。但Cocos2d-x是⼀个跨平台的2D游戏引擎,核⼼选择了⽤C++代码实现(iOS提供的C绑 定,不提供Java绑定;Android则提供了Java和C绑定),因此 在开发Android平台的2D游戏时,引擎部分是SDK与NDK交相互应,⽐如GLThread的创建和管理⽤的是SDK的GLSurfaceView和GLThread,但真正的Surface绘制部分则是回调Cocos2d-x⽤C++编写的绘制实现(链接NDK 中的库)。
⼆、Cocos2d-x Android⼯程代码组织结构
以samples/Cpp/HelloApp的Android⼯程为例,Android版的Cocos2d-x⼯程与普通android应⽤程序 差别 不⼤,核⼼部分只是多了⼀个jni⽬录和⼀个build_native.sh脚本⽂件。其中jni⽬录下存放的是Java和C++调⽤转换的“胶 ⽔”代码;build_native.sh则是⽤于编译jni下C++代码以及 cocos2dx_static library代码的构建脚本。
HelloCpp的构建过程摘要如下:
**** Build of configuration Default for project HelloCpp ****
**** Build Finished ****
指挥NDK编译的则是jni下的Android.mk⽂件,其⾓⾊类似于Makefile。
三、Cocos2d-x Android⼯程代码阅读
单独将如何阅读代码拿出来,是为了后⾯分析引擎的驱动流程做准备⼯作。学习类似Cocos2d-x这样的游戏引擎,仅仅停留在游戏逻辑层代码是不 能很好的把握引擎本质的,因此适当的挖掘引擎实现实际上对于理解和使⽤ 引擎都是⼤有裨益的。
以⼀个Cocos2d-x Android⼯程为例,它的游戏逻辑代码以及涉及的引擎代码涵盖在⼀下路径下(还是以HelloCpp的Android⼯程为例):
项⽬层:
* cocos2d-x-2.2.2/samples/Cpp/HelloCpp/proj.android/src 主Activity的实现;
* cocos2d-x-2.2.2/samples/Cpp/HelloCpp/proj.android/jni/hellocpp Cocos2dxRenderer类的nativeInit实现,⽤于引出Application的⼊⼝;
* cocos2d-x-2.2.2/samples/Cpp/HelloCpp/Classes 你的游戏逻辑,以C++代码形式呈现;
引擎层:
* cocos2d-x-2.2.2/cocos2dx/platform/android/java/src 引擎层对Android Activity、GLSurfaceView以及Render的封装
* cocos2d-x-2.2.2/cocos2dx/platform/android/jni 对应上⾯封装的native method实现
* cocos2d-x-2.2.2/cocos2dx、cocos2d-x-2.2.2/cocos2dx/platform、cocos2d-x- 2.2.2/cocos2dx/platform/android cocos2dx引擎的核⼼实现(针对android平台)
后续的代码分析也将从这两个层次、六处位置出发。
四、从Activity开始
之前多少了解了⼀些Android App开发的知识,Android App都是始于Activity的。游戏也是App的⼀种,因此在Android平台
上,Cocos2d-x游戏也是从Activity开始的。于是 Activity,确切的说是Cocos2dxActivity是我们这次引擎驱动机制分析的出发点。
回顾Android Activity的Lifecycle,Activity启动的顺序是:Create -> Start() -> Resume()。接下来我们将按照 这条主线进⾏引擎驱动机制的分析。
HelloCpp.java中的HelloCpp这个Activity完全⽆所作为,仅仅是继承其⽗类Cocos2dxActivity的实现罢 了。
// HelloCpp.java
public class HelloCpp extends Cocos2dxActivity{
protected void onCreate(Bundle savedInstanceState){
}
… …
}
我们来看Cocos2dxActivity类。
// Cocos2dxActivity.java
@Override
protected void onCreate(final Bundle savedInstanceState) {
sContext = this;
this.mHandler = new Cocos2dxHandler(this);安卓app开发用什么框架
this.init();
Cocos2dxHelper.init(this, this);
}
public void init() {
// FrameLayout
ViewGroup.LayoutParams framelayout_params =
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.FILL_PARENT);
FrameLayout framelayout = new FrameLayout(this);
framelayout.setLayoutParams(framelayout_params);
… …
// Cocos2dxGLSurfaceView
this.mGLSurfaceView = CreateView();
// …add to FrameLayout
framelayout.addView(this.mGLSurfaceView);
… …
this.mGLSurfaceView.setCocos2dxRenderer(new Cocos2dxRenderer());
… …
// Set framelayout as the content view
setContentView(framelayout);
}
从上⾯代码可以看出,onCreate调⽤的init⽅法才是Cocos2dxActivity初始化的核⼼。在init⽅法 中,Cocos2dxActivity创建了⼀个Framelayout实例,并将该实例作为content View赋给了Cocos2dxActivity的实例。Framelayout实例也并不孤单,⼀个设置了
Cocos2dxRenderer实例的 GLSurfaceView被Added to it。⽽Cocos2d-x引擎的初始化已经悄悄地在这⼏⾏代码间完成了,⾄于初始化的细节我们后续再做分析。
接下来是onResume⽅法,它的实现如下:
@Override
protected void onResume() {
Resume();
}
onResume调⽤了View的onResume()。
// Cocos2dxGLSurfaceView:
@Override
public void onResume() {
this.queueEvent(new Runnable() {
@Override
public void run() {
Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleOnResume();
}
});
}
Cocos2dxGLSurfaceView将该事件打包放到队列⾥,扔给了另外⼀个线程去执⾏(后续会详细说明这个线程),对应的⽅法在
Cocos2dxRenderer class中。
public void handleOnResume() {
Cocos2dxRenderer.nativeOnResume();
}
Render实际上调⽤的是native⽅法。
JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeOnResume() {
if (CCDirector::sharedDirector()->getOpenGLView()) {
CCApplication::sharedApplication()->applicationWillEnterForeground();
}
}
applicationWillEnterForeground⽅法在你的AppDelegate.cpp中;
void AppDelegate::applicationWillEnterForeground() {
CCDirector::sharedDirector()->startAnimation();//
// if you use SimpleAudioEngine, it must resume here
// SimpleAudioEngine::sharedEngine()->resumeBackgroundMusic();
}
这⾥仅是重新获得了⼀下时间罢了。
五、Render Thread(渲染线程) - GLThread
游戏引擎要兼顾UI事件和屏幕帧刷新。Android的OpenGL应⽤采⽤了UI线程(Main Thread) + 渲染线程(Render Thread)的模式。Activity活在Main Thread(主线程)中,也叫做UI线程。该线程负责捕获与⽤户交互的信息和事件,并与渲染(Render)线程交互。⽐如当⽤户接听电话、切换到其他 程序时,渲染线程必须知道发⽣了 这些事件,并作出即时的处理,⽽这些事件及处理⽅式都是由主线程中的Activity以及其装载的View传递给渲染线程的。我们在Cocos2dx的框 架代码中看不到渲染线程的诞⽣过程,这是因为这⼀过程是在Android SDK层实现的。
我们回顾⼀下Cocos2dxActivity.init⽅法的关键代码:
// Cocos2dxGLSurfaceView
this.mGLSurfaceView = CreateView();
// …add to FrameLayout
framelayout.addView(this.mGLSurfaceView);
this.mGLSurfaceView.setCocos2dxRenderer(new Cocos2dxRenderer());
// Set framelayout as the content view
setContentView(framelayout);
Cocos2dxGLSurfaceView是 android.opengl.GLSurfaceView的⼦类。在android 上做原⽣opengl es 2.0编程的⼈应该都清楚GLSurfaceView的重要性。但渲染线程并⾮是在Cocos2dxGLSurfaceView实例化时被创建的,⽽是在 setRenderer的时候。
我们来看Cocos2dxGLSurfaceView.setCocos2dxRenderer的实现:
public void setCocos2dxRenderer(final Cocos2dxRenderer renderer) {
this.mCocos2dxRenderer = renderer;
this.setRenderer(this.mCocos2dxRenderer);
}
setRender是Cocos2dxGLSurfaceView⽗类GLSurfaceView实现的⽅法。在Android SDK GLSurfaceView.java⽂件中,我们看到:
public void setRenderer(Renderer renderer) {
checkRenderThreadState();
if (mEGLConfigChooser == null) {
mEGLConfigChooser = new SimpleEGLConfigChooser(true);
}
if (mEGLContextFactory == null) {
mEGLContextFactory = new DefaultContextFactory();
}
if (mEGLWindowSurfaceFactory == null) {
mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
}
mRenderer = renderer;
mGLThread = new GLThread(mThisWeakRef);
mGLThread.start();
}
GLThread的实例是在这⾥被创建并开始执⾏的。⾄于渲染线程都⼲了些什么,我们可以通过其run⽅法看到:
@Override
public void run() {
setName("GLThread " + getId());
if (LOG_THREADS) {
Log.i("GLThread", "starting tid=" + getId());
}
try {
guardedRun();
} catch (InterruptedException e) {
// fall thru and exit normally
} finally {
sGLThreadManager.threadExiting(this);
}
}
run⽅法并没有给我们带来太多有价值的东西,真正有价值的信息藏在guardedRun⽅法中。guardedRun是这个源⽂件中规模最为庞 ⼤的⽅法,但抽取其核⼼结构后,我们发现它⼤致就是⼀个死循环,以下是摘要式的伪代码:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论