一、相關說明
在使用cocos2dx開發時,資源需要打包,這裏我們使用一個開源項目:zpack項目。
zpack可以把所有資源打成一個包,然後在運行時解析包內的資源即可。
二、資源打包
- 1.下載zpack項目,用VS打開工程,然後編譯項目生成zpEditorD.exe程序。
- 2.打開zpEditorD創建一個項目,這裏我取名叫做data.zpk。
- 3.然後把Cocos2dx工程下Resources裏的資源放到新建的文件夾data目錄下。
- 4.再打開zpEditorD工具欄Edit中的Add Folder添加data文件夾即可。
三、添加庫文件
1.把zpack下相關文件資源拷貝到項目的cocos2d\external\下,並且創建了PackManager管理類文件。
2.在cocos2d\cocos下的Android.mk文件裏的LOCAL_SRC_FILES下添加zpack路徑,如下:
../external/zpack/PackManager.cpp \
../external/zpack/zpack.cpp \
../external/zpack/zpCompressedFile.cpp \
../external/zpack/zpFile.cpp \
../external/zpack/zpPackage.cpp \
../external/zpack/zpWriteFile.cpp \
../external/zpack/WriteCompressFile.cpp \
上面是爲了Android工程編譯時找到庫文件,同時在proj.android\jni下的Android.mk導入目錄,如:
$(call import-add-path,$(LOCAL_PATH)/../../cocos2d/external/zpack)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes \
$(LOCAL_PATH)/../../cocos2d/external \
$(LOCAL_PATH)/../../cocos2d/external/zpack \
3.然後打開VS工程,在libcocos2d中的external文件夾,添加篩選器zpack,在導入zpack中的所有文件。
四、zpack使用
1.問題處理:
目前zpack文件只能在windows下使用,還需要把一些Android下沒有的函數做一些更改,才能在Android下使用。比如_fseeki64函數更改爲fseek函數,
void File::seekInPackage()
{
#if defined(WIN32) && defined(_WINDOWS)
_fseeki64(m_package->m_stream, m_offset + m_readPos, SEEK_SET);
m_package->m_lastSeekFile = this;
#else
fseek(m_package->m_stream, m_offset + m_readPos, SEEK_SET);
m_package->m_lastSeekFile = this;
#endif
}
除此之外,注意zpack定義了寬字符和普通字符,這裏我們使用普通字符,即屏蔽掉寬字符,在zpack.h中關閉ZP_USE_WCHAR就行了。
#if defined (_MSC_VER) && defined (UNICODE)
// #define ZP_USE_WCHAR
#endif
2.PackManager類編寫
PackManager類是我們用來管理zpack的類,
PackManager.h
#ifndef __CPACKMANAGER_H__
#define __CPACKMANAGER_H__
#include <cocos2d.h>
#include "iostream"
#include "zpPackage.h"
#include "zpack.h"
#include "zpFile.h"
using namespace std;
USING_NS_CC;
namespace zp
{
class IPackage;
}
class CPackManager
{
public:
CPackManager();
~CPackManager();
bool Open(const std::string& filename, const zp::String& path, bool readonly = false);
unsigned char* getFileData(const std::string& Path, ssize_t &size);
private:
void clear();
void copy();
private:
zp::IPackage* m_pack;
std::string m_zpkFileName;
};
CPackManager& PackManager();
#endif
PackManager.cpp文件如下:
#include "PackManager.h"
#include <fstream>
CPackManager& PackManager()
{
static CPackManager s;
return s;
}
CPackManager::CPackManager()
:m_pack(nullptr),
m_zpkFileName("")
{
}
CPackManager::~CPackManager()
{
}
void CPackManager::clear()
{
if (m_pack != NULL)
{
zp::close(m_pack);
m_pack = NULL;
}
}
bool CPackManager::Open(const std::string& filename ,const zp::String& path, bool readonly)
{
clear();
m_zpkFileName = filename;
copy();
std::string writePath = cocos2d::FileUtils::getInstance()->getWritablePath() ;
writePath += m_zpkFileName;
// 這裏很奇怪,在Android下就可以 string 直接轉化爲 wstring,難道是linux的原因
// 其實在這裏可以關閉到寬字符,避免麻煩
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
const zp::String newpath(writePath);
#else
zp::String newpath = path;
#endif
m_pack = zp::open(newpath.c_str(), readonly ? zp::OPEN_READONLY : 0);
if (m_pack == NULL)
{
return false;
}
return true;
}
void CPackManager::copy()
{
ssize_t len = 0;
std::string filepath = cocos2d::FileUtils::getInstance()->fullPathForFilename(m_zpkFileName);
unsigned char * data = cocos2d::FileUtils::getInstance()->getFileData(filepath, "r+b", &len);
std::string writePath = cocos2d::FileUtils::getInstance()->getWritablePath();
writePath += m_zpkFileName;
FILE* fp = fopen(writePath.c_str(), "w+");
if (fp == nullptr)
{
return;
}
fwrite(data, sizeof(unsigned char), len, fp);
fclose(fp);
if (len > 0 && data)
{
delete[] data;
data = nullptr;
}
}
/*
* getFileData函數需要在 CCFileUtilsAndroid類中使用
*/
unsigned char* CPackManager::getFileData(const std::string& Path, ssize_t &size)
{
std::stringstream ss;
ss << "data/";
ss << Path;
zp::IReadFile* file = nullptr;
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
file = m_pack->openFile(ss.str().c_str());
#endif
if (file == NULL)
{
return nullptr;
}
unsigned char* buffer = new unsigned char[file->size() + 1];
size = file->read((zp::u8*)buffer, file->size());
buffer[file->size()] = '\0';
return buffer;
}
3.修改CCFileUtilsAndroid類
在Android平臺下,使用資源如添加精靈都要經過CCFileUtilsAndroid類,所以需要 通過CCFileUtilsAndroid讀取data.zpk文件中的內容。
CCFileUtilsAndroid中有兩個函數需要修改一下:
Data FileUtilsAndroid::getData(const std::string& filename, bool forString)
unsigned char* FileUtilsAndroid::getFileData(const std::string& filename, const char* mode, ssize_t * size)
在getData函數中需要修改的部分是,當找到assets/data.zpk時,即調用CPackManager::getFileData函數,返回讀取的數據
if (fullPath[0] != '/')
{
if (fullPath.find("assets/") == std::string::npos && filename.find("data.zpk") == std::string::npos)
{
data = PackManager().getFileData(filename, size);
}
else
{
......
getFileData函數修改同上:
if (fullPath[0] != '/')
{
if(filename.find("data.zpk") == std::string::npos && fullPath.find("assets/") == std::string::npos)
{
data = PackManager().getFileData(filename, *size);
}
else
{
......
最後在AppDelegate.cpp添加查找路徑
std::string writePath = cocos2d::FileUtils::getInstance()->getWritablePath();
std::vector<std::string> searchPaths;
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
searchPaths.push_back("data");
searchPaths.push_back(writePath);
#else
searchPaths.push_back(writePath);
searchPaths.push_back("../../Resources");
searchPaths.push_back(writePath + "/Resources");
#endif
CCFileUtils::getInstance()->setSearchPaths(searchPaths);
在第一個使用的場景中使用即可
zp::String filename = _T("data.zpk");
PackManager().Open("data.zpk" , filename );
五、總結
使用zpack庫有利於打包,最好對文件進行加密,加密的內容之後再寫。