亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Android > 正文

Cocos2d-x 3.0多線程異步加載資源實例

2020-04-11 11:50:54
字體:
來源:轉載
供稿:網友

Cocos2d-x從2.x版本到上周剛剛才發布的Cocos2d-x 3.0 Final版,其引擎驅動核心依舊是一個單線程的“死循環”,一旦某一幀遇到了“大活兒”,比如Size很大的紋理資源加載或網絡IO或大量計算,畫面將 不可避免出現卡頓以及響應遲緩的現象。從古老的Win32 GUI編程那時起,Guru們就告訴我們:別阻塞主線程(UI線程),讓Worker線程去做那些“大活兒”吧。

手機游戲,即便是休閑類的小游戲,往往也涉及大量紋理資源、音視頻資源、文件讀寫以及網絡通信,處理的稍有不甚就會出現畫面卡頓,交互不暢的情況。雖然引擎在某些方面提供了一些支持,但有些時候還是自己祭出Worker線程這個法寶比較靈活,下面就以Cocos2d-x 3.0 Final版游戲初始化為例(針對Android平臺),說說如何進行多線程資源加載。

我們經??吹揭恍┦謾C游戲,啟動之后首先會顯示一個帶有公司Logo的閃屏畫面(Flash Screen),然后才會進入一個游戲Welcome場景,點擊“開始”才正式進入游戲主場景。而這里Flash Screen的展示環節往往在后臺還會做另外一件事,那就是加載游戲的圖片資源,音樂音效資源以及配置數據讀取,這算是一個“障眼法”吧,目的就是提高用 戶體驗,這樣后續場景渲染以及場景切換直接使用已經cache到內存中的數據即可,無需再行加載。


一、為游戲添加FlashScene

在游戲App初始化時,我們首先創建FlashScene,讓游戲盡快顯示FlashScene畫面:

復制代碼 代碼如下:

// AppDelegate.cpp
bool AppDelegate::applicationDidFinishLaunching() {
    … …
    FlashScene* scene = FlashScene::create();
    pDirector->runWithScene(scene);

    return true;
}

在FlashScene init時,我們創建一個Resource Load Thread,我們用一個ResourceLoadIndicator作為渲染線程與Worker線程之間交互的媒介。

復制代碼 代碼如下:

//FlashScene.h

struct ResourceLoadIndicator {
    pthread_mutex_t mutex;
    bool load_done;
    void *context;
};

class FlashScene : public Scene
{
public:
    FlashScene(void);
    ~FlashScene(void);

    virtual bool init();

    CREATE_FUNC(FlashScene);
    bool getResourceLoadIndicator();
    void setResourceLoadIndicator(bool flag);

private:
     void updateScene(float dt);

private:
     ResourceLoadIndicator rli;
};

// FlashScene.cpp
bool FlashScene::init()
{
    bool bRet = false;
    do {
        CC_BREAK_IF(!CCScene::init());
        Size winSize = Director::getInstance()->getWinSize();

        //FlashScene自己的資源只能同步加載了
        Sprite *bg = Sprite::create("FlashSceenBg.png");
        CC_BREAK_IF(!bg);
        bg->setPosition(ccp(winSize.width/2, winSize.height/2));
        this->addChild(bg, 0);

        this->schedule(schedule_selector(FlashScene::updateScene)
                       , 0.01f);

        //start the resource loading thread
        rli.load_done = false;
        rli.context = (void*)this;
        pthread_mutex_init(&rli.mutex, NULL);
        pthread_attr_t attr;
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
        pthread_t thread;
        pthread_create(&thread, &attr,
                    resource_load_thread_entry, &rli);

        bRet=true;
    } while(0);

    return bRet;
}

static void* resource_load_thread_entry(void* param)
{
    AppDelegate *app = (AppDelegate*)Application::getInstance();
    ResourceLoadIndicator *rli = (ResourceLoadIndicator*)param;
    FlashScene *scene = (FlashScene*)rli->context;

    //load music effect resource
    … …

    //init from config files
    … …

    //load images data in worker thread
    SpriteFrameCache::getInstance()->addSpriteFramesWithFile(
                                       "All-Sprites.plist");
    … …

    //set loading done
    scene->setResourceLoadIndicator(true);
    return NULL;
}

