May and the Amazing Bouquet - Refactor Part 1
Hello everyone!
This week I began a major ground-up refactor of my game, May and the Amazing Bouquet.
There was a lot of clunky code, and a lot of design decisions which were making adding maps and characters more difficult than it needed to be. This is my first time making a professional game, and first time maknig a game with the Godot engine, so some of that is to be expected.
Ultimately, I felt a refactor was in the cards. After mapping out the problems and brainstorming solutions, here's all of the changes so far (this will be kinda technical):
1. Screen-sized "maps" no longer need scene or script files.
Previously, each screen-sized "map" had a scene file (.tscn) containing its terrain data and NPCs. It also had a script file (.gd) containing all dialogue and cutscenes for said map. This required...a lot of files. And a lot of time spent remembering which map is which...
Now, all those files are *gone*!
The scene files with terrain and NPC data are now merged into a single large world map scene file. For the overworld, this means over ~200 fewer files to keep track of. And makes editing them much easier too.
As for the cutscene and dialogue data (those .gd files)...
2. Cutscene and dialogue data are now stored in each NPC's script file rather than the map files.
This is much more convenient:
Don't need to keep track of dozens of map script files anymore.
I can move an NPC to a different map, and now its dialogue data will come with it (don't need to move it to different map file anymore)
NPCs with special behavior now only need to define that behavior once, in that NPC's script file. Rather than in every map it exists in. For example, signposts.
Objects that appear in many places now have all their dialogue and events in one file. For example, again, signposts. And doorways. This will make it easy to change all of them at once, if needed. And spot inconsistencies.
3. NPCs no longer need scene (.tscn) files.
Most NPCs are, funtionally, extremely similar. Same hitbox size, same common animations (walking, standing, ...), etc. Instead of copying and pasting all that similar functionality into lots of similar scene files, we can generate them on-the-fly by using an entity-component system that automatically adds all these common elements. This also means I don't need to worry about keeping all those similar files in-sync, since they'll all be using the same file now.
Most NPCs will differ only in needing a different spritesheet, and a different script/cutscene file. We can set things up so those will still be customized. Any further customization can be defined within its scripts file, which previously were defined in the scene file as exported variables.
4. Overhauled Scripting Engine
My scripting engine which handles things like cutscenes, dialogue, etc, has been overhauled. It now uses a scripts stack. NPCs can have "automated" behavior such as loading in different custom attributes (like bounding box size or animations), or other idle behavior.
Once the loading is done or the behavior ends, that script is removed from the stack.
If the player triggers some action by interacting with the NPC, a new script is pushed onto the stack. NPCs can talk to the player (stack item #2), and then go right back to whatever it was they were doing before (stack item #1).
5. Overhauling Global Classes and State
The old project had *lots* of global utility classes. Which didn't need to exist.
Many of them did not actually have state to keep track of, like math-related ones. These utility classes have been converted to static classes, which can still be accessed from any code at any time. But I no longer have to worry about their initialization order, since they aren't being initialized on startup anymore.
Some of them actually *will* require state to keep track of. To simplify things, I'm moving all of those things into a single "GlobalState" parent class. That, and a "Constants" class of lookup values, will be the only two global classes going forward.
When editing the world map, I can now place a marker denoting each NPC. And I can specify the NPC just by changing the marker's name. Doing so will define its texture, and its script file, and its marker will update to show that NPC's sprite.
I can also specify up to two other parameters in the name (a number and a keyword), and that's it. No more having a dozen different attributes to specify. It's three, max. And I can see them all at one - no more checking them one at a time. Everything else will be customized in its script file.
(this might look like a mess, but honestly there's no need to be rearranging any of it anyway. I'll be looking at the map instead. I could add sub-nodes to categorize things if I wanted to - but it's not necessary.)
Editing the map's terrain is much easier too. I can create new "rooms" just by adding more tiles where I want the rooms to be. I can move NPCs from one room to another by dragging.
I can even jump straight to an NPC's script file by clicking it and pressing F2. And then jump back to the map view by pressing F1. No more digging through folder hierarchies to find it.
Additionally, I want to completely eliminate the concept of "map coordinates" and "map names." Previously, a scene that warped the player to a different map had to either specify the map name, or the x/y position of that map in the world. This was tedious and required either a lot of counting, or memorization of names.
Instead, warps will define a target marker, and the game will automatically figure out where in the world that marker is. This is another way I plan to make editing much easier.
The overhauled game, so far, lacks a lot of what it had before and *looks* underwhelming. But under the hood, it's in a much better shape, and I expect it to regain a lot of the features it had pre-refactor very quickly.
In fact, so far, it's all only 580 lines of code. And I'd like to keep it as simple like that as I can. ^^