cocos2d-x遊戲引擎核心之九——跨平臺

1、cocos2d-x跨平臺html

cocos2d-x究竟是怎樣實現跨平臺的呢?這裏以Win32和Android爲例。html5

1. 跨平臺項目目錄結構java

先看一下一個項目建立後的目錄結構吧!這仍是以HelloCpp爲例。python

從左邊目錄能夠看到,Classes和Resource已經平臺無關了,而Classes中包含了AppDelegate類,所以咱們能夠認爲AppDelegate是與平臺最接近的類,在它以上就要區分平臺了。linux

2. Win32下的實現android

在前一篇就介紹了Win32怎麼開始cocos2dx,Win32平臺下main.cpp就是程序入口:c++

int APIENTRY _tWinMain(HINSTANCE hInstance,  
                       HINSTANCE hPrevInstance,  
                       LPTSTR    lpCmdLine,  
                       int       nCmdShow)  
{  
    UNREFERENCED_PARAMETER(hPrevInstance);  
    UNREFERENCED_PARAMETER(lpCmdLine);  
  
    // create the application instance  
    AppDelegate app;//建立應用實例  
    CCEGLView* eglView = CCEGLView::sharedOpenGLView();  
    eglView->setViewName("HelloCpp");  
    eglView->setFrameSize(2048, 1536);  
    eglView->setFrameZoomFactor(0.4f);  
    return CCApplication::sharedApplication()->run();//運行程序  
}  

Win32下的實現比較簡單,就是正常的建立實例,運行就能夠了。編程

3.Android下的實現瀏覽器

3.1.cocos2d-x程序入口網絡

咱們先看一下Android下cocos2d-x程序入口點在哪,咱們知道Android是採用Java編寫的,而cocos2d-x是c++編寫的,因此若是要在Java中調用c++代碼,那就須要採用JNI技術,看起來好像高端大氣上檔次,其實程序就是函數調用,也就是輸入→處理→輸出,因此JNI實際上簡單抽象出來就這麼回事:

java輸入→Jni→c++輸入→c++處理(API實現)→c++輸出→Jni→java輸出  

在\proj.android\jni\hellocpp文件夾下能夠找到main.cpp,這就是cocos2d-x的入口:

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);  
  
        AppDelegate *pAppDelegate = new AppDelegate();  
        CCApplication::sharedApplication()->run();  
    }  
    else  
    {  
        ccGLInvalidateStateCache();  
        CCShaderCache::sharedShaderCache()->reloadDefaultShaders();  
        ccDrawInit();  
        CCTextureCache::reloadAllTextures();  
        CCNotificationCenter::sharedNotificationCenter()->postNotification(EVENT_COME_TO_FOREGROUND, NULL);  
        CCDirector::sharedDirector()->setGLDefaultValues();   
    }  
}  

裏面包含了2個函數,JNI_OnLoad和Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit。咱們看一下功能而先無論它在哪裏被調用。

(1)JNI_OnLoad,這個函數主要是用來告訴Android VM當前使用的是什麼版本是Jni,若是不提供此函數,則默認使用Jni1.1版本。

(2)Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit,這個函數很明顯就是運行一個cocos2d-x的應用實例了,這和Win32是同樣的,固然它多了一個openGlView的檢測。一旦調用了它那麼cocos2d-x遊戲啓動。

接下來再看看它們是在哪裏被調用的。

在Android.mk文件內容以下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := hellocpp_shared

LOCAL_MODULE_FILENAME := libhellocpp

LOCAL_SRC_FILES := hellocpp/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)

3.2 JNI_OnLoad的調用

在proj.android\src\org\cocos2dx\hellocpp目錄下,能夠看到Android的入口Activity,也就是HelloCpp,它繼承自Cocos2dxActivity。

public class HelloCpp extends Cocos2dxActivity{  
    protected void onCreate(Bundle savedInstanceState){  
        super.onCreate(savedInstanceState);  
    }  
      
    static {  
         System.loadLibrary("hellocpp");  
    }  
} 

很簡單的代碼,由於功能都被封裝到Cocos2dxActivity中了,因此OnCreate中調用了父類的OnCreate就把功能都實現了,而system.LoadLibrary就是載入編譯出來的.so文件,此時就會執行JNI_OnLoad

3.3 Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit的調用

