Gerenciamento de conteúdo (parte 2)
No post passado demonstrei algumas funções para criação e leitura de um pacote de arquivos binários extremamente simples. Neste artigo, iremos colocar as funcionalidades explicadas lá numa classe, e fazer um pequeno gerenciador de conteúdo para que fique fácil recuperar imagens desse arquivo como surfaces SDL.
(NOTA: Pode acessar a parte 1 desta série AQUI)
Inicialmente, vamos olhar a definição da classe responsável por LER arquivos Pack:
class PackReader { private: FILE *mFile; PackHeader mHeader; PackFileEntry *mEntries; public: // construtor padrao PackReader() : mFile(0), mEntries(0) { } ~PackReader() { close(); } // abre/fecha arquivo void open ( const string &file_name ); void close (); // pega surface SDL SDLSurface* getSurface ( const dword id ); };
E agora a implementação:
void PackReader::open ( const string &file_name ) { mFile = fopen(file_name.c_str(), "rb"); if ( !mFile ) { /*ERRO*/ } // pega dados do arquivo fread(&mHeader, sizeof(PackHeader), 1, mFile); mEntries = new PackFileEntry[mHeader.numFiles]; fread(mEntries, sizeof(PackFileEntry), mHeader.numFiles, mFile); } void PackReader::close () { if ( mFile ) { fclose(mFile); mFile = 0; } memset(&mHeader, 0, sizeof(PackHeader)); if ( mEntries ) { delete[] mEntries; mEntries = 0; } } SDLSurface* PackReader::getSurface ( const dword id ) { if ( !mFile ) { /*ERRO*/ } if ( id >= mHeader.numFiles ) { /*ERRO*/ } // cria buffer de dados e le do arquivo byte *buffer = new byte[mEntries[id].size]; fseek(pack, mEntries[id].offset, SEEK_SET); fread(buffer, sizeof(byte), mEntries[id].size, mFile); // transforma em RWOps SDL_RWops *rw = SDL_RWFromMem(buffer, mEntries[id].size); SDLSurface *surf = <span>IMG_Load_RW ( rw, 1 );</span> // apaga dados lidos e retorna imagem delete[] buffer; return surf; }
Agora que temos a classe para ler os dados de forma encapsulada, vamos criar o nosso primeiro gerenciador.
Um gerenciador de conteúdo é responsavel por ler e controlar todo o conteúdo de um jogo. Este nosso gerenciador de exemplo vai nos fornecer métodos para ler e liberar surfaces SDL. Iremos nos aprofundar nele e no sistema de arquivamento ao longo dos artigos, implementando diversas técnicas e sistemas de apoio importantes. Por hora, vamos ver o básico:
// estrutura de apoio struct SurfaceContainer { SDLSurface *surface; int refCount; }; // o gerenciador em si class ContentManager { private: // o nosso leitor de pack PackReader mPackReader; // este map vai conter os conteúdos já em uso map<dword,SurfaceContainer*> mContent; public: ContentManager(); ~ContentManager(); void init (); SDLSurface *getSurface ( const dword id ); void releaseSurface ( const SDLSurface *surface ); };
A classe inclui apenas 3 métodos: init(), q vai inicializar o gerenciador, e os dois métodos para pegar e liberar surfaces. Vamos olhar rapidamente a sua implementação:
void ContentManager::init () { mPackReader.open("arquivo.pck"); } SDLSurface *ContentManager::getSurface ( const dword id ) { // primeiramente, verificamos se já existe esta surface na memória map<dword, SurfaceContainer*>::iterator it = mContent.find(id); if ( it != mContent.end() ) { // já temos essa surface. Incrementa contador e // retorna surface SurfaceContainer *container = it->second; container->refCount++; return container->surface; } // nao encontramos nenhuma surface, entao vamos ler do arquivo SurfaceContainer *container = new SurfaceContainer(); container.surface = mPackReader.getSurface(id); // inicializar contador de referencia container.refCount = 1; // colocar no map mContent[id] = container; // e retornar return container.surface; } void ContentManager::releaseSurface ( const SDLSurface *surface ) { // vamos procurar o container desta surface dword id; SurfaceContainer *container = 0; map<dword, SurfaceContainer*>::iterator it = mContent.begin(); map<dword, SurfaceContainer*>::iterator itEnd = mContent.end(); for ( ; it != itEnd; ++it ) { if ( it->second->surface == surface ) { id = it->first; container = it->second; } } // checar erros if ( !container ) { /*ERRO*/ } // decrementa contador e verifica se podemos remover da memoria container->refCount--; if ( !(container->refCount) ) { mContent.remove(id); SDL_FreeSurface(container->surface); delete container; } }
Pronto. Este gerenciador apenas controla surfaces SDL a partir de imagens estocadas no nosso arquivo Pack binário. O controle de uso é simples e com alta probabilidade de erro (ie. necessita que o programador lembre de liberar as surfaces), e seus algoritmos são ineficientes.
Porém, agora temos uma estrutura básica em cima da qual poderei explicar e implementar detalhadamente um sistema de empacotamento e gerenciamento de conteúdo de nível profissional. No próximo artigo, irei me aprofundar em algumas técnicas de empacotamento “reais”, para que a partir daí poderemos implementar um empacotador interessante, rápido prático e útil.
Até mais garotada!
Cara muito show parabéns pelo tuto !!! vou aplicar !
Muito bom, pena que não sabe fazer o tal “modo profissional”, se alguem souber ajude esse pobre coitado…