一、相关说明
在使用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库有利于打包,最好对文件进行加密,加密的内容之后再写。