Sign in to follow this  
kzar

Making a texture manager

Recommended Posts

Hi, I am making a 2d engine for my game. I am working on getting animation and objects working in a moduler way so that I can build on it. So far I have made animation class's (cSpriteFrame for each single frame, cSpriteBase to store the animation's details and cSprite for each instance of an animation), an object class (cObject just has a cSprite pointer and x,y coords at the moment) and a kind of system class (the engine, I kind of shove everything in it to avoid global variables). Now I was confused as to how to progress, how would the user make a object? He could make a spriteBase, sprite, and then the object manually, or I could make a function to automate it and do all 3 in one but then how could I avoid loading the same spriteBase twice. Should each object really have its own sprite? What happens when I want to have an object that could play a few different animations depending on stuff that happens? Anyway I am getting really confused at this point and I asked my housemate for some advice and he said he uses a teture manager to do this and I have been searching about guides about how to implement one. Anyway I am still a bit stuck. I read that you can use a texture manager to manage animations, particle effects and everything, which sounds great but is making my head hurt trying to figure out how to do it! I have started the texture_manager class which has a std::map to convert the file names of animations to a cSpriteBase pointer so that I can check if an animation has already been loaded before loading it agian. I'm not sure if this is the best way of doing it because that would work alright for making sure cSpriteBase's arn't loaded twice but it doesn't help me when objects have multiple animations or with particle effects or anything. Anyway if someone could explain a bit how to do it, it would be very helpfull. Thanks, David

Share this post


Link to post
Share on other sites
That's a fine start. Just keep with that and you'll gain the experience that will teach you how to do the bigger, better, more complete version.

Share this post


Link to post
Share on other sites
Yeah like Telastyn says youre on the right road, just take it one step at a time and keep adding whatever features you need. I use a std::map as well in my texture manager and it works for me.

Share this post


Link to post
Share on other sites
I guess your right, normaly when I try and figure out a really clever way of doing somthing I just confuse myself and get stuck lol. Thanks

Share this post


Link to post
Share on other sites

Hello David,



Sprite programming is really fun, because once you get it out of the way you'll be producing games like crazy, because sprites are the foundation of animation in game programming, and games are just animation with rules.



Anyway, I like the idea that your trying to do everything in modules, but their only useful if their properly organized, if you're wrapping every method in a module, it becomes redundent, and adds coding overhead with no benefit. The whole point of modules is to organize your code.



I would converge your spriteframe, and sprite class all together into a class called cSprite, and only keep SpriteBase separated. Have cSprite take care of what frame its on, and what animation to use, and where to find this animation, and have a SpriteBase object in Sprite. Your Object class, commonly known as an Entity class would then have have a pointer to cSprite. So to answer your question the user would then create a Entity object like so</pL


{
Entity Monster_Ogre(x, y);
Monster.cSprite = new cSprite("Ogre", x, y);
}
// here is a pseudo code implementation of cSprite()
cSprite::cSprite(char *AnimationSet, int x, int y)
{
if ( strcmp(AnimationSet, "Mermaid") == 1)
{
GraphixManager::LoadSpriteSheet("MermaidSS.bmp");
cSpriteBase::LoadAnimationInfo("Mermaid.ani"); // SpriteBase then takes in the animation details
}

if ( strcmp(AnimationSet, "Ogre") == 1)
{
GraphixManager::LoadSpriteSheet("OgreSS.bmp");
cSpriteBase::LoadAnimationInfo("Ogre.ani", animation_info); // SpiteBase then takes in the animation details.
}

// assign member data
position_x = x;
position_y = y;

animation_frame = 0;

// rest of implementation

}



During your game logic, if the Monster_Ogre was moving, you would tell its cSprite to use its animation method with the "Ogre_Walking" animation, by calling its Animation Method cSprite.Animation("Walking"), if the monster was fighting you would call cSprite.Animation("Fighting"), if the monster takes damage you would call cSprite.Animation("Damage"). Because you already loaded the sprite sheet into memory when you created your entity, loading animation is as simple as telling your image pointers where to point to.





