This is the third in an (N) part series on the Seven Essential Elements of a Tile Based Rendering Engine.
Historically, I typically referred to this topic as tileset and sprite management, or simply sprite management, or even image management. However, now that I have done work in various graphical modalities (for lack of a better term), I can tell you that there are, generally, about 3 ways in which you will make use of visual elements:
- You will make use of large sheets with many images on them, and render them with whatever 2D rendering API is handy (GDI, DirectDraw, SDL, XNA(SpriteBatch))
- You will make use of textures which may contain a number of images as above, and render them with a 3D API (XNA, DirectX, OpenGL)
There is even a bit more of a blur between #2 and #3, as you can fake the former with the latter. As a concrete example, XNA has a SpriteBatch class that is used for 2D rendering. It doesn’t actually do 2D rendering… it fakes 2D rendering with 3D rendering.
You can also effectively emulate #2 in a scenario where #1 is more typical of a case. CSS has clipping values for images in HTML, Flash has masks, and while I haven’t checked, I’m sure Silverlight/WPF has SOMETHING that’ll crop a portion of an image. I generally do not do so myself (well, I did once, in a JS application, but not all browsers deal with it very well).
In the end, you wind up in one of two cases: making use of whole images, or portions of images. Portions of images gets a little more complex, but not by much.
Let us start with discussing the common needs for visual asset managers:
First, we have the issue of “caching” the visual element. This varies by modality.
In JS in HTML, we can set the “src” attribute of any “img” tag we want to whatever URI we want. When we do that, however, we are making an HTTP request to load that image. It might take a short time to load it, especially if it is big. This can have undesired effects on the UI, so to avoid this, we can preload our images into “img” tags that are invisible, so that the web browsers caching mechanism won’t have to re-load the image (hopefully) when we want to display it.
In other modalities, we might be loading an image from a resource that is a part of the executing application, or it might be an external file, or an external assembly with resources. Again, it takes a short time to load from resources or files, which can be seen as lag in loading of a scene.
There are two common strategies for dealing with this issue:
- Preload everything
- Load on demand
Naturally, the “preload everything” will be really good once it is loaded. The down side is that there is an intial wait period while the assets are loading up. This is pretty common in games, and viewing a “loading” screen is pretty much expected anymore. If the visual resources are large and/or numerous, you are likely better off going this way.
The “load on demand” strategy is good for getting started quickly, but can really drag things down if a lot of new images are being loaded often, especially if they are large. This can have a detriment on game play. If visual resources are small and not very numerous, this is an OK choice.
In a quick and dirty implementation, I have made applications that simply load up a graphical resource whenever it needs one. If I had a hundred copies of the same graphic on the screen, it would load from resource or disk 100 times.
Don’t do that. Eventually, you will pay a dear price.
Instead, load a visual resource once, and have some sort of management class keep track of what is loaded.
This means that you need a unique manner of identifying each visual resource. In a “one resource, one visual element” environment, you need only keep track of individual image files. In a “one resource, multiple visual elements” environment, you must be able to keep track of not only the image, but also the subimages.
If you are in a memory constrained environment, and your visual elements are not of a fixed number, there can be a tendency to keep loading assets until memory runs out. In cases like these, it may be a good idea to keep a “last accessed” time associated with each of the resources loaded into memory, and to release the resources that were accessed longest ago when memory is getting low.
Ease of Rendering
In a “one resource, one visual element” environment, this is less of an issue. This comes more into play when there are multiple elements on a single resource.
When it comes time to render your visual element, you do not want to be specifying source and destination rectangles. You want to be able to specify a point in space, and the renderer knows how to properly put the visual element into place. This is more complex than it sounds.
Often, in very simplistic board games, we can make all of our images the same size, and render them in fixed size cells. We simply refer everything to one corner of the cell, and work from there. Nice and simple.
In other types of grids, like isometric rhombusses or hexagons, it is more useful to think in terms of where the tile is centered, or the point on the item’s bottom point, or between the creature’s feet. This way, once we have layered a grass tile, an overlay image of the character’s shadow, and then finally the character, we refer to the exact same point in each rendering, and the renderer makes it look right.
We can look at this “reference point” or (as I often call it) “anchor point” as a form of “center of mass”, even though mass has no meaning.
This was a hard one to write, which explains why it took so long between the last installment and this one. The problem was that there are different schemes by which to do your rendering, not just one. They do share some commonalities, though, and hopefully this will give something for you to think about when making your tile based rendering engine.