那最重要的Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit是在哪調用呢?這就比較麻煩了,先大體瞭解一下Cocos2dxActivity作了一些什麼事。

直接進入Cocos2dxActivity的OnCreate函數,它調用了一個init初始化函數:

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佈局,這一個跟GLSurfaceView兼容的edittext  
        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);  
  
        // 添加到framelaout  
     framelayout.addView(edittext);

    
// 建立Cocos2dxGLSurfaceView this.mGLSurfaceView = this.onCreateView(); // 添加到framelaout framelayout.addView(this.mGLSurfaceView);

     // Switch to supported OpenGL (ARGB888) mode on emulator if (isAndroidEmulator()) this.mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0); //設置Cocos2dxRenderer和Cocos2dxEditText this.mGLSurfaceView.setCocos2dxRenderer(new Cocos2dxRenderer()); this.mGLSurfaceView.setCocos2dxEditText(edittext); // 設置framelayout做爲內容視圖    setContentView(framelayout); }

在這裏Cocos2dxActivity作的就是建立Cocos2dxGLSurfaceView,並設置了Cocos2dxRenderer和Cocos2dxEditText,而後添加到FramLayout。具體的各部分實現這裏就不貼代碼了,畫了個圖:

2、 AppDelegate 析造函數沒有被調用

  怎麼樣使用 Cocos2d-x 快速開發遊戲,方法很簡單,你能夠看看其自帶的例程,或者從網上搜索教程,運行起第一個 Scene HelloWorldScene,而後在HelloWorldScene 裏面寫相關邏輯代碼,添加咱們的層、精靈等 ~ 咱們並不必定須要知道 Cocos2d-x 是如何運行或者在各類平臺之上運行,也不用知道 Cocos2d-x 的遊戲是如何運行起來的,它又是如何渲染界面的 ~~~

  咱們只用知道 Cocos2d-x 的程序是由 AppDelegate 的方法 applicationDidFinishLaunching 開始,在其中作些必要的初始化,並建立運行第一個 CCScene 便可,正如咱們第一次使用各類編程語言寫 Hello World! 的程序同樣,如 Python 打印:

print(‘Hello World!’)

咱們能夠不用關心其是怎麼實現的,咱們只要知道這樣就能打印一句話就夠了,這就是封裝所帶來的好處 。Cocos2d-x 自帶的例程已經足夠豐富,可是有些問題並非看看例子,調用其方法就能明白的事情,在這裏遇到了以下問題:

// AppDelegate.cpp 文件

AppDelegate::AppDelegate()
{
    CCLog("AppDelegate()");     // AppDelegate 構造函數打印
}

AppDelegate::~AppDelegate()
{
    CCLog("AppDelegate().~()");     // AppDelegate 析構函數打印
}

// 程序入口
bool AppDelegate::applicationDidFinishLaunching()
{
    // initialize director
    CCDirector *pDirector = CCDirector::sharedDirector();
    pDirector->setOpenGLView(CCEGLView::sharedOpenGLView());

    // 初始化,資源適配,屏幕適配,運行第一個場景等代碼
    ...
    ...
    ...

    return true;
}

void AppDelegate::applicationDidEnterBackground()
{
    CCDirector::sharedDirector()->pause();
}

void AppDelegate::applicationWillEnterForeground()
{
    CCDirector::sharedDirector()->resume();
}

  此時我並不知道程序運行時,什麼時候調用 AppDelegate 的構造函數,析構函數和程序入口函數,咱們只要知道,程序在這裏調用了其構造函數,而後進入入口函數執行其過程,最後再調用其析構函數便可。然而事與願違,在實際執行的過程當中,發現程序只調用其構造函數和入口函數,而直到程序結束運行,都沒有調用其析構函數。要驗證此說法很簡單,只要如上在析構函數中調用打印日誌即可驗證。

  發生這樣的狀況,讓我在構造函數建立[資源],而且在析構函數中釋放[資源] 的想法不能完成!!! 咱們知道它是從哪裏開始運行,但殊不知道它在哪裏結束!疑問,惟有疑問!

兩個入口

程序入口的概念是相對的,AppDelegate 做爲跨平臺程序入口,在這之上作了另外一層的封裝,封裝了不一樣平臺的不一樣實現,好比咱們一般認爲一個程序是由 main 函數開始運行,那咱們就去找尋,咱們看到了在 proj.linux 目錄下存在 main.cpp 文件,這就是咱們要看的內容,以下:

