RPG Coding Wisdom

Hey folks, it’s been awhile since I’ve written one of these. As I have been recently thinking about code a lot as I study for coding interviews (I was laid off about two months ago) I figured it would be cool (for coder minded folks) to talk about some of the learnings I have had working on Sonic RPG over the years that I wish I knew when I started.


1. Rely on game state flags for remembering map mutations and just rebuild your game maps from scratch each time you enter/exit, don’t cache!

Given how much stuff is in a game map it is tempting to try to build it once and work off cache as much as possible. IMO this is a mistake. Now there are times that I do like to cache maps—like if you enter battle, win, and come back to a map, you might as well just keep the non-battle map state cached because you are guaranteed to return to that state at end of battle.

If you are entering a hut, or cave, or leaving one map for another, however, it really is just better imo to not cache and instead rebuild the map as if it’s your first time seeing it. The reason why is because there are multiple ways to enter a map (from battle, from loading, from teleporting via cutscene, entering from world map, entering from a submap like a hut/cave room), and you will also want to take into account various configuration changes like day/night, weather, etc. This issue presents itself most clearly when you have a map you frequently return to but whose state may change when you come back to it.

It’s much cleaner to handle these various cases by just rebuilding the map from scratch each visit with whatever modifications you might want and not have to worry about differentiating between cached object states and how that data should be navigated with other configuration changes we may want on map load. Game state flags, which track player progress, can be relied on to sort out aspects of the map that the player messed with that you want remembered like which bots you’ve beaten, which chests you’ve opened, etc (which is needed when you load from save anyways).

Thankfully I figured this idea out working on ep 2, so not too much wasted effort. The next one is in kind of a similar vein…


2. If you have a map that has a ton of configurable stuff on it (knothole vs knothole at night), consider just making a second map

Again, this is just a convenience thing. Keep the data simpler, keep the checks simpler. Now I did not follow this advice in all cases. For the nighttime huts I just reused the daytime huts and have special nighttime code/art which is activated if you load a “night” version of the map. I think it’s really a question of how painful the data management is. If your map has a ton of weird special content in it, like knothole at night map, it might be better to just make a new map rather than hide those weird game objects and only show them under certain conditions.


3. Manage and track your memory—yes even in a scripting language like Lua

This is something I’m still paying for. Because I was lazy and did not prioritize having strict and clear control over memory in the game I have the unfortunate situation wherein some combination of assets, memory leaks, and garbage build up due to constant action list usage, leads to the game topping over 4 gigabytes of RAM at times. This really sucks as it makes the game unstable for long playthroughs at times and makes it impossible for folks on lower end hardware to play the game which should absolutely not require 4gb+ of RAM. I still have work to do to correct these issues and sadly it will be much harder since I didn’t start out with this thought process—overall it’s doable though—what’s not as doable at this point is the next one…


4. All game text should be put into a localization map!!

I really should’ve known better. I had been working on professional games for years that used simple localization keys and mapping strategies. I guess chalk this one up to laziness and not thinking people who didn’t read English would care about this game. I was wrong. Sadly, someone from the discord did ask to translate the game to Czech (I believe it was), but I had to turn them down because all the text is just scattered throughout the code and data… I can still do it at some point—it’s just really painful.


5. Define a clear pattern for async Action Lists coordinating with one another

So much of what I do with action lists in the game is authoritative or purely async. In cases where I want an action list on one object to trigger a different object to do an async action list and then have the first object respond to it in some structured way, I just don’t have any great pattern for this.

You can see this on display if you look at how timed dodge/counter attacks work in battle. I just hackily make the enemy in charge of the dodge/counter stuff and have them manage the whole sequence to maintain the declarative scope in one place. That’s kinda nice for debugging, it keeps the logic simple (well, as simple as the optional interruption and branching of an action list can be) to follow, but I do think it would have been nice and possible (and I can still do this if I feel motivated) to just have some kind of event based communication between one action list and another such that you could tell one action list to wait for the other to fire some signal or what not. Maybe this could just be done using a string as your signal so it’s very readable/understandable.


There is def more I could talk about here, but I’ll leave it at that for now. Hope this was interesting!

Comments

Popular Posts