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!
apenas um comentário
Leave a reply
Cara muito show parabéns pelo tuto !!! vou aplicar !