本文主要介绍Cocos2d-x的入门教程,包括详细的例子、讲解和实现过程。有需要的可以参考一下。
目前,智能手机上的游戏备受关注。哪个智能手机没有几款企鹅公司出品的游戏?我之前没有参与过游戏开发,但是我知道游戏开发之前要选择一个合适的游戏引擎,从零开始敲代码的时间就出来了。在找游戏引擎之前,我需要回答面前的三个选择题:
1.2D引擎还是3D引擎?2.平台专用引擎还是跨平台引擎?3.收费引擎还是开源引擎?
作为入门级玩家,2D游戏显然更适合入门。此外,适合这个年龄段的学龄前游戏大多是2D游戏。3D游戏本身太难了。不仅需要编程能力,还需要三维建模能力。这些学习周期太长;总是Ubuntu的粉丝,而我手头没有Mac本,开发iOS程序就成了一件坏事。在Ubuntu下搭建一个iOS的App开发环境太复杂了,连虚拟机都懒得尝试。但从游戏体验来说,还是在iPad上玩比较好,所以引擎最好能跨平台,以便后续迁移到iOS我习惯使用开源,付费引擎目前不在考虑范围内。总而言之,我正在寻找一个开源的,跨平台的移动2D游戏引擎。
所以我找到了Cocos2d-x!Cocos2d-x是Cocos2d-iphone的跨平台分支。因为是中国人创办的,所以在中国有大量的用户,有大量的引擎信息,有非常活跃的社区。国内已经出版了很多关于Cocos2d-x的中文书籍,比如《Cocos2d-x高级开发教程:制作自己的 “捕鱼达人”》,《Cocos2d-x权威指南》等。哪个都不差。更重要的是,Cocos2d-x自带了丰富的例子供初学者“复制学习”,其中Cocos2d-X-2 . 2 . 2/samples/CPP/test CPP是一个几乎涵盖了引擎所有功能的例子。让我们开始Cocos2d-x(安卓版)的入门之旅吧。
一、引擎安装
测试环境:复制代码如下:Ubuntu 12 . 04 . 1 x86 _ 64 gcc 4 . 6 . 3 javac 1 . 7 . 0 _ 21 Java ' 1 . 7 . 0 _ 21 ' Hotspot 64位服务器VM ADT-bundle-Linux-x86 _ 64-2013 10 30 . zip Android-。
Cocod-X官网目前提供2.2.2稳定版和3.0beta2下载(当然也可以下载老版本)。因为3.0变化很大,信息量不多,对编译器等版本要求高(需要支持C 11标准),所以2.2.2版本仍然是这里的学习目标。下载coco2d-x-2.2.2后,解压到一个目录:比如/home 1/tonybai/Android-dev/Cocos2d-x-2 . 2 . 2。如果只使用Cocos2d-x开发游戏的Android版本,则不需要做任何编译工作。Android游戏项目构建在项目构建时自动用NDK的编译器编译C代码,并与NDK链接。如果想看看Cocos2d-x示例中的例子运行时是什么样子,可以在Ubuntu下编译游戏的Linux版本:只需在cocos2d-x-2.2.2下执行make-all-linux-project.sh即可。编译需要一些时间。编译完成后,我们可以到“Cocos2d-x-2 . 2 . 2/samples/CPP/HelloCpp/proj . Linux/bin/release”,执行可执行文件“hello CPP”。一个最简单的cocos2d-x游戏就会展现在你面前。
Android示例项目的构建稍微复杂一些:
首先,从Eclipse中的现有代码添加libcocos2d x库项目(注意:不要复制到workspace,就地构建)。这个项目的代码路径是cocos2d-x-2 . 2 . 2/cocos2d x/platform/Android/Java。在project.properties和AndroidManifest.xml中,适当修改您使用的api版本以使编译通过。我这里用的是target=android-19。
然后,设置NDK根环境变量(如exportndk根='/home 1/tonybai/Android-dev/ADT-bundle-Linux-x86 _ 64/Android-ndk-R9c ')供build_native.sh使用
最后添加游戏项目。在黯然失色中添加来自现有代码的HelloCpp项目,位置cocos2d-x-2。2 .2/samples/Cpp/hello Cpp/proj安卓(注意:不复制到工作空间中,原地建立)。在HelloCpp的项目。属性中添加" android.library.reference.1=././././cocos 2dx/platform/Android/Java。同样别忘了在项目。属性和AndroidManifest.xml适当修改你所使用的美国石油学会(美国石油协会)版本,以让编译通过。
如果一切顺利的话,你会在安慰窗口看到"****构建完成**** .问题窗口显示0个错误。启动机器人模拟器,运行应用程序,同样的HelloCpp画面会呈现在模拟器上。
Cocos2d-x是建构在OpenGL技术之上的。对于机器人平台而言,Android SDK已经完全封装了opengl es 1.1/2.0的API(安卓。OpenGL。*;javax。微版。克罗诺斯。egl。*;javax。微版。克罗诺斯。opengles。*),引擎完全可以建立在这个之上,无需C代码。但Cocos2d-x是一个跨平台的2D游戏引擎,核心选择了用C代码实现(iOS提供的C绑定,不提供Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)绑定;机器人则提供了Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)和C绑定),因此在开发机器人平台的2D游戏时,引擎部分是软件开发工具包(软件开发工具包)与NDK交相互应,比如标距长度线程的创建和管理用的是软件开发工具包(软件开发工具包)的GLSurfaceView和GL线程但真正的表面绘制部分则是回调Cocos2d-x用C编写的绘制实现(链接NDK中的库)。
二、Cocos2d-x Android工程代码组织结构
以样品/Cpp/HelloApp的机器人工程为例,安卓版的Cocos2d-x工程与普通机器人应用程序差别不大,核心部分只是多了一个jni目录和一个build_native.sh脚本文件。其中jni目录下存放的是Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)和C调用转换的"胶水"代码;build_native.sh则是用于编译jni下C代码以及cocos2dx _静态库代码的构建脚本。
HelloCpp的构建过程摘要如下:复制代码代码如下:****项目HelloCpp的配置默认值的生成****
bash/home 1/tonybai/Android-dev/cocos2d-x-2。2 .2/samples/Cpp/hello Cpp/projAndroid/build _ native。shndk _ ROOT=/home 1/tonybai/Android-dev/ADT-bundle-Linux-x86 _ 64/Android-ndk-r9 CCO cos 2 dx _ ROOT=/home 1/tonybai/Android-dev/cocos2d-x-2。2 .2/samples/Cpp/hello Cpp/proj安卓/./././.APP _ ROOT=/home 1/tonybai/Android-dev/cocos2d-x-2。2 .2/samples/Cpp/hello Cpp/proj安卓/.APP _ ANDROID _ ROOT=/home 1/tonybai/ANDROID-dev/cocos2d-x-2。2 .2/samples/Cpp/hello Cpp/projANDROID/home 1/tonybai/ANDROID-dev/ADT-bundle-Linux-x86 _ 64/ANDROID-ndk-build-C/home 1/tonybai/ANDROID-dev/cocos2d-x-2。2 .2/samples/Cpp/hello Cpp/projandroidndk _模块_路径././././home 1/tonybai/Android-dev/cocos2d-x-2。2 .2/samples/Cpp/hello Cpp/proj安卓/././././cocos 2dx/platform/third _ party/Android/prebuilddtusing预构建外部安卓NDK:警告:/home 1/tonybai/Android-dev/cocos 2d-x-2。2 .2/samples/Cpp/hello Cpp/proj安卓/././././cocos 2 dx/Android。MK:cocos 2 dx _ static:LOCAL _ LDLIBS对于静态库总是被忽略品牌:进入目录`/home 1/tonybai/Android-dev/cocos 2d-x-2。2 .2/samples/Cpp/hello Cpp/proj安卓[阿尔梅ABI]编译拇指:你好Cpp _ shared=main。ABI陆军编译拇指:你好Cpp _ shared=app delegate。ABI陆军编译拇指:你好Cpp _ shared=你好世界
****构建完成****指挥NDK编译的则是jni下的Android.mk文件,其角色类似于生成文件。
三、Cocos2d-x Android工程代码阅读
单独将如何阅读代码拿出来,是为了后面分析引擎的驱动流程做准备工作。学习类似Cocos2d-x这样的游戏引擎,仅仅停留在游戏逻辑层代码是不能很好的把握引擎本质的,因此适当的挖掘引擎实现实际上对于理解和使用引擎都是大有裨益的。
以一个Cocos2d-x Android工程为例,它的游戏逻辑代码以及涉及的引擎代码涵盖在一下路径下(还是以HelloCpp的机器人工程为例):复制代码代码如下:项目层:* cocos2d-x-2。2 .2/samples/Cpp/hello Cpp/projAndroid/src主活动的实现;* cocos2d-x-2。2 .2/samples/Cpp/hello Cpp/proj安卓/JNI/hello Cpp cocos2d xd渲染器类的nativeInit实现,用于引出应用的入口;* cocos2d-x-2。2 .2/样品/Cpp/你好Cpp/类你的游戏逻辑,以C代码形式呈现;
引擎层:* cocos2d-x-2。2 .2/cocos2d x/平台/Android/Java/src引擎层对机器人活动、GLSurfaceView以及提供;给予的封装* cocos2d-x-2。2 .2/cocos2d x/平台/安卓/JNI对应上面封装的本地方法实现* cocos2d-x-2.2.2/cocos2dx、cocos2d-x-2。2 .2/cocos 2 dx/平台、cocos2d-x-2。2 .2/cocos 2 dx/platform/Android cocos 2 dx引擎的核心实现(针对机器人平台)后续的代码分析也将从这两个层次、六处位置出发。
四、从Activity开始
之前多少了解了一些安卓应用开发的知识,安卓应用都是始于活动的。游戏也是应用的一种,因此在机器人平台上,Cocos2d-x游戏也是从活动开始的。于是活动,确切的说是Cocos2dxActivity是我们这次引擎驱动机制分析的出发点。
回顾机器人活动的生命周期,活动启动的顺序是:活动。oncreate-activity。onstart()-活动。在简历上()。接下来我们将按照这条主线进行引擎驱动机制的分析。
HelloCpp.java中的HelloCpp这个活动完全无所作为,仅仅是继承其父类Cocos2dxActivity的实现罢了。复制代码代码如下://HelloCpp.javapublic类HelloCpp扩展cocos 2d x activity { protected void onCreate(Bundle savedInstanceState){ super。onCreate(savedInstanceState);} … …}我们来看Cocos2dxActivity类。复制代码代码如下://Cocos2dxActivity.java
@Overrideprotected void onCreate(最终捆绑包savedInstanceState){ super。onCreate(savedInstanceState);sContext=thisthis。mhandler=new cocos 2d xhandler(这个);这个。init();Cocos2dxHelper.init(这个,这个);}
public void init(){//框架布局视图组.布局参数框架layout _ params=新视图组LayoutParams(视图组LayoutParams。FILL_PARENT,ViewGroup .布局参数。FILL _ PARENT);框架布局框架布局=新框架布局(this);框架布局。setlayoutparams(框架布局_参数);
……//cocos 2 dxglsurface查看此。mglsurfaceview=this。oncreate view();
//…添加到框架布局框架布局。添加视图(this。mglsurfaceview);……这个。mglsurfaceview。setcocos 2 dx渲染器(新的cocos 2 dx渲染器());… …
//将框架布局设置为内容视图setContentView(框架布局);}
从上面代码可以看出,onCreate调用的初始化方法才是Cocos2dxActivity初始化的核心。在初始化方法中,Cocos2dxActivity创建了一个框架布局实例,并将该实例作为内容视图赋给了Cocos2dxActivity的实例框架布局实例也并不孤单,一个设置了Cocos2dxRenderer实例的GLSurfaceView被添加到它。而Cocos2d-x引擎的初始化已经悄悄地在这几行代码间完成了,至于初始化的细节我们后续再做分析。
接下来是简历上方法,它的实现如下:复制代码代码如下:@ Override resume()上受保护的void { super。on resume();
cocos 2d xhelper。on resume();这个。mglsurfaceview。on resume();}关于简历调用了视角的onResume()。复制代码代码如下://cocos 2 dxglsurfaceview:@ Override public void on resume(){ super。on resume();
这个。队列事件(new Runnable(){ @ Override public void run(){ cocos 2 dxglsurfaceview。这个。mco cos 2 dx渲染器。handleonresume();} });}Cocos2dxGLSurfaceView将该事件打包放到队列里,扔给了另外一个线程去执行(后续会详细说明这个线程),对应的方法在Cocos2dxRenderer类中。复制代码代码如下:public void handleOnResume(){ cocos 2 dx renderer。原生简历();}渲染实际上调用的是当地的方法。复制代码代码如下:JNI导出void JNI调用Java _ org _ cocos 2 dx _ lib _ cocos 2 dx renderer _ native resume(){ if(cc director:shared director()-getOpenGLView()){ cc application:shared application()-applicationwilletenter foreground();} }
应用程序将进入前景方法在你的AppDelegate.cpp中;
void app delegate:applicationwilletenterforeground(){ cc director:shared director()-startAnimation();//
//如果使用简单音频引擎,它必须在这里恢复//简单音频引擎:共享引擎()-resumeBackgroundMusic();}
这里仅是重新获得了一下时间罢了。
五、Render Thread(渲染线程) - GLThread
游戏引擎要兼顾用户界面事件和屏幕帧刷新安卓。的OpenGL应用采用了用户界面线程(主线程)渲染线程(渲染线程)的模式100 .活动活在主线程(主线程)中,也叫做用户界面线程。该线程负责捕获与用户交互的信息和事件,并与渲染(渲染)线程交互。比如当用户接听电话、切换到其他程序时,渲染线程必须知道发生了这些事件,并作出即时的处理,而这些事件及处理方式都是由主线程中的活动以及其装载的视角传递给渲染线程的。我们在Cocos2dx的框架代码中看不到渲染线程的诞生过程,这是因为这一过程是在Android SDK层实现的。
我们回顾一下Cocos2dxActivity.init方法的关键代码:复制代码代码如下://cocos 2 dxglsurface查看此。mglsurfaceview=this。oncreate view();
//…添加到框架布局框架布局。添加视图(this。mglsurfaceview);这个。mglsurfaceview。setcocos 2 dx渲染器(新的cocos 2 dx渲染器());
//将框架布局设置为内容视图setContentView(框架布局);
Cocos2dxGLSurfaceView是android.opengl.GLSurfaceView的子类。在机器人上做原生opengl es 2.0编程的人应该都清楚GLSurfaceView的重要性。但渲染线程并非是在Cocos2dxGLSurfaceView实例化时被创建的,而是在setRenderer的时候。
我们来看cocos 2 dxglsurface视图。setcocos 2 dx渲染器的实现:复制代码代码如下:public void setcocos 2 dx渲染器(最终的cocos 2 dx渲染器){ this。mcocos 2 dx渲染器=渲染器;这个。设置渲染器(this。mco cos 2 dx渲染器);}setRender是Cocos2dxGLSurfaceView父类GLSurfaceView实现的方法。在Android SDK GLSurfaceView.java文件中,我们看到:复制代码代码如下:public void set Renderer(Renderer Renderer){ checkRenderThreadState();if(mEGLConfigChooser==null){ mEGLConfigChooser=new SimpleEGLConfigChooser(true);} if(mEGLContextFactory==null){ mEGLContextFactory=new DefaultContextFactory();} if(mEGLWindowSurfaceFactory==null){ mEGLWindowSurfaceFactory=new DefaultWindowSurfaceFactory();} mRenderer=渲染器;mGLThread=新的GL线程(mthiswearref);mgl线程。start();}
标距长度线程的实例是在这里被创建并开始执行的。至于渲染线程都干了些什么,我们可以通过其奔跑方法看到:复制代码代码如下:@ Override public void run(){ setName(' GL thread ' getId());if(LOG _ THREADS){ LOG。I(' GL thread ',' starting tid=' getId());}
请尝试{ guardedRun();} catch(中断的异常e){//fall thru并正常退出}最后是{ sglthreadmanager。thread exiting(this);} }
奔跑方法并没有给我们带来太多有价值的东西,真正有价值的信息藏在卫队方法中100 .保护运行是这个源文件中规模最为庞大的方法,但抽取其核心结构后,我们发现它大致就是一个死循环,以下是摘要式的伪代码:复制代码代码如下:while(true){ synchronized(sGLThreadManager){ while(true){……如果(!meventqueue。isempty()){ event=meventqueue。删除(0);打破;} } }//同步结束(sGLThreadManager)
如果(事件!=null){ event。run();事件=空继续;}
如果需要查看。伦德勒先生。onsurfacecreated(GL,megl helper。megl配置);
如果需要查看。伦德勒先生。onsurfacechanged(GL,w,h);
如有需要查看。伦德勒先生。ondrawframe(GL);}在这里我们看到了事件、渲染器的三个回调方法onSurfaceCreated、onSurfaceChanged以及onDrawFrame,后续我们会对这三个函数做详细分析的。
六、游戏逻辑的入口
在HelloCpp的班级下有好多C代码文件(涉及具体的游戏逻辑),在HelloCpp的机器人项目jni目录下也有Jni胶水代码,那么这些代码是如何和引擎一起互动生效的呢?
上面讲到过,涉及到画面的一些渲染都是在标距长度线程中进行的,这涉及到onSurfaceCreated、onSurfaceChanged以及onDrawFrame三个方法。我们看看cocos 2 dx渲染器。已创建onsurface方法的实现,该方法会在表面被首次渲染时调用:复制代码代码如下:public void onSurfaceCreated(final GL10 pgl 10,final egl config pEGLConfig){ cocos 2 dx renderer。本机初始化(this。mscreenwutth这个。mscreenheight);这个。mlasttickinnanoseconds=system。纳米时间();}该方法继续调用HelloCpp工程jni目录下的nativeInit代码:复制代码代码如下:void Java _ org _ cocos 2 dx _ lib _ cocos 2 dx renderer _ native init(JNI env * env,jobject thiz,jint w,jint h){ if(!cc director:shared director()-getOpenGLView()){ CCEGLView * view=CCEGLView:sharedopenview();view-setFrameSize(w,h);
app delegate * pAppDelegate=new app delegate();CCApplication:共享应用程序()-run();} else { ccGLInvalidateStateCache();CCShaderCache:sharedShaderCache()-reloadDefaultShaders();ccDrawInit();CCTextureCache:reload all textures();cc通知中心:sharedNotificationCenter()-post通知(EVENT _ COME _ TO _ FOREGROUND,NULL);cc director:共享director()-setGLDefaultValues();}}
这似乎让我们看到了游戏逻辑的入口了:复制代码代码如下:CCEGLView * view=CCEGLView:sharedopengview();view-setFrameSize(w,h);
app delegate * pAppDelegate=new app delegate();CCApplication:共享应用程序()-run();继续追踪应用程序:运行方法:复制代码代码如下:int CCApplication:run(){ //初始化实例和cocos2d .如果(!applicationDidFinishLaunching()){ return 0;}
return-1;}
applicationDidFinishLaunching,没错这就是游戏逻辑的入口了。我们得回到样品代码目录中去找到对应方法的实现。复制代码代码如下://cocos2d-x-2。2 .2/samples/Cpp/hello Cpp/Classes/app委托。卡片打印处理机(Card Print Processor的缩写)
bool app delegate:applicationDidFinishLaunching(){//初始化director cc director * p director=cc director:共享director();CCEGLView * peg lview=CCEGLView:sharedopenview();
p director-setpengview(peg lview);cc size frameSize=peg lview-get frameSize();… …
//打开显示FPS p director-setDisplayStats(true);
//设置FPS .如果不调用这个p director-setAnimationInterval(1.0/60),默认值是1.0/60;
//创建一个场景。它是一个自动释放对象cc场景* PS场景=hello world:scene();
//运行p导演-runWithScene(PS cene);
返回真实}
的确,在applicationdiddfinishlaunching中我们做了很多引擎参数的设置。接下来大管家CCDirector实例登场,并运行了HelloWorld场景的实例。但这依旧是初始化的一部分,虽然方法名让人听起来像是某种持续连贯行为:复制代码代码如下://cocos2d-x-2。2 .2/cocos2d x/cc director。卡片打印处理机(Card Print Processor的缩写)
void cc director:runWithScene(cc scene * PS scene){……push scene(PS scene);start animation();}
void CCDisplayLinkDirector:startAnimation(void){ if(CCTime:gettimeofdayCocos2d(m _ Plast update,NULL)!=0){ cc log(' cocos2d:displaylink director:gettimeofday上的错误');}
m _ bInvalid=false}这两种方法都只是初始化了一些数据成员变量,并没有真正驱动引擎。
七、驱动引擎
游戏画面移动的原因是用更高的帧数刷新屏幕,使人眼看到连续的运动,这和电影放映原理是一样的。Cocos2d-x引擎中驱动屏幕刷新的这些代码在哪里?
我们再来回顾一下之前说的GLThread线程。我们说图像渲染的所有工作都是它做的。GLThread的核心是guardedRun函数,该函数以“无限循环”的方式调用Cocos2dxRender.onDrawFrame方法连续渲染画面。
我们来看看引擎实现的Cocos2dxRender.onDrawFrame方法:复制代码代码如下:public void ondrawframe(final GL 10g l){/* * FPS控制算法不准确,在某些设备上会减慢FPS *。所以注释FPS控制代码。*/
/* final long nowInNanoSeconds=system . nano time();最终长间隔=nowInNanoSecondsthis . mlasttickinnanoseconds;*/
//当调用onDrawFrame()或存在//' ghost ' cocos 2 dx renderer . native render()时,应呈现一个帧;
/* //fps控制if(interval cocos 2 dx renderer . sanimationinterval){ try {//因为我们之前渲染过,所以要休眠两次时间间隔thread . sleep((cocos 2 dx renderer . sanimationintervalinterval)/cocos 2 dx renderer。毫微秒(毫微秒);} catch(最终异常e) { } }
this . mlasttickinnanoseconds=nowInNanoSeconds;*/}这个方法实现起来很奇怪。好像修改过很多次,但最后决定只保留一个方法调用:cocos 2d xd renderer . native render()。从评论的代码来看,似乎是想通过这个方法中的Thread.sleep来控制Render线程渲染的帧率。但是因为控制不理想,我们干脆不控制,这就让guardedRun真的成了死循环。但根据HelloCpp示例的运行状态,画面始终在60帧左右,这是非常令人惊讶的。据说Cocos2d-x 3.0重新设计了渲染这一块的机制。(后记:虽然Android上没有帧数控制,但真正的渲染帧率其实是受‘垂直同步’信号影响的——vertical sync。在游戏中,也许一个强大的显卡可以快速绘制一个屏幕图像,但是在没有垂直同步信号到来的情况下,显卡无法绘制下一个屏幕,只能在vsync信号到来的时候进行绘制。这样fps实际上是受到操作系统刷新率值的限制)。
NativeRender从名字上看,这明显是c写的函数实现,我们只能在jni目录里找。复制代码如下:cocos2d-x-2 . 2 . 2/cocos2 dx/platform/Android/JNI/Java _ org _ cocos2 dx _ lib _ cocos2 dx renderer . CPP
JNI export void JNI call Java _ org _ cocos 2 dx _ lib _ cocos 2 dx renderer _ native render(JNI env * env){ cocos 2d:cc director:shared director()-main loop();}nativeRender也很简洁,直接调用CCDirector的mainLoop,也就是说CCDirector:mainLoop在渲染每一帧的过程中是真正在工作的。至此,我们终于找到了引擎渲染的驱动:GLThead:guardedRun,以“无限循环”的方式刷新画面,让我们感受到“动”的魅力。
八、mainLoop
让我们进一步看看mainLoop所做的工作。MainLoop是CCDirector类的纯虚函数,CCDirector的子类CCDisplayLinkDirector真正实现了它:复制代码如下://CCDirector。CPPvoid CCDisplayLinkDirector:主循环(void){ if(m _ bpurgedirecordorenectloop){ m _ bpurgedirecordorenectloop=false;purge director();} else if(!m _ bin valid){ draw scene();
//释放对象CCPoolManager:sharedPoolManager()-pop();}}
void cc director:draw scene(void){//calculate ' global ' dt calculate delta time();
//在glClear前打勾:issue #533 if(!m _ BPA used){ m _ pScheduler-update(m _ fDeltaTime);}
GL clear(GL _ COLOR _ BUFFER _ BIT | GL _ DEPTH _ BUFFER _ BIT);
/*为了避免flickr,nextScene必须在这里:打勾之后,画之前XXX:这是哪个bug .好像不能用v 0.9 */if(m _ pNextScene){ setNextScene();}
kmGLPushMatrix();
//绘制场景if(m _ pRunningScene){ m _ pRunningScene-visit();}
//绘制通知节点if(m _ pNotificationNode){ m _ pNotificationNode-visit();}
if(m _ bDisplayStats){ showStats();}
kmGLPopMatrix();
m _自动框架
//交换缓冲区if(m _ pobOpenGLView){ m _ pobOpenGLView-swap buffers();}
if(m _ bDisplayStats){ calculate mpf();}}帧渲染由主循环调用的绘图场景()完成,绘制场景方法根据事件下的渲染树,根据结节的最新属性逐个渲染节点,并调整各个结节的调度定时器数据,细节这里就不详细说明了。
九、UI线程与GLThread的交互
用户的屏幕触控动作由用户界面线程捕捉到,该类事件需要传递给引擎,并由标距长度线程根据各个画面元素的最新状态重新绘制画面。用户界面线程负责处理用户交互事件,并将特定的事件通知标距长度线程处理。用户界面线程通过Cocos2dxGLSurfaceView的队列事件方法,将事件以及处理方法传递给标距长度线程执行的。
Cocos2dxGLSurfaceView的队列事件方法继承自其父类GLSurfaceView:复制代码代码如下:公共void队列事件(Runnable r){ mgl线程。队列事件(r);}而标距长度线程的队列事件方法实现如下:复制代码代码如下:公共void队列事件(Runnable r){ if(r==null){ throw new IllegalArgumentException(' r不得为null’);}同步(sgl线程
Manager) {??????? mEventQueue.add(r);??????? sGLThreadManager.notifyAll();??? }? }该方法将event互斥地放入EventQueue,并通知阻塞在Queue上的线程取货。运行着的GLThread实例在guardedRun中会从event队列中取出runnable event并run的。复制代码 代码如下:? while (true) {??? synchronized (sGLThreadManager) {??????? while (true) {??????????? if (mShouldExit) {??????????????? return;??????????? }?
??????????? if (! mEventQueue.isEmpty()) {??????????????? event = mEventQueue.remove(0);??????????????? break;??????????? }? ???????? …….??????? }? ???? }?
???? … …???? if (event != null) {??????? event.run();??????? event = null;??????? continue;??? }? ??? …}
Activity的各种事件Pause、Resume、Stop以及View的各种屏幕触控事件都是通过queueEvent传递给GLThread执行的,比如:View的onKeyDown方法:复制代码 代码如下:??? //Cocos2dxGLSurfaceView.java??? @Override??? public boolean onKeyDown(final int pKeyCode, final KeyEvent pKeyEvent) {??????? switch (pKeyCode) {??????????? case KeyEvent.KEYCODE_BACK:??????????? case KeyEvent.KEYCODE_MENU:??????????????? this.queueEvent(new Runnable() {??????????????????? @Override??????????????????? public void run() {??????????????????????? Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleKeyDown(pKeyCode);??????????????????? }??????????????? });??????????????? return true;??????????? default:??????????????? return super.onKeyDown(pKeyCode, pKeyEvent);??????? }??? }
十、小结
有了以上的对Cocos2d-x引擎的理解后,再编写游戏代码就更加游刃有余了,至少出现问题时,我们知道应该在哪里查找了。就像对汽车的发动机了如指掌 后,一旦发生动力故障,我们基本知道排除的方法。但对发动机了解的再透彻,也不能代表就能设计和生产出好车,游戏也是这样,对引擎了解是一码事,设计和实现出好游戏是另外一码事。学习引擎只是编写游戏的起点而已。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。