Sprite Manager Class

Started by
4 comments, last by vipah 14 years, 10 months ago
Hi, I am in the process of writting a river city ransom style of side scrolling beat em up game. I am using SDL and C++ (code::blocks ide). Currently I am working on the Sprite Manager class (singleton) and I'd like to run it by you guys to see if what I'm doing makes sense and if possible there are some other ideas that could help me along the way. The class looks something like this: It contains a private member map<string,SDL_Surface*> where the string portion holds the filename of the sprite and a pointer to an SDL_Surface. There is an interface which returns the SDL_Surface pointer when passed a sprite filename. This would be used by the map rendering process (the map would be a vector of structs containing amoung other things the filename of the sprite). Entities would have a series of filenames held in vectors for animation purposes (jumping, running, walking etc...). It also contains another map<string,vector<string>> where the first string holds the "group" that the sprites following belong to. This is used for the Map Editor portion of the game. The reason I will have it organized like this is so that each "group" series of sprites to be shown in a pallet in the map editor program. The would be grouped into things like roads, grass, buildings etc... An interface would be created to return a vector<string> with all the filenames belonging to that group. There would be an interface for loading the above two maps with data from an XML/Text file:

<group name="roads">
  <sprite name="gray_road_1.png" solid="0">
  <sprite name="gray_road_2.png" solid="0">
</group>
<group name="wall">
  <sprite name="stone_wall_1.png" solid="1">
  <sprite name="clay_wall_1.png" solid="1">
</group>
The additional information such as "solid" would be default information for a specific tile and I would imagine this may require me to change the above maps to be map<string,some_struct> to contain that extra information along with the SDL_Surface pointer. I am clearly a beginner to C++ and some of these ideas may not be that great. I have scoured the boards for information concerning Sprite Manager classes but there doesn't seem to be too much on this subject. Anyone care to expand on these ideas with me?
Advertisement
Seems pretty reasonable. Here are a few random suggestions:

1. Use a smart pointer class (e.g. boost/tr1::shared_ptr with a custom deleter) to manage your SDL_Surface pointers. There are some gotchas to watch out for when using smart pointers with SDL in this way, but on the whole it'll make your code safer and easier to maintain.

2. I would create a separate class to represent a 'sprite group' (the class would own a container of smart pointers referencing the sprites in the group). I also wouldn't give the responsibility of managing the sprite groups to the same module that manages the sprites themselves.

3. Don't use a singleton for this. A singleton is an appropriate design pattern to use when global access is needed and when it is an error for more than one instance to exist at any one time. A 'sprite manager' class doesn't meet either of these criteria.

4. Some developers consider class names that include the word 'manager' to be an indication of poor design (it can indicate that the class does too much, or that you haven't thought through carefully what the class is supposed to do to begin with). I don't have a strong opinion on this myself, but it's something to be aware of. One alternative would be to use the word 'cache' rather than 'manager', which makes the purpose of the class a little more clear, and might help keep you from throwing too much stuff into it.

Ok, that wasn't a very good post, but it's late :) Maybe it'll at least give you a few things to think about. (There's a lot of good info in the forum archives as well; if you search the archives for 'smart pointers' or 'singletons', you'll get a lot of good information on those particular topics.)
1.) While I've done some reading on Smart pointers I am not entirely sure what their main purpose / advantage would be. Do they promote some sort of automatic garbage collection? Am I correct in that there is a way to use them in the same way as an std::map? In terms of the challenges with using them for SDL_Surfaces, could you elaborate?

2.) By creating a second class to hold the sprite groups would this be in order to separate the functionality? I am assuming that since the groups would only ever be required by the Mapeditor and the flat sprite list would only ever be used by the game rendering functions, we don't want to include both sets of data is not needed...is this what the intention is?

3.) The singleton pattern stuff is the hardest bit to move away from. Nearly all of the game engine design tutorials are using the Singleton pattern in some way or another:

http://gpwiki.org/index.php/SDL:Tutorials:Complete_2D_Engine_Overview
http://www.sdltutorials.com/
http://gamedevgeek.com/tutorials/managing-game-states-in-c/

I am assuming from the books I've read and the other very limited tutorials on engine design that I would want to create these objects in a root cApp type class and then send pointers to the subsequent classes that make use of them? (ie. create an instance of an Input class and send a point to it to the cPlayer class, create an instance of the cGraphics class and then send a pointer to the cMap class and cEntity class). This is the part that I'm sure stumps most novice programmers and sends them running to the "ease" of use in global static classes.

4.) I understand what you are saying here and see your point. I will try to think of my class names more strictly based on their intended function.

Thanks!
Quote:Original post by vipah
3.) The singleton pattern stuff is the hardest bit to move away from. Nearly all of the game engine design tutorials are using the Singleton pattern in some way or another:


