This article has been reposted to my GameJolt profile’s blog from my real blog, Torben’s Game Dev Log.
**It has been quite a while since I have made a post but again posts will indeed be few and far between. Though recently I have been researching quite a bit in regards to game engine architecture. This includes three new (to me) very fascinating topics, Resource Management Systems, Game States, and Robust Input Handling. As such, this is going to be the first of three articles, one per each of these topics. As usual I’ll try to offer plentiful amounts of information in these articles. So let us begin with the first topic, Resource Management Systems. I have actually had the pleasure of implementing a resource management system after learning about this, so I will be able to provide some functional code to show you and some real insight into how I did it. The general idea of a resource management system is instead of having to setup a vector of some sort for a class you’ve created to contain a certain filetype (such as the Rendering Resource Manager mentioned in the Rendering Management Thoughts and Questions article) you are able to create one general manager and plugin new data types for it to manage which will be accessible via handle. For instance, what I was previously doing was something similar to this:*
1: class renderResourceMgr {
2:
3: public:
4:
5: renderMgr();
6:
7: ~renderMgr();
8:
9: SDL_Surface getImage(const unsigned int handle);
10:
11: unsigned int addImage(const char filePath);
12:
13: void removeImage(const unsigned int handle);
14:
15: void clearImages();
16:
17: private:
18:
19: std::vector<SDL_Surface> images;
20:
21: };**which would manage loading images and accessing them later using an unsigned int handle. This worked okay, but it was a pain when I had many different file types I wanted to manage (images, sounds, fonts, etc.) and it can get frustrating having to create these file type classes and then figure out how to plug them into the game without being overly coupled. The new resource management system layout, allows you to create a class which handles the file type itself and nothing more, then use a previously made class to access and manage all of your resources of that file type you load via unsigned int handles just like before. The ease of use involved in no longer having to plugin each resource type in it’s own resource manager setup and being able to access all the resources for the entire game at any time through one general purpose interface is very helpful and allows for seamless merging with the game state setup I’ll discuss in an article in the near future. The way the new resource management system works, is via templates. A single template class is created which acts as a general purpose resource management system, then a base file type class is created for all future file types to inherit from. After this is done it is as simple as making a new class for each new file type you want to support, and only adding functionality specifically for that file type. This also allows you to easily make changes to your various file types without having to change code anywhere else in your game engine. Let’s take a look at some pseudo code regarding the implementation of the resourceManager template class:
1: template<class T>
2:
3: class resourceManager {
4:
5: public:
6:
7: resourceManager();
8:
9: ~resourceManager();
10:
11: unsigned int addResource(const char path);
12:
13: void removeResource(const unsigned int handle);
14:
15: void emptyList();
16:
17: T getResource(const unsigned int handle);
18:
19: T getResource(const char path);
20:
21: std::vector<T> getList();
22:
23: unsigned int getSize();
24:
25: T operator [](unsigned int handle);
26:
27: private:
28:
29: std::stack<unsigned int> mHandles; // List of deallocated indexes in mList.
30:
31: std::vector<T> mList; // Resource pointers stored here.
32:
33: unsigned int mSize; // Size of mList.
34:
35: }; *
**Alright, now there are some interesting elements here. First thing you may notice is the mHandles and mSize private member variables. This is a good time to bring up the two other goals of the resource management system, O(1) access speed and reusing deallocated indexes. O(1) access speed means that whenever you attempt to access a resource, it should always be handled in the same amount of time under the same circumstances, meaning it is constant. This means there is no searching through all the indexes or anything of the sort (which wouldn’t be O(1) due to it finding the proper index faster or slower depending on its location in the mList vector). The way you allow for O(1) access speed? Simple, accessing a specific vector index via direct handle is O(1) access speed. mHandles is used to keep track of any handles that were cleared using the removeResource() function. This means that upon a resource’s deletion, the index it was in should be cleaned up and prepared for the next resource. The handle is pushed onto the mHandles stack so that next time a new file path is passed to addResource, it automatically gets added into that index. This is great because growing the mList vector is an expensive operation we do not want to do to often, so reusing already created indexes helps us keep growing to a minimum and it keeps memory usage at a minimum. mSize is the size of mList, the reason for this is that mList.size() is not standardized and this means that different compilers can implement it in different ways, which means it could potentially not be O(1), having an unsigned int keep track of the size manually allows for O(1) access to the size of mList. The rest of this is probably pretty self explanatory, an object is created with a file type and this object will keep track of all resources of that file type, allowing you to safely add and remove resources without having to re-write all this for each and every file type. Now let’s take a look at the base class for all file types to inherit from:
1: class resourceBase {
2:
3: public:
4:
5: resourceBase(const unsigned int handle, const char path);
6:
7: ~resourceBase();
8:
9: void incRefCount();
10:
11: void decRefCount();
12:
13: unsigned int getHandle();
14:
15: const char getPath();
16:
17: unsigned long getRefCount();
18:
19: private:
20:
21: unsigned int mHandle;
22:
23: const char mPath;
24:
25: unsigned long mRefCount;
26:
27: };
*
*This is pretty self explanatory as well, a great feature this adds is reference counting. This allows files to be loaded once and re-used again multiple times without having to reload it into a new object (thus wasting space and time). Whenever the removeResource function of the resourceMgr class is called, it will only actually free the index if, after decrementing it, mRefCount is 0. Other than that, mPath is the file path, mHandle is the index in mList.
To show an example of a new file type being added, you simply create a class like this:*
1: class imageResource : public resourceBase {
2:
3: public:
4:
5: imageResource(const unsigned int handle, const char path);
6:
7: ~imageResource();
8:
9: SDL_Surface getImage();
10:
11: unsigned int getWidth();
12:
13: unsigned int getHeight();
14:
15: private:
16:
17: void setImage(const char filePath); // Private because I don’t want to break all other refrences of this image.
18:
19: unsigned int mWidth, mHeight;
20:
21: SDL_Surface* mImage;
22:
23: };
**
The note on setImage refers to somewhere else in the program accidentally calling getResource(5).setImage(filePath) which would change the file path for all other references of index 5. This would not be good. The general idea of this class is simple enough too, the only additions are those that are related to the image resource itself, which keeps the file types simple and easy to manage.
That’s about it for the resource management itself, the above is all much more clearly covered in this fantastic article on gamedev A Simple Fast Resource Manager Using C++ and STL There is however, one step that the article doesn’t cover which I prefer doing.
Basically create two files, resources.hpp and resources.cpp. These two files act as a simple interface between the game engine and all resource types. Inside these files you simply create a class similar to this:*
1: #include “resourcemanagement.hpp”
2:
3: #include “imageresource.hpp”
4:
5: class resources {
6:
7: public:
8:
9: resources(); // Set imageMgr to a “new” object of imageResource type.
10:
11: ~resources(); // Delete imageMgr.
12:
13: resourceManager<imageResource> getImageMgr(); // returns imageMgr.
14:
15: private:
16:
17: resourceManager<imageResource> imageMgr;
18:
19: };
Then add on new file types as you create them, this allows me to access all of my file types through one convenient object of resources type and I highly recommend doing so.
I hope you enjoyed this article and hopefully learned something, again I highly recommend reading the article mentioned “A Simple Fast Resource Manager Using C++ and STL” as this is where I learned about all this. I am currently in the process of creating many game engine architectural advancements to my coding style so expect a little rush of articles hopefully. I still have two more topics to talk about, those being a robust input management system and game state structuring. Then after all this I may create a “Putting it all Together” type of article showing off my new game engine structure (which I have made a few diagrams for already).
Thank for for reading, and please leave a comment below if you have some recommendations on topics or advice for me.










0 comments