解決cocos2d-x 安卓鎖屏再解鎖,OPenGL報錯;後臺轉前臺異常問題解決辦法
最近幾天解決遊戲中安卓遇到一個讓人蛋碎的問題,先具體描述一下問題:
遊戲在安卓手機上執行正常,按HOME鍵轉後臺以後 ,再點桌面圖示遊戲無法正常執行,顯示黑框;無法正常從後臺轉到前臺;鎖屏以後再解鎖,如果是在遊戲執行狀態時鎖屏再解鎖螢幕,無法執行。如果遊戲轉入後臺,鎖屏再解鎖,再點遊戲圖示遊戲執行正常;其實就是遊戲轉入後臺再回到前臺的問題,在網上找了很多相關文件,如果大家有仔細查閱的話應該都會有看到,這裡我進行一下彙總,希望可以幫到再次遇到類似問題的朋友。
方法一:修改三個檔案(這個方法網上有很多人,具體不知道原創是誰,我這裡只簡單介紹一下,因為這個方法沒有解決我的問題)
(1 ) cocos2dx/platform/CCPlatformMacros.h 將84行的#define CC_ENABLE_CACHE_TEXTURE_DATA 1 值改成0
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) || (CC_TARGET_PLATFORM == CC_PLATFORM_EMSCRIPTEN)
#define CC_ENABLE_CACHE_TEXTURE_DATA 1
#else
#define CC_ENABLE_CACHE_TEXTURE_DATA 0
#endif
( 2 )cocos2dx/platform/android/java/src/org/cocos2dx/lib/Cocos2dxGLSurfaceView.java
個性167行,onPause方法裡的super.onPause();把這一行註釋掉( 3 )android工程目錄下jin/hellocpp/main.cpp 找到Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit 這個方法,把方法裡else整個註釋掉;
這個方法其實是想在activity切入後臺的時候,不清除textureCache,這樣下次從後臺切回前臺的時候就不需要重新load資源。有興趣的朋友可以親自嘗試一下。
方法二:網上有一篇文章《Cocos2d-x解決Android後臺切換回遊戲無響應》以及續,這兩篇文章我都有看過,作者提出可以在後臺轉入前臺的時候,加一個Load資源介面,在這裡做重新載入資源,和上一方法是不同的(注:本方法和上一方法是獨立的,不要混淆)。作者考慮到一些低端機載入資源會出現的問題具體介紹大家去參考原作者文章:http://ywy.me/cocos2d-x-android-fix-force-close.html
在大家看過原作者的文章,明白作者的思路以後,我這裡結合一下我自己的解決方法,給大家演示一下:
說明:本人用的是cocos2d-JSB,所以有部分是用到JS指令碼的,大家參考思路即可
首先:在C++裡新增一個ReloadLayer類:這就直接上原始碼了
ReloadLayer.h
//#include "f:\work\herogame\jsbengine\engine\cocos2dx\layers_scenes_transitions_nodes\cclayer.h"
#include "cocos2d.h"
using namespace cocos2d;
#include "..\..\..\..\engine\cocos2dx\layers_scenes_transitions_nodes\cclayer.h"
class ReloadLayer : public CCLayerColor
{
public:
ReloadLayer(void);
~ReloadLayer(void);
virtual bool init();
virtual void updateReload(float dt);
NODE_FUNC(ReloadLayer); //這句只是新增一個node()方法
};
ReloadLayer.cpp
ReloadLayer::ReloadLayer(void)
{
}
ReloadLayer::~ReloadLayer(void)
{
}
bool ReloadLayer::init() {
CCTextureCache::initReload();
this->initWithColor(ccc4(0, 0, 0, 255));
this->schedule(schedule_selector(ReloadLayer::updateReload));
CCDirector::sharedDirector()->getRunningScene()->addChild(this, 10, 88);
return true;
}
void ReloadLayer::updateReload(float dt) {
if(!CCTextureCache::reloadOneByOne()) {
CCDirector::sharedDirector()->getRunningScene()->removeChildByTag(88, true);
}
}
接下來我們需要動一下底層的東西,開啟CCTextureCache類,新增程式碼
CCTextureCache.h 在CCTextureCache裡新增
static int m_curReloadIndex;
static int m_reloadCount;
static void initReload();
static bool reloadOneByOne();
在VolatileTexture裡新增:static void initReload();
static bool reloadOneByOne();
CCTextureCache.cpp裡新增:
int CCTextureCache::m_curReloadIndex;
int CCTextureCache::m_reloadCount;
void CCTextureCache::initReload()
{
#if CC_ENABLE_CACHE_TEXTURE_DATA
VolatileTexture::initReload();
#else
m_curReloadIndex = 0;
m_reloadCount = 0;
#endif
}
bool CCTextureCache::reloadOneByOne()
{
#if CC_ENABLE_CACHE_TEXTURE_DATA
return VolatileTexture::reloadOneByOne();
#else
return false;
#endif
}
void VolatileTexture::initReload() {
CCTextureCache::sharedTextureCache()->removeUnusedTextures();
CCTextureCache::m_curReloadIndex = 0;
CCTextureCache::m_reloadCount = textures.size();
isReloading = true;
}
bool VolatileTexture::reloadOneByOne() {
std::list<VolatileTexture *>::iterator iter = textures.begin();
CCTextureCache::m_reloadCount = textures.size();
// 主要就是修改這裡改為直接使用textures list載入
if(CCTextureCache::m_curReloadIndex < textures.size()) {
isReloading = true;
// 略過已經載入過的
for(int j = 0; j < CCTextureCache::m_curReloadIndex; j++) {
iter++;
}
VolatileTexture* vt = *iter;
switch (vt->m_eCashedImageType)
{
case kImageFile:
{
std::string lowerCase(vt->m_strFileName.c_str());
for (unsigned int i = 0; i < lowerCase.length(); ++i)
{
lowerCase[i] = tolower(lowerCase[i]);
}
if (std::string::npos != lowerCase.find(".pvr"))
{
CCTexture2DPixelFormat oldPixelFormat = CCTexture2D::defaultAlphaPixelFormat();
CCTexture2D::setDefaultAlphaPixelFormat(vt->m_PixelFormat);
vt->texture->initWithPVRFile(vt->m_strFileName.c_str());
CCTexture2D::setDefaultAlphaPixelFormat(oldPixelFormat);
}
else
{
CCImage* pImage = new CCImage();
unsigned long nSize = 0;
unsigned char* pBuffer = CCFileUtils::sharedFileUtils()->getFileData(vt->m_strFileName.c_str(), "rb", &nSize);
if (pImage && pImage->initWithImageData((void*)pBuffer, nSize, vt->m_FmtImage))
{
CCTexture2DPixelFormat oldPixelFormat = CCTexture2D::defaultAlphaPixelFormat();
CCTexture2D::setDefaultAlphaPixelFormat(vt->m_PixelFormat);
vt->texture->initWithImage(pImage);
CCTexture2D::setDefaultAlphaPixelFormat(oldPixelFormat);
}
CC_SAFE_DELETE_ARRAY(pBuffer);
CC_SAFE_RELEASE(pImage);
}
}
break;
case kImageData:
{
vt->texture->initWithData(vt->m_pTextureData,
vt->m_PixelFormat,
vt->m_TextureSize.width,
vt->m_TextureSize.height,
vt->m_TextureSize);
}
break;
case kString:
{
vt->texture->initWithString(vt->m_strText.c_str(),
vt->m_strFontName.c_str(),
vt->m_fFontSize,
vt->m_size,
vt->m_alignment,
vt->m_vAlignment
);
}
break;
case kImage:
{
vt->texture->initWithImage(vt->uiImage);
}
break;
default:
break;
}
vt->texture->setTexParameters(&vt->m_texParams);
}else{
CCLog("Reload texture over!");
isReloading = false;
}
return isReloading;
}
完成這些以後,我們修改安卓專案中的main.cpp
#include "ReloadLayer.h"
找到Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit 方法,修改else中內容:
註釋掉這句:CCTextureCache::reloadAllTextures(); 新增:ReloadLayer::node();
else語句塊修改後內容:
<span style="white-space:pre"> </span>ccGLInvalidateStateCache();
CCShaderCache::sharedShaderCache()->reloadDefaultShaders();
ccDrawInit();
//CCTextureCache::reloadAllTextures();
ReloadLayer::node();
CCNotificationCenter::sharedNotificationCenter()->postNotification(EVENT_COME_TO_FOREGROUND, NULL);
CCDirector::sharedDirector()->setGLDefaultValues();
到這一步的時候我們可以打包測試一下,你會發現遊戲執行正常,在遊戲執行時按HOME鍵切換到後臺,點桌面圖示可以正常切入遊戲。
未完待續...