bool FlashScene::getResourceLoadIndicator()
{
    bool flag;
    pthread_mutex_lock(&rli.mutex);
    flag = rli.load_done;
    pthread_mutex_unlock(&rli.mutex);
    return flag;
}

void FlashScene::setResourceLoadIndicator(bool flag)
{
    pthread_mutex_lock(&rli.mutex);
    rli.load_done = flag;
    pthread_mutex_unlock(&rli.mutex);
    return;
}

我們在定時器回調函數中對indicator標志位進行檢查,當發現加載ok后,切換到接下來的游戲開始場景:

復制代碼 代碼如下:

void FlashScene::updateScene(float dt)
{
    if (getResourceLoadIndicator()) {
        Director::getInstance()->replaceScene(
                              WelcomeScene::create());
    }
}

到此,FlashScene的初始設計和實現完成了。Run一下試試吧。

二、解決崩潰問題

在GenyMotion的4.4.2模擬器上,游戲運行的結果并沒有如我期望,FlashScreen顯現后游戲就異常崩潰退出了。

通過monitor分析游戲的運行日志,我們看到了如下一些異常日志:

復制代碼 代碼如下:

threadid=24: thread exiting, not yet detached (count=0)
threadid=24: thread exiting, not yet detached (count=1)
threadid=24: native thread exited without detaching


很是奇怪啊,我們在創建線程時,明明設置了 PTHREAD_CREATE_DETACHED屬性了啊:

復制代碼 代碼如下:
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

怎么還會出現這個問題,而且居然有三條日志。翻看了一下引擎內核的代碼TextureCache::addImageAsync,在線程創建以及線程主函數中也沒有發現什么特別的設置。為何內核可以創建線程,我自己創建就會崩潰呢。Debug多個來回,問題似乎聚焦在resource_load_thread_entry中執行的任務。在我的代碼里,我利用SimpleAudioEngine加載了音效資源、利用UserDefault讀取了一些持久化的數據,把這兩個任務去掉,游戲就會進入到下一個環節而不會崩潰。

SimpleAudioEngine和UserDefault能有什么共同點呢?Jni調用。沒錯,這兩個接口底層要適配多個平臺,而對于Android 平臺,他們都用到了Jni提供的接口去調用Java中的方法。而Jni對多線程是有約束的。Android開發者官網上有這么一段話:

復制代碼 代碼如下:

All threads are Linux threads, scheduled by the kernel. They're usually started from managed code (using Thread.start), but they can also be created elsewhere and then attached to the JavaVM. For example, a thread started with pthread_create can be attached with the JNI AttachCurrentThread or AttachCurrentThreadAsDaemon functions. Until a thread is attached, it has no JNIEnv, and cannot make JNI calls.

由此看來pthread_create創建的新線程默認情況下是不能進行Jni接口調用的,除非Attach到Vm,獲得一個JniEnv對象,并且在線 程exit前要Detach Vm。好,我們來嘗試一下,Cocos2d-x引擎提供了一些JniHelper方法,可以方便進行Jni相關操作。

復制代碼 代碼如下:

#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include "platform/android/jni/JniHelper.h"
#include <jni.h>
#endif

static void* resource_load_thread_entry(void* param)
{
    … …

    JavaVM *vm;
    JNIEnv *env;
    vm = JniHelper::getJavaVM();

    JavaVMAttachArgs thread_args;

    thread_args.name = "Resource Load";
    thread_args.version = JNI_VERSION_1_4;
    thread_args.group = NULL;

    vm->AttachCurrentThread(&env, &thread_args);
    … …
    //Your Jni Calls
    … …

    vm->DetachCurrentThread();
    … …
    return NULL;
}

關于什么是JavaVM,什么是JniEnv,Android Developer官方文檔中是這樣描述的:

