游戏中需要全局管理很多的资源,如图片、音频文件等。下面我们设计一个骨骼资源管理类,名叫:AtlasLoader,设计为全局共享类,用于载入资源和通过资源名称获取精灵帧。
下面先上头文件:
#ifndef __EngryBird__AtlasLoader__
#define __EngryBird__AtlasLoader__
#include "cocos2d.h"
/** * The struct of the atlas file */
typedef struct tag_atlas {
char name[255]; // the image's name
int width; // the image's width
int height; // the image's height
cocos2d::Vec2 startPosition; // the image's start position
cocos2d::Vec2 endPosition; // the image's end position
} Atlas;
/** * The hepler class for loading the resources of atlas.png/atlas.txt * * @Note This is a global shared class,just call the getInstance class method * to get the object */
class AtlasLoader {
public:
/** * Get the instance of AtlasLoader */
static AtlasLoader* getInstance();
/** * Destroy the instance of AtlasLoader,and will free all memory it takes */
static void destroy();
/** * Load with file name of an atlas and default load the atlas.png to texture * * @param filename the name of an atlas file * * @note This function load the images sync,so it will delay the main thread. * You can call like that: AtlasLoader::getInstance()->loadAtlas("filename"); */
void loadAtlas(std::string filename);
/** * Load with file name of an atlas * * @param filename the name of an atlas file * @param texture the Texture2D object,using to create a sprite frame * * This function load the images sync,so it will delay the main thread. * You can call like that: AtlasLoader::getInstance()->loadAtlas("filename",texture); */
void loadAtlas(std::string filename,cocos2d::Texture2D *texture);
/** * Get the sprite frame with an image name * * @param imageName the name of image * * @return the sprite frame object related to an image */
cocos2d::SpriteFrame* getSpriteFrame(std::string imageName);
protected:
/** * The default constructor */
AtlasLoader();
/** * Initializes the instance of AtlasLoader * * @note When you are porting Cocos2d-x to a new platform,you may need to take care of * this method. * * @return true if succeeded,otherwise false */
virtual bool init();
private:
/** * The singleton pointer of AtlasLoader */
static AtlasLoader* s_sharedAtlasLoader;
/** * The container to store all the sprite frames that has already loaded */
cocos2d::Map<std::string,cocos2d::SpriteFrame*> _spriteFrames;
};
#endif /* defined(__EngryBird__AtlasLoader__) */
声明了Atlas枚举,与atlas.txt文件中每一行结构是一一对应的。
骨骼文件中,会记录着精灵的图片名称、宽度、高度、起始位置和终止位置,这样就可以通过读取文件来获取精灵应该放置在哪里了。
/** * Get the instance of AtlasLoader */
static AtlasLoader* getInstance();
我们在编码的时候,尽量与cocos2dx引擎的代码风格一致,这样更易于阅读。静态方法是类方法,不是某个对象的方法,这样就是共享的了。
接下来是实现文件。
关于怎么写单例,这里只说一遍,后面的文章中不会重复再说明,因为写法是一样的,只是名称变了而已。
// Initializes the shared object to be nullptr
AtlasLoader* AtlasLoader::s_sharedAtlasLoader = NULL;
AtlasLoader* AtlasLoader::getInstance() {
if (s_sharedAtlasLoader == NULL) {
s_sharedAtlasLoader = new AtlasLoader();
if (!s_sharedAtlasLoader->init()) {
delete s_sharedAtlasLoader;
s_sharedAtlasLoader = NULL;
CCLOG("Error: could not init s_sharedAtlasLoader object");
}
}
return s_sharedAtlasLoader;
}
静态成员变量s_sharedAtlasLoader不能放在构造函数中初始化,所以需要在全局中初始化。
下面是getInstance方法的实现,这里通过判断全局共享变量是否为NULL,如果为NULL表示没有创建过,则创建之,否则不再创建,而是直接使用。这就是所谓的单例。当创建失败时,就把资源给释放掉了。
注意:这里是没有考虑线程安全的,在iOS中,我们写单例都会考虑到线程的安全问题。
下面说说加载资源最重要的方法:
void AtlasLoader::loadAtlas(std::string filename,cocos2d::Texture2D *texture) {
// 这里通过文件操作,读取文件,如atlas.txt文件
// FileUtils是一个文件操作辅助类,是引擎自带的类,获取的是字符串
std::string data = FileUtils::getInstance()->getStringFromFile(filename);
// 这里是通过分割读取行,一行一行的读取后(换行符是分割符),把对应的字段写入到Atlas结构中,然后获取精灵在骨骼中的区域,就可以获取到精灵帧了,并放到_spriteFrames中,这个是一个Map类型对象,可通过精灵的名称(key)来获取到精灵帧(value)
size_t pos = data.find_first_of("\n");
std::string line = data.substr(0,pos);
data = data.substr(pos + 1);
Atlas atlas;
while (line.length() > 0) {
sscanf(line.c_str(),"%s %d %d %f %f %f %f",atlas.name,&atlas.width,&atlas.height,&atlas.startPosition.x,&atlas.startPosition.y,&atlas.endPosition.x,&atlas.endPosition.y);
atlas.startPosition.x *= 1024;
atlas.startPosition.y *= 1024;
atlas.endPosition.x *= 1024;
atlas.endPosition.y *= 1024;
// 读取下一行
pos = data.find_first_of("\n");
line = data.substr(0,pos);
data = data.substr(pos + 1);
// fix 1px edge bug
if (atlas.name == std::string("land")) {
++atlas.startPosition.x;
}
// use the data to create a sprite frame
Rect rect = Rect(atlas.startPosition.x,atlas.startPosition.y,atlas.width,atlas.height);
auto spriteFrame = SpriteFrame::createWithTexture(texture,rect);
_spriteFrames.insert(std::string(atlas.name),spriteFrame);
}
}
好了,这个类已经说得很明白了吧,如果还有不明白的地方,可以联系我哦!!!!
下一步,说说这个屏幕适配的问题