Just don't do the part of the work where they make the manager class into a singleton. Then you'll be forced to use instances of the manager in a natural way. Voila, you've moved away.
Quote:1.) While I've done some reading on Smart pointers I am not entirely sure what their main purpose / advantage would be. Do they promote some sort of automatic garbage collection?
Yes, automatic garbage collection is one reason to use smart pointers (although it's worth noting that this really isn't the same thing as the 'built-in' garbage collection offered by languages such as C#).

More generally, smart pointers are used to manage ownership in a (relatively) safe and predictable way. Here are a few questions you can ask yourself, the answers to which will help show why smart pointers are useful:

1. Your 'sprite cache' class passes a raw sprite pointer to the caller. Who owns the sprite now? Who's responsible for deleting it?

2. What happens if the sprite cache object deletes the sprite for some reason while someone else is still using it? Or what if the sprite cache itself is destroyed?

3. What happens if someone else (outside of the sprite cache object) deletes the sprite, and then later someone requests that sprite from the cache?

And so on. Of course these problems can all be addressed through careful programming, but using smart pointers and other RAII containers will, in general, make your code cleaner, safer, and easier to write and debug.

I haven't read the whole thing, but this looks like it might be a good article to take a look at.
Quote:Am I correct in that there is a way to use them in the same way as an std::map?
Not quite sure what you mean here. Smart pointers and std::map are both RAII containers, but other than that they solve different problems, more or less. You can certainly store smart pointer objects *in* a map though, if that's what you mean.
Quote:In terms of the challenges with using them for SDL_Surfaces, could you elaborate?
First of all, you have to make sure to use the custom deleter feature so that the referenced object will be freed properly. Also, you have to make sure that no smart pointer that references an SDL_Surface goes out of scope *after* SDL is shut down, as this could lead to crashes or other undefined behavior. (I'd actually never thought about this, since all of my resources are cleaned up before SDL is shut down, but someone else on the forum pointed out that this can be a problem if you're not careful. Actually, making your cache a singleton is one good way to cause this very problem :)
Quote:2.) By creating a second class to hold the sprite groups would this be in order to separate the functionality?
Yes (you could say it would be an application of the SRP).
Quote:I am assuming that since the groups would only ever be required by the Mapeditor and the flat sprite list would only ever be used by the game rendering functions, we don't want to include both sets of data is not needed...is this what the intention is?
Yes, that's another reason; separating the two makes it easier to give the various modules only what they need to function and nothing more, which is generally a good thing.
Quote:3.) The singleton pattern stuff is the hardest bit to move away from. Nearly all of the game engine design tutorials are using the Singleton pattern in some way or another:

http://gpwiki.org/index.php/SDL:Tutorials:Complete_2D_Engine_Overview
http://www.sdltutorials.com/
http://gamedevgeek.com/tutorials/managing-game-states-in-c/
There is definitely a disconnect between what you'll see in production code, books, and online tutorials, and what you'll see recommended here or advocated from a theoretical or academic standpoint. A whole essay could be written on this subject, but I'll just make a couple of quick comments.

First of all, a lot (but not all) of what you see in production code and references on game development is simply the product of inertia. It will probably take a while for the current 'best practices' with respect to C++ coding and software design in general to find their way into the types of resources you're likely to find online and elsewhere.

It's not all inertia though; there are other considerations as well, such as practicality, and simple personal preference. There is no 'one right way' to design software, and different people have different approaches.

That said, the singleton pattern is a somewhat peculiar case, and I would argue that although the pattern is used quite often, it is almost always misapplied. I suggest you search the forum archives for 'singletons' and read some of the previous threads on the subject. You might also give this a read (it's by our own ToohrVyk, I believe), especially the last few sections (starting at 'Misconceptions').
Quote:I am assuming from the books I've read and the other very limited tutorials on engine design that I would want to create these objects in a root cApp type class and then send pointers to the subsequent classes that make use of them? (ie. create an instance of an Input class and send a point to it to the cPlayer class, create an instance of the cGraphics class and then send a pointer to the cMap class and cEntity class). This is the part that I'm sure stumps most novice programmers and sends them running to the "ease" of use in global static classes.
Yes, that is the approach that many would advocate, and yes, it can be a little daunting :) In my own experience though, it's definitely worth it. Passing everything around explicitly in this way makes you think very carefully about what really needs access to what, which, in turn, leads to increased modularity and better overall design.
Thanks for the replies.

Does anyone have a link to a decent tutorial or some source code which details the Model-View-Controller pattern and how it applies to game programming?

From what I read it allows for such things as an easy way to replace the controller class for an entity with network code or AI code, this is something I am interested in as I plan to make the game Networkable at some point down the road.

I've looked around but there is precious little with regards to this pattern and game development.

Thanks guys.

This topic is closed to new replies.

Advertisement