The JavaVM provides the "invocation interface" functions, which allow you to create and destroy a JavaVM. In theory you can have multiple JavaVMs per process, but Android only allows one.
The JNIEnv provides most of the JNI functions. Your native functions all receive a JNIEnv as the first argument.
The JNIEnv is used for thread-local storage. For this reason, you cannot share a JNIEnv between threads.

三、解決黑屏問題

上面的代碼成功解決了線程崩潰的問題,但問題還沒完,因為接下來我們又遇到了“黑屏”事件。所謂的“黑屏”,其實并不是全黑。但進入游戲 WelcomScene時,只有Scene中的LabelTTF實例能顯示出來,其余Sprite都無法顯示。顯然肯定與我們在Worker線程加載紋理 資源有關了:

復制代碼 代碼如下:
SpriteFrameCache::getInstance()->addSpriteFramesWithFile("All-Sprites.plist");

我們通過碎圖壓縮到一張大紋理的方式建立SpriteFrame,這是Cocos2d-x推薦的優化手段。但要想找到這個問題的根源,還得看monitor日志。我們的確發現了一些異常日志:

復制代碼 代碼如下:
libEGL: call to OpenGL ES API with no current context (logged once per thread)

通過Google得知,只有Renderer Thread才能進行egl調用,因為egl的context是在Renderer Thread創建的,Worker Thread并沒有EGL的context,在進行egl操作時,無法找到context,因此操作都是失敗的,紋理也就無法顯示出來。要解決這個問題就 得查看一下TextureCache::addImageAsync是如何做的了。

TextureCache::addImageAsync只是在worker線程進行了image數據的加載,而紋理對象Texture2D instance則是在addImageAsyncCallBack中創建的。也就是說紋理還是在Renderer線程中創建的,因此不會出現我們上面的 “黑屏”問題。模仿addImageAsync,我們來修改一下代碼:

復制代碼 代碼如下:

static void* resource_load_thread_entry(void* param)
{
    … …
    allSpritesImage = new Image();
    allSpritesImage->initWithImageFile("All-Sprites.png");
    … …
}

void FlashScene::updateScene(float dt)
{
    if (getResourceLoadIndicator()) {
        // construct texture with preloaded images
        Texture2D *allSpritesTexture = TextureCache::getInstance()->
                           addImage(allSpritesImage, "All-Sprites.png");
        allSpritesImage->release();
        SpriteFrameCache::getInstance()->addSpriteFramesWithFile(
                           "All-Sprites.plist", allSpritesTexture);

        Director::getInstance()->replaceScene(WelcomeScene::create());
    }
}