#include "main.h"
#include "../Classes/AppDelegate.h"
#include "cocos2d.h"


#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string>

USING_NS_CC;

// 500 is enough?
#define MAXPATHLEN 500

int main(int argc, char **argv)
{
    // get application path
    int length;
    char fullpath[MAXPATHLEN];
    length = readlink("/proc/self/exe", fullpath, sizeof(fullpath));
    fullpath[length] = '\0';

    std::string resourcePath = fullpath;
    resourcePath = resourcePath.substr(0, resourcePath.find_last_of("/"));
    resourcePath += "/../../../Resources/";

    // create the application instance
    AppDelegate app;
    CCApplication::sharedApplication()->setResourceRootPath(resourcePath.c_str());
    CCEGLView* eglView = CCEGLView::sharedOpenGLView();
    eglView->setFrameSize(720, 480);
   // eglView->setFrameSize(480, 320);

    return CCApplication::sharedApplication()->run();
}

在這裏咱們看見了程序的真正入口,包含一個 main 函數,今後進入,執行 cocos2d-x 程序。咱們看到 main 就知道其是入口函數,那麼沒有 main 函數就沒有入口了嗎?顯然不是,以 Android 平臺啓動 cocos2d-x 程序爲例。咱們找到 Android 平臺與上面等價的入口點,proj.android/jni/hellocpp/main.cpp:

#include "cocos2d.h"
#include "AppDelegate.h"
#include "platform/android/jni/JniHelper.h"
#include <jni.h>
#include <android/log.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);

        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(); 
    }
}

  咱們並無看到所謂的 main 函數,這是因爲不一樣的平臺封裝因此有着不一樣的實現,在 Android 平臺,默認是使用 Java 開發,可使用 Java 經過 Jni 調用 C++ 程序,而這裏也正式如此。咱們暫且只需知道,由 Android 啓動一個應用,經過各類峯迴路轉,最終執行到了 Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit 函數,由此,便開始了咱們 cocos2d-x Android 平臺的程序入口處。對於跨平臺的 cocos2d-x 來講,除非必要,不然可沒必要深究其理,好比想要使用 Android 平臺固有的特性等,那就須要更多的瞭解 Jni 使用方法,以及 Android 操做系統的更多細節。

因此說程序的入口是相對的,正如博文開始的 print(‘Hello World’) 同樣,不一樣的語言,不一樣平臺總有着不一樣的實現。

這裏咱們參考了兩個不一樣平臺的實現, Linux 和 Android 平臺 cocos2d-x 程序入口 main.cpp的實現,那麼其它平臺呢,如 iOS ,Win32 等 ~~~ 異曲同工,其它平臺程序的入口必然包含着其它平臺的不一樣封裝實現 ,知道有等價在此兩平臺的程序入口便可。而經過這兩個平臺也足夠解決咱們的疑問,程序的開始與結束 ~

問題的推測

咱們就從 Linux 和 Android 這兩個平臺的入口函數開始,看看 cocos2d-x 的執行流程到底爲什麼?何以發生只執行了 AppDelegate 的構造函數,而沒有析構函數。在查看 cocos2d-x 程序代碼時,咱們只關注必要的內容,何謂必要,只要能解決咱們此時的疑問便可!在兩個平臺的入口函數,咱們看到以下內容:

// Linux 平臺關鍵代碼
int main(int argc, char **argv)
{
    // 初始化等內容
    ...
    ...
    // 建立 app 變量
 AppDelegate app;  
    ...
    ...
    // 執行 核心 run() 方法
    return CCApplication::sharedApplication()->run();
}

// Android 平臺關鍵代碼
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);

        // 建立 AppDelegate 對象
        AppDelegate *pAppDelegate = new AppDelegate();
        // 執行 核心 run() 方法
        CCApplication::sharedApplication()->run();
    }
    else
    {
        ...
        ...
    }
}

  不一樣的平臺,卻實現相同操做,建立 AppDelegate 變量和執行 run 方法。下面將以 Linux 平臺爲例,來講明程序是如何開始與結束的,由於 Linux 的內部實現要簡單一點,而 Android 平臺的實現稍顯麻煩,Jni 之間來回調用,對咱們理解 cocos2d-x 的執行流程反而有所 阻礙,何況 cocos2d-x 自己就是跨平臺的程序。沒必要拘泥於特有平臺的專有特性。