void cSprite::Animation(char *NameOfAnimation)
{
if (IsAnimationDifferent() == true) // see if were doing a different animation
{
SpriteBase::GetAnimationDetails(NameOfAnimation);
Frame = 0; // reset which frame we're on
MaxFrames = SpriteBase::MaxFrames; // redudent, just to illustrate how cSPrite and SpriteBase work
}

AnimateFrame(NameOfAnimation, frame, x, y);
frame++;
if (frame == SpriteBase::MaxFrames)
{
frame = 0; // loop the animation
}

};

// pseudo implemantation of AnimateFrame
void cSprite::AnimateFrame(char *NameOfAnimation, int frame, int x, int y)
{
IMAGE *ptr = (IMAGE)SpriteBase.GetImage(NameofAnimation, 5);
Render::TransparentBlit(ptr, x, y); // this can a global object that all entities see, or you can have a pointer to it in your sprite class, the skys the limit.
// just keep it organized
}



That's one way to do it, you don't need anything else, its that simple, your Entity object will take care of its position and whats happening to it, your Sprite only has to worry about what it's animating, your renderer takes care of actually rasterizing the images, and you only have to take care of your game logic.

Share this post


Link to post
Share on other sites
Quote:

and games are just animation with rules.


Actually games are just rules. How you choose to present those rules to the player(s) is up to you, and by providing well engineered, modular code you can more easily mix/match/fix/upgrade the presentation of the game. Not so infrequently you won't even present animations, images or anything to the player with multiplayer gaming as prevalent as it is.

Oh, and strcmp returns 0 on a match, not 1.

Share this post


Link to post
Share on other sites
Quote:
Original post by ordered_disorder

Hello David,



Sprite programming is really fun, because once you get it out of the way you'll be producing games like crazy, because sprites are the foundation of animation in game programming, and games are just animation with rules.



Anyway, I like the idea that your trying to do everything in modules, but their only useful if their properly organized, if you're wrapping every method in a module, it becomes redundent, and adds coding overhead with no benefit. The whole point of modules is to organize your code.



I would converge your spriteframe, and sprite class all together into a class called cSprite, and only keep SpriteBase separated. Have cSprite take care of what frame its on, and what animation to use, and where to find this animation, and have a SpriteBase object in Sprite. Your Object class, commonly known as an Entity class would then have have a pointer to cSprite. So to answer your question the user would then create a Entity object like so</pL
*** Source Snippet Removed ***


During your game logic, if the Monster_Ogre was moving, you would tell its cSprite to use its animation method with the "Ogre_Walking" animation, by calling its Animation Method cSprite.Animation("Walking"), if the monster was fighting you would call cSprite.Animation("Fighting"), if the monster takes damage you would call cSprite.Animation("Damage"). Because you already loaded the sprite sheet into memory when you created your entity, loading animation is as simple as telling your image pointers where to point to.




*** Source Snippet Removed ***


That's one way to do it, you don't need anything else, its that simple, your Entity object will take care of its position and whats happening to it, your Sprite only has to worry about what it's animating, your renderer takes care of actually rasterizing the images, and you only have to take care of your game logic.




Cool that sounds good. So each sprite file stores all the possible animations for that sprite and each of the animations has a name.

So I would load a sprite text file up, setup the cSpriteBase with its values + an array of all the possible animations, then add a cSprite using that cSpriteBase and then add an object using that cSprite.

Say when I wanted to add an object that was an ogre though, where would I start? somthing like game_engine.add_object("ogre.txt") ?

I pretty much understand how to do it now, thanks for the replys.

Share this post


Link to post
Share on other sites
Quote:

Say when I wanted to add an object that was an ogre though, where would I start? somthing like game_engine.add_object("ogre.txt") ?


Yes, that's it, the implementation is up to you. Generally it's very automatous, where you engine is adding and deleting objects based on the type of game your creating, and the parts that are being played.

Share this post


Link to post
Share on other sites
Quote:
Original post by kzar
Is there a way to loop through every single entry in a std::map ?