完成這一修改后,游戲畫面就變得一切正常了,多線程資源加載機制正式生效。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产欧美va欧美va香蕉在线| 欧美国产日韩一区| 亚洲精品国产精品乱码不99按摩| 992tv成人免费视频| 日韩精品在线免费播放| 日韩成人中文字幕在线观看| 2019亚洲男人天堂| 日本乱人伦a精品| 欧美国产第二页| 亚洲图中文字幕| 国产91在线高潮白浆在线观看| 亚洲专区在线视频| 欧美区二区三区| 欧美丝袜第一区| 欧美大片大片在线播放| 久久久久久尹人网香蕉| 亚洲欧美在线免费观看| 欧美制服第一页| 亚洲男人的天堂在线| 国产一区二区黑人欧美xxxx| 成人国产精品色哟哟| 91九色在线视频| 最近2019年日本中文免费字幕| 全亚洲最色的网站在线观看| 久久福利网址导航| 亚洲天堂网在线观看| 亚洲国产天堂久久国产91| 亚洲第一精品电影| 91极品视频在线| 久久这里有精品| 色综合久久88色综合天天看泰| 国模吧一区二区三区| 91啪国产在线| 欧美电影免费观看电视剧大全| 日韩在线精品视频| 欧美电影在线观看完整版| 日本sm极度另类视频| 欧美电影免费观看高清完整| 欧洲午夜精品久久久| 欧美激情精品久久久久久蜜臀| 国产一区二区三区直播精品电影| 色琪琪综合男人的天堂aⅴ视频| 九九热精品视频在线播放| 96sao精品视频在线观看| 伊人久久综合97精品| 精品人伦一区二区三区蜜桃免费| 97**国产露脸精品国产| 亚洲香蕉伊综合在人在线视看| 亚洲欧洲中文天堂| 亚洲最新av网址| 日韩av网站在线| 2019中文在线观看| 亚洲欧洲日韩国产| 精品国产区一区二区三区在线观看| 亚洲国内高清视频| 国模极品一区二区三区| 日韩av理论片| 日本一区二区三区四区视频| 性欧美亚洲xxxx乳在线观看| 2018中文字幕一区二区三区| 全色精品综合影院| 亚洲香蕉成视频在线观看| 欧美高清不卡在线| 精品久久久中文| 精品中文字幕在线2019| 亚洲缚视频在线观看| 亚洲国产精品成人av| 亚洲福利精品在线| 最新国产成人av网站网址麻豆| 国产精品日日做人人爱| 国产福利精品av综合导导航| 国产精品99久久久久久白浆小说| 日韩的一区二区| 久久国产精品偷| 亚洲欧美一区二区三区久久| 国产自产女人91一区在线观看| 欧美日韩国产精品一区| 亚洲色图欧美制服丝袜另类第一页| 精品视频在线播放| 欧美在线一区二区视频| 国产精品国产福利国产秒拍| 国产国语videosex另类| 夜夜嗨av一区二区三区四区| 欧美精品18videos性欧| 日本精品久久久久久久| 欧美激情视频一区二区| 亚洲精品自在久久| 亚洲va欧美va国产综合久久| 亚洲精品白浆高清久久久久久| 欧美在线性视频| 日韩av在线免费播放| 色www亚洲国产张柏芝| 亚洲国产精久久久久久| 日本精品视频在线观看| 亚洲免费中文字幕| 国产亚洲欧美另类中文| 久久久人成影片一区二区三区观看| 91精品久久久久久久久久久久久久| 国产精品91免费在线| 亚洲已满18点击进入在线看片| 国产精品极品尤物在线观看| 久久久久久成人| 日韩国产在线播放| 中日韩午夜理伦电影免费| 日韩三级影视基地| 欧美精品久久久久a| 日韩精品一二三四区| 久久精品电影一区二区| 亚洲已满18点击进入在线看片| 久久亚洲春色中文字幕| 亚洲毛片在线看| 黑人极品videos精品欧美裸| 九九九久久国产免费| 国产精品99久久久久久白浆小说| 午夜精品福利视频| 精品久久久一区| 欧美在线一级va免费观看| 亚洲aa中文字幕| 国产成人在线播放| 91探花福利精品国产自产在线| 午夜精品久久久99热福利| 国产成人精品综合久久久| www.xxxx精品| 91国产视频在线播放| 欧美在线视频在线播放完整版免费观看| 亚洲精品国产品国语在线| 少妇高潮久久久久久潘金莲| 欧洲中文字幕国产精品| 欧美激情精品久久久久久大尺度| 国产欧美日韩免费| 中文字幕自拍vr一区二区三区| 中文字幕日韩在线观看| 亚洲欧美日韩视频一区| 久久99久国产精品黄毛片入口| 亚洲黄色成人网| 在线电影欧美日韩一区二区私密| 欧美在线精品免播放器视频| 亚洲日本成人女熟在线观看| 日韩禁在线播放| 欧美日韩中文在线观看| 久久久午夜视频| 亚洲电影免费观看高清| 欧美另类老肥妇| 午夜精品一区二区三区在线播放| 国产精品香蕉国产| 欧洲亚洲妇女av| www.亚洲人.com| 日韩中文字幕av| 久久夜色撩人精品| 久久久91精品国产一区不卡| 91深夜福利视频| 欧美性xxxx18| 久久久精品国产亚洲| 亚洲激情视频网站| 国产精品高潮呻吟久久av野狼| 欧美高清视频在线播放| 国产97在线|亚洲| 成人欧美一区二区三区黑人孕妇| 亚洲视频自拍偷拍| 久久精品电影网| 热久久美女精品天天吊色| 97在线视频一区| 亚洲国产美女久久久久| 亚洲国产成人爱av在线播放|