程序的流程 (這裏以 Linux 的實現爲主,其它平臺舉一反三便可)

AppDelegate 與 CCApplication關係

  咱們從 main.cpp 中 CCApplication::sharedApplication()->run(); 這一句看起,這一句標誌着, cocos2d-x 程序正式開始運行,一點點開始分析,咱們定位到 sharedApplication() 方法的實現,這裏只給出必要的代碼,具體看一本身直接看源碼:

// [cocos2dx-path]/cocos2dx/platform/linux/CCApplication.cpp
...
// 此變量爲定義了一個 CCApplication 的靜態變量,也及時本身類型自己,實現單例模式
CCApplication * CCApplication::sm_pSharedApplication = 0;
...
// 構造函數,將所建立的 對象直接付給其靜態變量
CCApplication::CCApplication()
{
    // 斷言在此決定着此構造函數只能運行一次
    CC_ASSERT(! sm_pSharedApplication);
    sm_pSharedApplication = this;
}

CCApplication::~CCApplication()
{
    CC_ASSERT(this == sm_pSharedApplication);
    sm_pSharedApplication = NULL;
    m_nAnimationInterval = 1.0f/60.0f*1000.0f;
}

// run 方法,整個 cocos2d-x 的主循環在這裏開始
int CCApplication::run()
{
    // 首次啓動調用初始化函數
    if (! applicationDidFinishLaunching())
    {
        return 0;
    }

    // 遊戲主循環,這裏 Linux 的實現相比其它平臺的實現,簡單明瞭
    for (;;) {
        long iLastTime = getCurrentMillSecond();
        // 在循環以內調用每一幀的邏輯,組織而且控制 cocos2d-x 之中各個組件
        CCDirector::sharedDirector()->mainLoop();
        long iCurTime = getCurrentMillSecond();
        // 這裏的幾個時間變量,能夠控制每一幀所運行的 最小 時間,從而控制遊戲的幀率
        if (iCurTime-iLastTime<m_nAnimationInterval){
            usleep((m_nAnimationInterval - iCurTime+iLastTime)*1000);
        }

    }
    // 注意,這裏的 for 循環,並無退出循環條件,這也決定着 run() 方法永遠也不會返回
    return -1;
}

// 方法直接返回了靜態對象,而且作了斷言,也既是在調用此方法以前,
// 必須事先建立一個 CCApplication 的對象,以保證其靜態變量可以初始化,不然返回空
CCApplication* CCApplication::sharedApplication()
{
    CC_ASSERT(sm_pSharedApplication);
    return sm_pSharedApplication;
}

  從上面的內容能夠看出,從 sharedApplication() 方法,到 run() 方法,在這以前,咱們須要調用到它(CCApplication)的構造函數,不然不能運行,這就是爲何在 CCApplication::sharedApplication()->run(); 以前,咱們首先使用了 AppDelegate app; 建立 AppDelegate 變量的緣由! 嗯 !! AppDelegate 和 CCAppliation 是什麼關係? 由 AppDelegate 的定義咱們能夠知道,它是 CCApplication 的子類,在建立子類對象的時候,調用其構造函數的同時,父類構造函數也會執行,而後就將 AppDelegate 的對象賦給了 CCApplication 的靜態變量,而在 AppDelegate 之中咱們實現了 applicationDidFinishLaunching 方法,因此在 CCApplication 中 run 方法的開始處調用的就是 AppDelegate 之中的實現。而咱們在此方法中咱們初始化了一些變量,建立了第一個 CCScene 場景等,以後的控制權,便全權交給了 CCDirector::sharedDirector()->mainLoop(); 方法了。

(這裏的實現機制,不作詳細說明,簡單說來:applicationDidFinishLaunching 是由 CCApplicationProtocol 定義,CCApplication 繼承, AppDelegate 實現的 ~)

比較重要的所在,for 循環並無循環退出條件,因此 run 方法永遠不會返回。那麼是怎麼結束的呢?

從 CCApplication 到 CCDirector

cocos2d-x 程序已經運行起來了,咱們繼續下一步,mainLoop 函數:

// [cocos2dx-path]/cocos2dx/CCDirector.cpp
...
// 定義靜態變量,實現單例模式
static CCDisplayLinkDirector *s_SharedDirector = NULL;
...
// 返回 CCDirector 實例
CCDirector* CCDirector::sharedDirector(void)
{
    // 判斷靜態變量,以保證只有一個實例
    if (!s_SharedDirector)
    {
        s_SharedDirector = new CCDisplayLinkDirector();
        s_SharedDirector->init();
    }
    // CCDisplayLinkDirector 爲 CCDirector 的子類,這裏返回了其子類
    return s_SharedDirector;
}

// mainLoop 方法的具體實現
void CCDisplayLinkDirector::mainLoop(void)
{
    // 此變量是咱們須要關注,而且跟蹤的,由於它決定着程序的結束時機
   if (m_bPurgeDirecotorInNextLoop)
   {
       m_bPurgeDirecotorInNextLoop = false;
       // 運行到此,說明程序的運行,已經沒有邏輯代碼須要處理了
       purgeDirector();
   }
   else if (! m_bInvalid)
    {
        // 屏幕繪製,並作一些相應的邏輯處理,其內部處理,這裏暫且不作過多探討
        drawScene();

        // 這裏實現了 cocos2d-x CCObject 對象的內存管理機制,對此有興趣者,能夠深刻下去
        CCPoolManager::sharedPoolManager()->pop();
    }
}

// 彈出場景 CCScene
void CCDirector::popScene(void)
{
    CCAssert(m_pRunningScene != NULL, "running scene should not null");

    m_pobScenesStack->removeLastObject();
    unsigned int c = m_pobScenesStack->count();

    if (c == 0)
    {
        // 若是沒有場景,調用 end() 方法
        end();
    }
    else
    {
        m_bSendCleanupToScene = true;
        m_pNextScene = (CCScene*)m_pobScenesStack->objectAtIndex(c - 1);
    }
}

void CCDirector::end()
{
    // 在 end 方法中,設置了變量爲 true,這所致的結果,在 mainLoop 函數中,達成了運行 purgeDirector 方法的條件
    m_bPurgeDirecotorInNextLoop = true;
}

// 此方法作些收尾清理的工做
void CCDirector::purgeDirector()
{
    ...
    if (m_pRunningScene)
    {
        m_pRunningScene->onExit();
        m_pRunningScene->cleanup();
        m_pRunningScene->release();
    }
    // 作一些清理的工做
   ...
    // OpenGL view

    // ###此句代碼關鍵###
    m_pobOpenGLView->end();
    m_pobOpenGLView = NULL;

    // delete CCDirector
    release();
}

// 設置 openglview
void CCDirector::setOpenGLView(CCEGLView *pobOpenGLView)
{
    CCAssert(pobOpenGLView, "opengl view should not be null");

    if (m_pobOpenGLView != pobOpenGLView)
    {
        // EAGLView is not a CCObject
        delete m_pobOpenGLView; // [openGLView_ release]
        // 爲當前 CCDirector m_pobOpenGLView  賦值
        m_pobOpenGLView = pobOpenGLView;

        // set size
        m_obWinSizeInPoints = m_pobOpenGLView->getDesignResolutionSize();

        createStatsLabel();

        if (m_pobOpenGLView)
        {
            setGLDefaultValues();
        }

        CHECK_GL_ERROR_DEBUG();

        m_pobOpenGLView->setTouchDelegate(m_pTouchDispatcher);
        m_pTouchDispatcher->setDispatchEvents(true);
    }
}

  遊戲的運行以場景爲基礎,每時每刻都有一個場景正在運行,其內部有一個場景棧,遵循後進後出的原則,當咱們顯示的調用 end() 方法,或者彈出當前場景之時,其自動判斷,若是沒有場景存在,也會觸發 end() 方法,以說明場景運行的結束,而遊戲若是沒有場景,就像演出沒有了舞臺,程序進入最後收尾的工做,經過修改變量 m_bPurgeDirecotorInNextLoop 促使在程序 mainLoop 方法以內調用 purgeDirector 方法。

CCEGLView 的收尾工做

  purgeDirector 方法以內,經過猜想與排查,最終定位到 m_pobOpenGLView->end(); 方法,在這裏結束了 cocos2d-x 遊戲進程。而 m_pobOpenGLView 什麼時候賦值,它的具體實現又在哪裏呢?咱們能夠在 AppDelegate 的 applicationDidFinishLaunching 方法中找到以下代碼:

