For an open source game I wanted to redesign the resource management system but got into a dead end and need some pointers to some reading material or where to go now.
Starting point was (mostly) a Loader
class Header/Src
Format of the resources:
- Resources are (usually) in archives, that consist of numbered files (no names) which can be of any type and may contain related meta data (size, draw offset, ...)
- Often common formats (wav, midi, bmp) but custom formats possible, e.g. for textures:
- 2D Offset (model matrix translation)
- Player color mask (extra greyscale bmp)
- Partitioned bmps (e.g. links (indices) to body and head (bmps) to reuse bmps in the same file)
- Extensions/Overwrites possible: Having 2 archives with the same name (different folders) will merge them (later loaded archive entries add or overwrite ones from the earlier ones)
The problems I wanted to solve are messy code (no SRP...), non-uniform handling of resources and a dynamic_cast for most accesses (basically: dynamic_cast<type*>(Get("foo", 42))
). Having only 1 type of texture would also simplify rendering (maybe 2: One with player masks, and one without)
My approach (based on ENTT):
ResourceLocator
: Given a name, returns the list of archives to load (handles extension/overwrites)ResourceCache<Type>
: Holds a loaded resource of a given typeResourceLoader<Type>
: Loads a resourceResourceRegistry
: Holds multiple caches, 1 locator and (maybe) loaders. Provides access to all resources by checking the cache first, and loading it on miss (adding it to cache)
This works well for sound effects and music:
SoundEffect& get(ResId id){
if(id in soundcache) return it;
archive& = get(id.name);
SoundEffect* effect = load(archive)
add to cache and return effect;
}
This is nice and generic. But I'm struggling on how to do this for textures for which I have different types:
- "normal" textures (backgrounds, buttons...) -> easy
- map-specific textures (e.g. trees with snow or w/o snow depending on map type. Previously the
Loader
stored a reference to the map archive and consumers simply calledGetMapTexture(idx)
-> might not be required for all maps, shall I unload all of them on map change and reload as required? - nation-specific textures (every nation has an archive and/or an offset in an archive) -> Using
("archive", number)
as the key does not work anymore.
Additionally lots of textures may need to be combined (e.g. the split-bitmaps or shadow with buildings/people...) and there are animations. This led to having fixed size multi-D-arrays with many different indices where the meaning is contained in the Loader
class.
So the "normal" textures are easy: Just load them into an OGL texture and use them. The others need (usually) multiple images combined into 1 texture. So I don't want to put those parts into the texture cache but only the combined one. But having those multi-D-Arrays also feels like a design flaw. And how would I reference/use them? How are others doing that?
Note: The indices of the images making up a texture/bld... can be (kinda) easily calculated from the nation index, building index, animation frame index, ...
Example usages (from current/old) code:
drawAnimal(int type, int dir, int animationFrame){animal_cache[type][dir][animationFrame].draw(pos...);}
drawCarrier(...){carrier_cache[carriedWare][dir][animationFrame][isFat].draw(pos, playerColor,...);}
drawBuilding(...){building_cache[nationIdx][bldIdx].draw(pos, playerColor,...);}