最近几天解决游戏中安卓遇到一个让人蛋碎的问题,先具体描述一下问题:
游戏在安卓手机上运行正常,按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键切换到后台,点桌面图标可以正常切入游戏。
未完待续...