There are quite a few things in the underlying infrastructure of video games that are caught in a bit of an unfortunate predicament: the only time someone actually even notices them is when they were designed poorly. The field of content storage and loading in video games is definitely one of those areas. So, while you wait for me to finish implementing locations in the game (it’s coming along, I promise), I thought I’d talk a little bit about it for your interest’s sake.
Discussion after the break, for those who are interested.
I’ll be upfront about this fact, first of all: this is the first project I’ve undertaken of this magnitude that’s gotten this far. I’m not new to programming by any means – I do computer programming as a career, in fact – but I have definitely not had a personal project of this size come this far before. A big factor in that is the MLP community – if no one cared about this project, it might’ve ended up in the pile of ambitious projects in the past that I got bored of after the going got tough. As a result of this being the first project of this scale that I’ve really stuck with, it’s definitely very much a learning experience for me in several areas, and the above area is certainly one of the biggest for me.
When it comes to content storage and loading, there are basically two desirable outcomes that are mutually exclusive – never a good spot to be in when trying to solve a problem, to be sure. The first outcome is that perceived loading times are minimized – nobody likes sitting through a game where every time they go to another screen, they get hit with a black screen and the word “loading”; this can majorly disrupt the flow of the game. The second outcome, though, is that memory usage is also minimized – ideally, a game shouldn’t be using any more resources than it needs, and should not allocate tons of memory for resources that it’s not going to be needing in the near future, since otherwise it’s needlessly bumping up the game’s system requirements.
Obviously, however, these outcomes are in conflict. Perceived loading time can be reduced by pre-loading more resources than you actually need, such that when you go to a new area, all the resources needed for that area are already present in memory. On the other hand, memory usage can be reduced by not pre-loading as many extra resources above and beyond what you actually need. Clearly, a balance is needed here.
I should say, though, that it’s not entirely just a complete tradeoff. There are definitely things that can be done to improve one without harming the other. For example, if actual loading time is improved – that is to say, the time between knowing we want a resource and actually having it available – then we can clearly reduce perceived loading time without adversely affecting memory usage. And this is, in fact, something that I just recently worked on – a little while back, the loading time for a location was upwards of thirteen seconds, which would’ve required me to load a huge amount of resources in order to prevent the player from seeing a loading screen. I realized that a big source of this problem was the fact that every single image in the game was stored individually, and needed to be separately run through the image decoder, so as an alternative method of storage, I instead created a giant image and pasted into it all of the images needed for that location, and then stored rectangles that indicated where in that image the specific images were that I actually wanted to individually draw. This single change cut the loading time from thirteen seconds down to three seconds. This was much more reasonable.
The #1 ultimate determinant for any design decision in a game is the impact it will have on the player (well, in a commercial game there are also budget considerations, but that doesn’t apply here). In this case, quantifying the optimal outcome for the player is fortunately very simple: the player, ideally, should not be faced with any loading screens, or if he or she is, they should be brief and nonintrusive – no pausing for a minute while the player has to sit and watch for a minute while a progress bar slowly fills. Given the above work to get the loading time down to three seconds, this is fortunately actually fairly easy to accomplish – since the player is likely to spend at least a couple seconds in each location, we can just load all adjacent locations to the player’s current location while the player is in that location. This may end up having certain optimizations put on it – for example, it might be worthwhile to also keep, say, the last five or so locations the player’s been to in memory in case the player is stumped and is quickly moving between them – but overall this seems to be a good starting point.
The bottom line for me is that working on this game has really given me an appreciation for the finer details of this sort of thing, as I think there’s a lot to video games that we just take for granted but which actually requires a fair bit of thought on the part of those implementing it. I don’t know if anyone out there cares about this stuff, but in case anyone does, I may periodically do more articles like this highlighting this sort of thing, in the hopes that it might cause others to recognize these underappreciated areas of implementation as well.
(I know, I know, you just want to see locations in action. Don’t worry; they’re coming. )