for_each(...), or the general for(iterator=map.begin();iterator!=map.end();++iterator){

Share this post


Link to post
Share on other sites
Quote:
Original post by kzar
I'm having a bit of bother getting this working still.


std::map<char *, cAnimation* > animation_map;

map <cAnimation*> ::iterator current_animation;

for(current_animation = animation_map.begin(); current_animation != animation_map.end(); ++current_animation)
{ ...


It says that I'm using the wrong number of arguments on the line I declare the iterator and that "iterator does not name a type" .


The standard map class takes four template arguments, two of which have default arguments.



template <class Key, class T, class Compare = less<Key>,
class Allocator = allocator<pair<const Key, T> > >
class map {
public:
typedef ... iterator;
};



Since only two of the template arguments have default values, you must provide two arguments explitly in order to access the iterator typedef contained within the map class template. Since you are trying to assign an iterator that represents an element of a map with type map<char*, cAnimation*, less<char*>, allocator<pair<char* const, cAnimation*> > > to the current_animation iterator, the current_animation variable must have the same iterator type as that returned from animation_map.begin().



std::map<char*, cAnimation*, std::less<char*>, std::allocator<std::pair<char* const, cAnimation*> >::iterator current_animation;


Or, making use of the default arguments provided


std::map<char*, cAnimation*>::iterator current_animation;


That should get the code to compile.

You should, however, be very cautious when using char*. It is unlikely that the code is doing what you think it is doing.


char SpriteName[] = "Sprite1.bmp";
cAnimation* pAnimation = new cAnimation(SpriteName);
animation_map.insert(std::make_pair(SpriteName, pAnimation));









With the above code, you have added a sprite to your std::map, but the SpriteName variable will no longer exist when the function which added the variable to the map returns, and so you will be left with a pointer to rubbish.

Assuming you were to allocate the char array on the heap, you would fix that problem.


char* SpriteName = new char[MAX_NAME];
strcpy(SpriteName, "Sprite1.bmp");
cAnimation* pAnimation = new cAnimation(SpriteName);
animation_map.insert(std::make_pair(SpriteName, pAnimation));









But doing it this way defeats the whole purpose of using an std::map. The main reason to use an std::map is that it is a sorted container. The elements are sorted based on the 'key values', in this case the char pointers. std::map provides member functions for quickly searching through the map to find elements by key value. Unfortunatly you cannot use these member functions with your current setup, as you require the pointers to be compared with strcmp and not any of the standard searching operators that the quick search member functions use, namely operator<. This means you will have to search through the entire list using strcmp, and so increase search times.


A much simpler alternative is to use the std::string class instead of a char* as the first argument in your std::map. This will mean that your map will be sorted by the actual filenames, and not just some random memory address. You will now be able to use the find member functions of std::map, which have logarithmic complexity.

Your sprite manager class might look like



class SpriteManager
{
public:

cAnimation* RequestSprite(const std::string& SpriteName);
~SpriteManager();

private:

bool IsSpriteLoaded(const std::string& SpriteName) const;

cAnimation* LoadSpriteFromFile(const std::string& FileName) const;
cAnimation* LoadSpriteFromList(const std::string& FileName) const;

void ReleaseSprites();

private:

std::map<std::string, cAnimation*> sprites_;

};

cAnimation* SpriteManager::RequestSprite(const std::string& SpriteName)
{
cAnimation* pSprite;

if(IsSpriteLoaded(SpriteName))
{
pSprite = LoadSpriteFromList(SpriteName);
}
else
{
pSprite = LoadSpriteFromFile(SpriteName);
}

return pSprite;
}

bool SpriteManager::IsSpriteLoaded(const std::string& SpriteName)const
{
return sprites_.find(SpriteName) != sprites_.end();
}

cAnimation* SpriteManager::LoadSpriteFromList(const std::string& FileName) const
{
assert(IsSpriteLoaded(FileName);
return sprites_.find(SpriteName)->second;
}

SpriteManager::~SpriteManager()
{
ReleaseSprites();
}










You could improve the memory management further by using a smart pointer to store the animation objects in the map. That would remove the need for the ReleaseSprites and the explicit destructor definition, as the animations would be destroyed when the map is destroyed. There is also room for optimization, as the map is being searched twice when retrieving a animation that is already loaded, but at least the code is clear.

Share this post


Link to post
Share on other sites
Thanks for that reply! I actualy deleted my question because I figured out the iterator bit, but the character pointer stuff was totaly right. I was actualy trying to figure out what I had done wrong because it was behaving wierdly! Thanks, gona go get it working :)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this