http://blog.163.com/shiyanchunyee@126/blog/static/12667909820131191434362/
3.Android
Android平台的游戏是从一个Activity开始的。(话说好像Android的所有应用都是从Activity开始的吧)。
在引擎源码下有个目录是android的java代码,是模板代码,几乎所有的游戏都用这个,不怎么变。不信可以你可以看
YourCocos2dxDir/cocos2dx/platform/android/java这个目录,就是创建android工程的时候会去这个目录拷贝java代码作为模板。
来看看HelloCpp的代码
- package org.cocos2dx.hellocpp;
- import org.cocos2dx.lib.Cocos2dxActivity;
- import android.os.Bundle;
- public class HelloCpp extends Cocos2dxActivity{
- protected void onCreate(Bundle savedInstanceState){
- super.onCreate(savedInstanceState);
- }
- static {
- System.loadLibrary("hellocpp");
- }
- }
1. Cocos2dxActivity才是核心的Activity。
2. 游戏的C++部分包括引擎部分,被编译成了动态链接库hellocpp。这里就是加载了hellocpp动态链接库。
这个动态链接库是在用NDK编译的时候生成的,就是libs/armeabi/libhellocpp.so。(扯远了)
还是来看看Cocos2dxActivity这个Activity。
- public abstract class Cocos2dxActivity extends Activity implements Cocos2dxHelperListener {
- // ===========================================================
- // Constants
- // ===========================================================
- private static final String TAG = Cocos2dxActivity.class.getSimpleName();
- // ===========================================================
- // Fields
- // ===========================================================
- private Cocos2dxGLSurfaceView mGLSurfaceView;//注意这个SurfaceView
- private Cocos2dxHandler mHandler;
- // ===========================================================
- // Constructors
- // ===========================================================
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- this.mHandler = new Cocos2dxHandler(this);
- this.init();
- Cocos2dxHelper.init(this, this);
- }
- // ===========================================================
- // Getter & Setter
- // ===========================================================
- // ===========================================================
- // Methods for/from SuperClass/Interfaces
- // ===========================================================
- @Override
- protected void onResume() {
- super.onResume();
- Cocos2dxHelper.onResume();
- this.mGLSurfaceView.onResume();
- }
- @Override
- protected void onPause() {
- super.onPause();
- Cocos2dxHelper.onPause();
- this.mGLSurfaceView.onPause();
- }
- @Override
- public void showDialog(final String pTitle, final String pMessage) {
- Message msg = new Message();
- msg.what = Cocos2dxHandler.HANDLER_SHOW_DIALOG;
- msg.obj = new Cocos2dxHandler.DialogMessage(pTitle, pMessage);
- this.mHandler.sendMessage(msg);
- }
- @Override
- public void showEditTextDialog(final String pTitle, final String pContent, final int pInputMode, final int pInputFlag, final int pReturnType, final int pMaxLength) {
- Message msg = new Message();
- msg.what = Cocos2dxHandler.HANDLER_SHOW_EDITBOX_DIALOG;
- msg.obj = new Cocos2dxHandler.EditBoxMessage(pTitle, pContent, pInputMode, pInputFlag, pReturnType, pMaxLength);
- this.mHandler.sendMessage(msg);
- }
- @Override
- public void runOnGLThread(final Runnable pRunnable) {
- this.mGLSurfaceView.queueEvent(pRunnable);
- }
- // ===========================================================
- // Methods
- // ===========================================================
- 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);
- // Cocos2dxEditText layout
- ViewGroup.LayoutParams edittext_layout_params =
- new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT);
- Cocos2dxEditText edittext = new Cocos2dxEditText(this);
- edittext.setLayoutParams(edittext_layout_params);
- // ...add to FrameLayout
- framelayout.addView(edittext);
- // Cocos2dxGLSurfaceView
- this.mGLSurfaceView = this.onCreateView();
- // ...add to FrameLayout
- framelayout.addView(this.mGLSurfaceView);// 添加GLSurfaceView
- this.mGLSurfaceView.setCocos2dxRenderer(new Cocos2dxRenderer());//注意这行,一个渲染器
- this.mGLSurfaceView.setCocos2dxEditText(edittext);
- // Set framelayout as the content view
- setContentView(framelayout);
- }
- public Cocos2dxGLSurfaceView onCreateView() {
- return new Cocos2dxGLSurfaceView(this);
- }
- // ===========================================================
- // Inner and Anonymous Classes
- // ===========================================================
- }
来看这个Cocos2dxRenderer
- package org.cocos2dx.lib;
- import javax.microedition.khronos.egl.EGLConfig;
- import javax.microedition.khronos.opengles.GL10;
- import android.opengl.GLSurfaceView;
- public class Cocos2dxRenderer implements GLSurfaceView.Renderer {
- // ===========================================================
- // Constants
- // ===========================================================
- private final static long NANOSECONDSPERSECOND = 1000000000L;
- private final static long NANOSECONDSPERMICROSECOND = 1000000;
- private static long sAnimationInterval = (long) (1.0 / 60 * Cocos2dxRenderer.NANOSECONDSPERSECOND);
- // ===========================================================
- // Fields
- // ===========================================================
- private long mLastTickInNanoSeconds;
- private int mScreenWidth;
- private int mScreenHeight;
- // ===========================================================
- // Constructors
- // ===========================================================
- // ===========================================================
- // Getter & Setter
- // ===========================================================
- public static void setAnimationInterval(final double pAnimationInterval) {
- Cocos2dxRenderer.sAnimationInterval = (long) (pAnimationInterval * Cocos2dxRenderer.NANOSECONDSPERSECOND);
- }
- public void setScreenWidthAndHeight(final int pSurfaceWidth, final int pSurfaceHeight) {
- this.mScreenWidth = pSurfaceWidth;
- this.mScreenHeight = pSurfaceHeight;
- }
- // ===========================================================
- // Methods for/from SuperClass/Interfaces
- // ===========================================================
- @Override //①注意这里
- public void onSurfaceCreated(final GL10 pGL10, final EGLConfig pEGLConfig) {
- Cocos2dxRenderer.nativeInit(this.mScreenWidth, this.mScreenHeight);//②初始化窗口
- this.mLastTickInNanoSeconds = System.nanoTime();
- }
- @Override
- public void onSurfaceChanged(final GL10 pGL10, final int pWidth, final int pHeight) {
- }
- @Override //③注意这里
- public void onDrawFrame(final GL10 gl) {
- /*
- * FPS controlling algorithm is not accurate, and it will slow down FPS
- * on some devices. So comment FPS controlling code.
- */
- /*
- final long nowInNanoSeconds = System.nanoTime();
- final long interval = nowInNanoSeconds - this.mLastTickInNanoSeconds;
- */
- // should render a frame when onDrawFrame() is called or there is a
- // "ghost"
- Cocos2dxRenderer.nativeRender();//④特别注意这个
- /*
- // fps controlling
- if (interval < Cocos2dxRenderer.sAnimationInterval) {
- try {
- // because we render it before, so we should sleep twice time interval
- Thread.sleep((Cocos2dxRenderer.sAnimationInterval - interval) / Cocos2dxRenderer.NANOSECONDSPERMICROSECOND);
- } catch (final Exception e) {
- }
- }
- this.mLastTickInNanoSeconds = nowInNanoSeconds;
- */
- }
- // ===========================================================
- // Methods
- // ===========================================================
- private static native void nativeTouchesBegin(final int pID, final float pX, final float pY);
- private static native void nativeTouchesEnd(final int pID, final float pX, final float pY);
- private static native void nativeTouchesMove(final int[] pIDs, final float[] pXs, final float[] pYs);
- private static native void nativeTouchesCancel(final int[] pIDs, final float[] pXs, final float[] pYs);
- private static native boolean nativeKeyDown(final int pKeyCode);
- private static native void nativeRender();
- private static native void nativeInit(final int pWidth, final int pHeight);
- private static native void nativeOnPause();
- private static native void nativeOnResume();
- public void handleActionDown(final int pID, final float pX, final float pY) {
- Cocos2dxRenderer.nativeTouchesBegin(pID, pX, pY);
- }
- public void handleActionUp(final int pID, final float pX, final float pY) {
- Cocos2dxRenderer.nativeTouchesEnd(pID, pX, pY);
- }
- public void handleActionCancel(final int[] pIDs, final float[] pXs, final float[] pYs) {
- Cocos2dxRenderer.nativeTouchesCancel(pIDs, pXs, pYs);
- }
- public void handleActionMove(final int[] pIDs, final float[] pXs, final float[] pYs) {
- Cocos2dxRenderer.nativeTouchesMove(pIDs, pXs, pYs);
- }
- public void handleKeyDown(final int pKeyCode) {
- Cocos2dxRenderer.nativeKeyDown(pKeyCode);
- }
- public void handleOnPause() {
- Cocos2dxRenderer.nativeOnPause();
- }
- public void handleOnResume() {
- Cocos2dxRenderer.nativeOnResume();
- }
- private static native void nativeInsertText(final String pText);
- private static native void nativeDeleteBackward();
- private static native String nativeGetContentText();
- public void handleInsertText(final String pText) {
- Cocos2dxRenderer.nativeInsertText(pText);
- }
- public void handleDeleteBackward() {
- Cocos2dxRenderer.nativeDeleteBackward();
- }
- public String getContentText() {
- return Cocos2dxRenderer.nativeGetContentText();
- }
- // ===========================================================
- // Inner and Anonymous Classes
- // ===========================================================
- }
代码很多,一副貌似很复杂的样子。其实脉络很清晰的,我们只看脉络,不考虑细节哈。我们顺藤摸瓜来...
首先要知道GLSurfaceView的渲染器必须实现GLSurfaceView.Renderer接口。就是上面的三个Override方法。
onSurfaceCreated在窗口建立的时候调用,onSurfaceChanged在窗口建立和大小变化是调用,onDrawFrame这个方法就跟普通View的Ondraw方法一样,窗口建立初始化完成后渲染线程不停的调这个方法。这些都是框架决定的,就是这个样子。下面分析下代码几处标记的地方:
看标记①,窗口建立,这个时候要进行初始化。这个时候它调用了一个native函数,就是标记②。看到这个函数形式是不是能想到什么呢,等下再说。
看标记③,前面说了,这个函数会被渲染线程不停调用(像不像主循环的死循环啊)。然后里面有个很牛擦的函数④。
这又是一个native的函数,呵呵。
native的代码是神马啊,就是C++啊。知之为知之,不知谷歌之。
既然是java调用C++,那就是jni调用了。
我们来看看jni/hellocpp/下的main.cpp
- #include "AppDelegate.h"
- #include "platform/android/jni/JniHelper.h"
- #include <jni.h>
- #include <android/log.h>
- #include "HelloWorldScene.h"
- #define LOG_TAG "main"
- #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
- using namespace cocos2d;
- extern "C"
- {
- jint JNI_OnLoad(JavaVM *vm, void *reserved)
- {
- JniHelper::setJavaVM(vm);
- return JNI_VERSION_1_4;
- }
- void Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit(JNIEnv* env, jobject thiz, jint w, jint h)
- {
- if (!CCDirector::sharedDirector()->getOpenGLView())
- {
- CCEGLView *view = CCEGLView::sharedOpenGLView();
- view->setFrameSize(w, h);
- CCLog("with %d,height %d",w,h);
- AppDelegate *pAppDelegate = new AppDelegate();
- CCApplication::sharedApplication()->run(); // 看这里⑤
- }
- else
- {
- ccDrawInit();
- ccGLInvalidateStateCache();
- CCShaderCache::sharedShaderCache()->reloadDefaultShaders();
- CCTextureCache::reloadAllTextures();
- CCNotificationCenter::sharedNotificationCenter()->postNotification(EVNET_COME_TO_FOREGROUND, NULL);
- CCDirector::sharedDirector()->setGLDefaultValues();
- }
- }
- }
看标注⑤,你以为这个run函数就进入主循环了么,呵呵
看这个run
- int CCApplication::run()
- {
- // Initialize instance and cocos2d.
- if (! applicationDidFinishLaunching())
- {
- return 0;
- }
- return -1;
- }</span>
我们看到了神马!实质上就调了一下applicationDidFinishLaunching,别的什么也没干。所以这里没有进入主循环。
现在再看③和④。这个逻辑貌似就是主循环。
这个nativeRender()函数的实现在Yourcocos2dDir/cocos2dx/platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxRenderer.cpp
- #include "text_input_node/CCIMEDispatcher.h"
- #include "CCDirector.h"
- #include "../CCApplication.h"
- #include "platform/CCFileUtils.h"
- #include "CCEventType.h"
- #include "support/CCNotificationCenter.h"
- #include "JniHelper.h"
- #include <jni.h>
- using namespace cocos2d;
- extern "C" {
- JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeRender(JNIEnv* env) {
- cocos2d::CCDirector::sharedDirector()->mainLoop();//看到木有,这是什么
- }
- JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeOnPause() {
- CCApplication::sharedApplication()->applicationDidEnterBackground();
- CCNotificationCenter::sharedNotificationCenter()->postNotification(EVENT_COME_TO_BACKGROUND, NULL);
- }
- JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeOnResume() {
- if (CCDirector::sharedDirector()->getOpenGLView()) {
- CCApplication::sharedApplication()->applicationWillEnterForeground();
- }
- }
- JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInsertText(JNIEnv* env, jobject thiz, jstring text) {
- const char* pszText = env->GetStringUTFChars(text, NULL);
- cocos2d::CCIMEDispatcher::sharedDispatcher()->dispatchInsertText(pszText, strlen(pszText));
- env->ReleaseStringUTFChars(text, pszText);
- }
- JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeDeleteBackward(JNIEnv* env, jobject thiz) {
- cocos2d::CCIMEDispatcher::sharedDispatcher()->dispatchDeleteBackward();
- }
- JNIEXPORT jstring JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeGetContentText() {
- JNIEnv * env = 0;
- if (JniHelper::getJavaVM()->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK || ! env) {
- return 0;
- }
- const char * pszText = cocos2d::CCIMEDispatcher::sharedDispatcher()->getContentText();
- return env->NewStringUTF(pszText);
- }
- }
到这里可以发现,Android上的主循环跟win上的不太一样,它不是一个简单的while就完了。它是由java的渲染线程发起的,通过不断调用render来驱动。