Music playback and memory management in Citadelic
Last week I've posted about making music for Citadelic. This week's devlog is about adding that soundtrack to the game.
Music and sounds are resources that can potentially use up more RAM than any other asset in the game. It is important to properly manage memory usage when dealing with big files like these, otherwise the game may take too long to load, or in some cases use up too much RAM and even crash.
My engine has a system that automatically preloads music that will soon be played. All of my previous games are level-based, so the engine knows ahead of time which music tracks can potentially be played in each level by scanning the script file that is attached to that level. This way the game can delete all unnecessary music from its memory and only load what's needed, so there is usually only 1 or 2 music tracks in memory at a time. This keeps RAM usage low and decreases each level's loading time.
But the problem is that Citadelic is not level-based like my previous games. There are around 40 minutes of soundtrack split into 6 tracks that must be played in random order, and it's possible that all songs would have to be loaded and played in a single game session. On top of that, there is a 7th track "Preparation", which is always played in the menu.
One approach would be to preload all songs before the game starts while a loading screen is shown, but that would take too much time, as it may take a couple of seconds to load and decode each song.
Another approach would be to stream small bits of music directly from the hard drive, instead of loading the entire music track at once. This would decrease RAM usage, but it also means that the game would be constantly reading and decoding data as the game is being played. This isn't a terrible solution, and I think this is what a lot of games do these days.
The simplest approach is to just load each song on demand when it's time to play it. The problem here is that loading the song and decoding it may freeze the game for a few seconds, if it is done in the main thread. But if the loading and decoding is done in a background thread, asynchronously, the lag is gone. That's what I've chosen to do.
I've also added a simple playlist system to the game, which randomly shuffles the tracks, so that the player doesn't get bored of listening to the same track over and over again. In addition, I've added a music selection screen to the pause menu, where the user can manually change the current track.
The game is now very close to being finished, but there are still 2 major things left to do: in-game medals (and Steam Achievements), and a final testing/balancing pass.
If you haven't tried the game yet, check out the free demo on Steam and let me know what you think. Add the game to your Wishlist if you liked it!