cocos android分析

來自:http://xiebaochun.github.io/html

cocos2d-x Android環境搭建java

cocos2d-x環境搭建比較簡單,但是小問題仍是很多,我儘可能都涵蓋的全面一些。



下載軟件 
cygwin、NDK(ADT):C++相關 
假設以前沒有Android開發環境。還需要Android SDK,Eclipse 
cocos2d-x源代碼 
個人環境爲ndk r7。cygwin1.7,Android SDK爲2.2和3.0.另外,我是經過真機調試,在模擬器上不行,預計仍是我T410顯卡的問題.
安裝cygwin。在cygwin文件進行路徑設置 
在cygwin\home\Administrator的.bash_profile中加入例如如下代碼


   1: ANDROID_NDK_ROOT=/cygdrive/e/ADT/android-ndk-r7c


   2: export ANDROID_NDK_ROOT


   3: NDK_ROOT=/cygdrive/e/ADT/android-ndk-r7c


   4: export NDK_ROOT
將libgnustl_static.a從NDK中的android-ndk-r7c\sources\cxx-stl\gnu-libstdc++\libs\armeabi拷貝至cocos2d-1.0.1-x-0.13.0-beta\HelloWorld\android\obj\local\armeabi。這個從解決方式上看應該是stl的引用不一致致使的問題。但編譯中會報錯「png.a can not find」,但是path路徑確實沒什麼問題,因此比較坑爹。總之這樣就搞定了。我也沒怎麼深究。
進入cocos2d-1.0.1-x-0.13.0-beta\HelloWorld\android文件夾下改動例如如下內容到指定路徑


   1: NDK_ROOT_LOCAL=/cygdrive/e/ADT/android-ndk-r7c


   2: COCOS2DX_ROOT_LOCAL=/cygdrive/f/cocos2d-1.0.1-x-0.13.0-beta
cygwin中進入cocos2d-1.0.1-x-0.13.0-beta\HelloWorld\android文件夾下。運行./build_native.sh,編譯C++,JNI接口。供Android Java使用,假設成功。在在libs中生成libhelloworld.so動態庫。咱們都是爲了它作了這麼多工做
在Eclipse中導入cocos2d-1.0.1-x-0.13.0-beta\HelloWorld\androidproject。熟悉Android的一看就發現,事實上這自己就是一個Javaproject,咱們剛纔的操做僅僅是當中jni的部分,供Java如下的調用實現而已
Eclipse中運行Build Project。生成R.java
Run
 
 
Make
ndk的Make是在GNU的Make的基礎上的一種封裝,如下咱們來分析一下./build_native.sh都作了哪些操做。

簡單說主要是資源拷貝和代碼編譯。


資源拷貝在個人cygwin裏面發現有問題。拷貝後的文件是錯誤的,且不能刪除我沒有深究,本身手動拷貝了一下。和shell一致,很是easy理解,再也不深究。


ndk-build編譯HelloWorldproject。編譯jni目錄如下的Android.mk,和makefile基本類似,指定需要編譯的文件。include路徑,依賴projectcocos2dx_static。進行編譯,好比HelloWorld的makefile大體例如如下:

LOCAL_PATH := $(call my-dir)

 include $(CLEAR_VARS)

LOCAL_MODULE := helloworld_shared

LOCAL_MODULE_FILENAME := libhelloworld
LOCAL_SRC_FILES := helloworld/main.cpp \

                   ../../Classes/AppDelegate.cpp \

            ../../Classes/HelloWorldScene.cpp

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

LOCAL_WHOLE_STATIC_LIBRARIES := cocos2dx_static

include $(BUILD_SHARED_LIBRARY)

$(call import-module,cocos2dx)
LOCAL_PATH := $(call my-dir):指定當前路徑爲爲LOCAL_PATH


include $(CLEAR_VARS):清空除LOCAL_PATH以外的其它環境變量的干擾


LOCAL_MODULE&LOCAL_MODULE_FILENAME:模塊名稱&生成庫名稱


LOCAL_SRC_FILES:編譯的C++ Source


LOCAL_WHOLE_STATIC_LIBRARIES:依賴的靜態庫


BUILD_SHARED_LIBRARY:生成的爲共享庫。因爲Android的動態庫都爲JNI所用,因此稱爲共享庫。而靜態庫僅僅爲其它C++庫所用。android




$(call import-module,<name>):經過NDK_MODULE_PATH環境變量引用的模塊<name>的文件夾列表。並且將其本身主動包括到Android.mk中