// AppDelegate.cpp

CCDirector *pDirector = CCDirector::sharedDirector();
pDirector->setOpenGLView(CCEGLView::sharedOpenGLView());

咱們終於走到最後一步,看 CCEGLView 是若是負責收尾工做的:

// [cocos2dx-path]/cocos2dx/platform/linux.CCEGLView.cpp

...
CCEGLView* CCEGLView::sharedOpenGLView()
{
    static CCEGLView* s_pEglView = NULL;
    if (s_pEglView == NULL)
    {
        s_pEglView = new CCEGLView();
    }
    return s_pEglView;
}
...

// openglview 結束方法
void CCEGLView::end()
{
    /* Exits from GLFW */
    glfwTerminate();
    delete this;
    exit(0);
}

end() 方法很簡單,只須要看到最後一句 exit(0); 就明白了。

cocos2d-x 程序的結束流程

  程序運行時期,由 mainLoop 方法維持運行着遊戲以內的各個邏輯,當在彈出最後一個場景,或者直接調用 CCDirector::end(); 方法後,觸發遊戲的清理工做,執行 purgeDirector 方法,從而結束了 CCEGLView(不一樣平臺不一樣封裝,PC使用OpenGl封裝,移動終端封裝的爲 OpenGl ES) 的運行,調用其 end() 方法,從而直接執行 exit(0); 退出程序進程,從而結束了整個程序的運行。(Android 平臺的 end() 方法內部經過Jni 方法 terminateProcessJNI(); 調用 Java 實現的功能,其功能同樣,直接結束了當前運行的進程)

  從程序的 main 方法開始,再建立 AppDelegate 等對象,運行過程當中倒是經過 exit(0); 來退出程序。因此咱們看到了 AppDelegate 構造函數被調用,而其析構函數沒有被調用的現象。exit(0); 的執行,意味着咱們的程序徹底結束,固然咱們的進程資源也會被操做系統釋放。可是注意,這裏的"在構造函數建立[資源],而且在析構函數中釋放[資源]"並不是絕對意義上的程序進程資源,在程序退出的時候,程序所使用的資源固然會被系統回收. 可是若是我在構造函數調用網絡接口初始化,析構再調用一次通知,所影響到的相似這種 非本地資源 邏輯上的處理,便會留下隱患。而經過理解 cocos2d-x 的運行機制,能夠減小這種可能存在的隱患。

cocos2d-x 的總體把握

  在本文經過解決一個小疑問,而去分析 cocos2d-x 遊戲的運行流程,固然其中不少細緻末葉咱們並無深刻下去。不去解決這個疑問也能夠,知道沒有調用析構函數,那我就不調用即是 (這也是簡單的解決方法,也不用以爲這不可行 )。這裏只是藉着這個疑問,對 cocos2d-x 的流程稍做探尋而已。也沒有貼一堆 cocos2d-x 源碼去分析,其思路也有跡可循。

  什麼是 cocos2d-x ,它是 cocos2d 一個 C++ 的實現,除 C++ 以外,有 python ,Objective-C 等其它語言的實現,那該怎麼去理解 cocos2d ,能夠這麼理解,cocos2d 是一個編寫 2D 遊戲的通用形框架,這種框架提供了一個通用模型,而這種模型或者說架構是 無關語言與平臺 的,說 cocos2d-x 使用 C++ 編寫,其跨平臺能力很強,但它能跑在瀏覽器上麼?cocos2d 仍是有着 html5 的實現,固然平臺決定着語言的選擇,而 cocos2d 可以適應這麼多不一樣的語言和平臺,其良好的設計,清晰的結構功不可沒。 而對不一樣語言,對相同功能有着不一樣的封裝,正如在本文問題中,在不一樣平臺(Linux 和 Android),對相同功能有着不一樣的封裝殊途同歸。那麼封裝到最後,咱們對 cocos2d 的理解就只剩下了,咱們要寫遊戲,那麼須要導演,場景、層、精靈、動做等 ~~ 組織好這些之間的關係便可 ~

【轉自】http://game.dapps.net/gamedev/game-engine/9515.html

相關文章
相關標籤/搜索