這樣,一個編譯環境的include,library,target基本指定。則編譯出終於的目標文件,和makefile思路上沒什麼差異,另外這裏需要編譯出cocos2dx.a,靜態庫,是經過cocos2d目錄中的make編譯而成。這個腳本則要複雜一些,只是思想並沒有不一樣。不少其它NDK Make可以參考:《Android Make》


JNI交互
C++接口封裝完成後,咱們就開始看一下Java代碼,瞭解一下終於實現的流程和效果,Java代碼例如如下:


 


Java層的框架也很是easy。這裏並無多Accelerometer和Music、Sound等進行分析,僅僅是對涉及到顯示相關的進行分析。Java層面流程例如如下:


 


如上,假設熟悉Android界面開發,可以從基類瞭解到Java層面是經過Activity、GLSuffaceView來進行的顯示。c++

這裏不具體介紹。假設有興趣。可以看一下《剖析遊戲開發用view仍是surfaceView》,View相似傳統的二維靜態界面,數據驅動顯示,而SurfaceView則相似三維機制,實時渲染。git

因爲Cocos2d是OpenGL的。這也好解釋。


對於整個框架事實上要說的也很是多,只是我對Java還不太瞭解。因此有些東西看的不必定透。也不免有一些問題。


Renderer
Renderer類負責每一幀的渲染驅動,調用步驟如圖裏面的1和2。在2中調用jni裏面的nativeRender實現一幀的渲染。而GLSurfaceView則負責UI交互的監聽。


這樣的機制的優勢是在Java中Renderer渲染器是獨立線程調用。所以和UI之間沒有交互性。這樣既保證了用戶體驗(用戶的事件經過GLSurfaceView監聽,終於經過Renderer傳遞至C++層面來響應),也保證了渲染過程的抗干擾,依然經過C++層面進行渲染。github

,整個顯示過程用到的jni封裝主要例如如下:

shell

 private static native void nativeTouchesBegin(int id, float x, float y);

 private static native void nativeTouchesEnd(int id, float x, float y);

private static native void nativeTouchesMove(int[] id, float[] x, float[] y);

private static native void nativeTouchesCancel(int[] id, float[] x, float[] y);

private static native boolean nativeKeyDown(int keyCode);

private static native void nativeRender();

private static native void nativeInit(int w, int h);

private static native void nativeOnPause();

private static native void nativeOnResume();




jni封裝
jni的封裝主要有兩部分,一個是cocos2d本身的JNI封裝,這部分封裝主要是爲了在Java中調用cocos2d的jni接口,一個是HelloWorld中本身的jni接口封裝。這一塊原本是我比較感興趣的地方,因爲jni封裝仍是挺繁瑣的一件事情。最後發現cocos2d在本質上也沒有什麼差異,麻煩的仍是得封裝。

第二點,cocos2d主要是遊戲引擎。因此基本所有功能都是由C++層面來實現,一幀的渲染,事件的處理,而Java層主要負責邏輯處理,終於經過jni調用C++接口來實現。第三點來講,cocos2d自己封裝的仍是很是簡潔的,這點我認爲作的仍是很是優雅的,在設計這塊,是以Java的邏輯爲根據來進行劃分。我認爲這個很是可取,儘管cocos2d是C++作起來的。但是並無爲了保證各個平臺的一致性而強迫接口的一致。而是在jni層依照SDK在詳細平臺的應用特色來進行封裝,這樣減低了實現難度。提升了代碼的易用度。犧牲就是應用平臺接口的局部不一致性。jni層面主要是事件傳遞和窗體渲染部分的接口封裝,針對遊戲開發人員而言,最核心的部分都可以在Windows平臺下完畢,而後在Android部分完畢特有事件的傳遞,渲染部分直接採用cocos2d給出的標準範例實現就能夠。大大簡化了開發人員本身封裝jni的工做。


窗體綁定
窗體綁定我理解的並不太透徹,首先,我以爲CCEGLView_Android僅僅是一個虛的窗體,並無實質功能。僅僅是爲了便於架構理解。
bash

void Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit(JNIEnv*  env, jobject thiz, jint w, jint h)

 {

   if (!cocos2d::CCDirector::sharedDirector()->getOpenGLView())

   {

       cocos2d::CCEGLView *view = &cocos2d::CCEGLView::sharedOpenGLView();

      view->setFrameWidthAndHeight(w, h);

      // if you want to run in WVGA with HVGA resource, set it

       // view->create(480, 320);  Please change it to (320, 480) if you're in portrait mode.

       cocos2d::CCDirector::sharedDirector()->setOpenGLView(view);



        AppDelegate *pAppDelegate = new AppDelegate();
       cocos2d::CCApplication::sharedApplication().run();

  }


}

 

void Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeRender(JNIEnv* env)

 {

  cocos2d::CCDirector::sharedDirector()->mainLoop();

}
 



函數一是Java層調用onSurfaceCreated時調用函數,用來獲取GLView窗體。用來下一步的渲染,而這個View窗體並無相似Windows下的handle綁定,而接下來函數二是Java中onDrawFrame渲染每一幀時進行調用,終於調用底層的Director渲染,完畢一幀繪製(具體內容可參考《cocos2d-x之HelloWorld範例分析(一)》)。


怎麼來理解這樣的窗體綁定方式,保證我現在調用的gl函數,就行繪製在窗體呢,通篇沒有一個相似的handle從Java傳遞給JNI,通篇C++層面的View也僅僅是一個僅僅有Width和Height屬性的結構體,因此我理解的是GLSurfaceView.Renderer默認在本身的線程中進行了封裝,已經本身完畢了和OpenGL的綁定。這個我認爲應該是靠譜的吧,而且本身來實時的每一幀渲染,如下的就不管裏,你本身願意調Java的接口也行,本身調gl的渲染也能夠。這樣也挺好的,都不用我顧慮這個事情了,僅僅要給我高度寬度知道位置信息,我直接渲染。


文字
其它圖形圖像的繪製,都是和系統無關的。整個的渲染過程,也是跨平臺的,一個平臺的整合。主要是環境搭建、不一樣語言之間的消息傳遞、View的映射這些,前面也都闡述了,僅僅是文字有必定的特殊。在Windows下使用CDC。在Linux是Freetype,在Android下怎樣實現?我認爲cocos2d實現思路也是不錯的:C++經過JNI在Java層繪製。生成一張BitMap給C++,而後貼圖完畢。這個長處是簡單,缺點就是假設文字太多的話,效率損失仍是有的,事實上我認爲假設有機會,仍是用Freetype來畫應該也可以嘗試一下。


固然,也新學了一招。C++調用Java的方式,在jni裏面也提供了,呵呵。代碼在如下貼一下:



bool getBitmapFromJava(const char *text, int nWidth, int nHeight, CCImage::ETextAlign eAlignMask, const char * pFontName, float fontSize)

{

  JniMethodInfo methodInfo;
if (! JniHelper::getStaticMethodInfo(methodInfo, "org/cocos2dx/lib/Cocos2dxBitmap", "createTextBitmap", 

       "(Ljava/lang/String;Ljava/lang/String;IIII)V"))

    {

      CCLOG("%s %d: error to get methodInfo", __FILE__, __LINE__);
      return false;

   }


    jstring jstrText = methodInfo.env->NewStringUTF(text);

    jstring jstrFont = methodInfo.env->NewStringUTF(pFontName);



   methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID, jstrText, 
    jstrFont, (int)fontSize, eAlignMask, nWidth, nHeight);


    methodInfo.env->DeleteLocalRef(jstrText);
   methodInfo.env->DeleteLocalRef(jstrFont);

   methodInfo.env->DeleteLocalRef(methodInfo.classID);


  return true;

}

static bool getStaticMethodInfo_(cocos2d::JniMethodInfo &methodinfo, const char *className, const char *methodName, const char *paramCode)

{

   jmethodID methodID = 0;

  JNIEnv *pEnv = 0;

  if (! getEnv(&pEnv))

    {

        break;
   }


   jclass classID = getClassID_(className, pEnv);


    methodID = pEnv->GetStaticMethodID(classID, methodName, paramCode);

 
參照裏面的凝視,C++驅動Java實現繪製,Java完畢繪製後,調用Java_org_cocos2dx_lib_Cocos2dxBitmap_nativeInitBitmapDC接口,實現內存的拷貝,而s_BmpDC中的m_pData用來保存,進行下一步的紋理貼圖,完畢整改流程的傳遞. 總結 介紹完成,整個過程當中,cocos2d使用的技術並不神奇,主要是一個熟悉的過程.最值得稱讚的是JNI封裝的比較使用,自己作遊戲開發,基本所有功能都會在C++中封閉實現,僅僅需要提供一個規範的Java外殼就可以,既跨平臺有高效.另外,就是cocos2d對各個平臺的語言取捨,哪些用Java方便,哪些用C++ 保持平臺一致,都作的仍是很是合理的.
相關文章
相關標籤/搜索