(Writing this one a few days early because I’ll be unavailable for when I normally post these. ^^)
Hope everything has been going wonderfully for everyone! I’ve been utterly flat-out with work, mostly to do with Management In Space, which is coming along really well! I’ll talk about some of the updates soon, but firstly; I mentioned last month that I went to GDC this year thanks to a travel grant provided by Screen NSW. Well they just posted an article about that trip with a Q&A with myself and the other recipients of that grant! You can check it out [here]!
So let’s talk about some MIS updates, specifically things that I’m doing differently this time around. What I mean by that is comparing the quick prototype build I made in Unity last year to the main build I am working on now in Godot, currently in pre-alpha. There are many similarities between the two, in essence they are basically the same game, with the main difference being one is a vertical slice meant for booth demonstration and short gameplay, while the other is being built as a full game. While working on the prototype last year I was able to cut a lot of corners because it was always going to have a limited range of functionality. The current build on the other hand is being built with a strong “forward-focus” in mind; I try to plan as much as possible in advance, write code to account for re-usability and future functionality, and try to make things as open-ended as possible. Making the prototype first has helped with that a lot, because rebuilding the game now has not only been much faster, but I’m able to make a lot of corrections ahead of time. So, I’m going to discuss a few of the features I’ve been working on in the current build that differ from the prototype.
- Prefabs & Modding: If you want to store an object and duplicate it to be used later, you could call that object a “prefab”. In MIS, almost every object is some sort of prefab; the modules, ships, bullets, asteroids, they’re all copied from their respective predefined “master objects”. Godot has a similar concept called “PackedScene”; a predefined Scene/Node that is stored and from which a duplicate can be instantiated into the game scene. Either way, it’s a common game-dev concept. But what if I want to open up the door to modding? I could allow for people to make their own PackedScenes which are loaded into the game, but that would require people to have access to the source code (somewhat acceptable) and would mean they could have most anything uploaded and instantiated into the game at run-time (less acceptable). Because modding is something I absolutely want to include in this game, I want to do it right, and that means incorporating the structure that allows it as soon as possible, instead of just tacking it on at the end.
While I still have a handful of ideas about how best to implement it, including building Godot Tools specifically for modding (which I have already tinkered with), the method I am sitting on at this early stage is having custom JSON databases. Let’s take a ship entity for example; the ship entity that I build will have a JSON element that points to a PackedScene. That resource is then loaded into a Preloader object (an object that stores and loads resources for quick access later). When I want to spawn that ship in, I instantiate it from the Preloader, simple! Now let’s say someone wants to mod that ship, maybe give it a new appearance or tweak its stats. Well they can install their own additional JSON database that, when loaded, creates Modification Data for that entity. When the ship is spawned now, it instantiates the base resource, and applies the changes according to the Modification Data. But if that player wants to create a totally new ship? Well, they would create their own object ID, which can either point to a reference resource or a blank resource, which is then modified or built when spawned.
Now I am certain there are better ways to do this process, and developing tools to install custom PackedScenes is still on the table, and there is still a lot of develop over time, but right now this process works. Loading objects from preloaded resources works, applying modifications works, and it all works fast, so I’m very happy with it for now. It’s a solid foundation to build both the game and the future modding tools from.
- Time & Coroutines: Both Unity and Godot have different methods of counting the passage of time in their engines. Unity‘s worked really well for me, particularly how it handles “Scaled Time” (manipulated time) and “Unscaled Time” (real-time) allowing me to easily slow-down and speed-up the game. This in turn also controlled how it handled coroutines; functions that pause to account for a passage of time within them. These could be used for anything time-related from setting alarms and delays to looping an event every few frames.
Godot doesn’t really have this and instead it has the very similar async functions. These are functions that must, at some point, await for a certain signal to occur, often a Timer signal. However, at least from what I’ve been able to find in the documents and forums thus far, creating an await timer has to create a new Timer Node. So all the little coroutines I would have running in the game all have to create their own timers. Additionally, these timers can’t be uniquely time-scaled. So I made my own singular static scalable timer!
Any async coroutine in MIS can use my custom GameTime class. This is a single class that is updated every frame counting the milliseconds that passes in both “Scaled” and “Unscaled” time. When it updates, it triggers any alarms for coroutines that may be waiting. It also accounts for overshooting alarms by sending the overshoot-multiplier back to the alarm’s signal. For example, let’s say the player is playing on ultra-fast speed and a there is an effect that must update dozens of times a second. If a single frame passes and it counts that the alarm would have triggered multiple times, it sends that number back along with the signal, allowing the effect to trigger the correct number of times and catch-up. Obviously it isn’t as good as being frame-perfect, but I’ve tested it at different speeds and intensities and it plays out really cleanly, and only really occurs when seriously pushing the time-scale to degrees that probably won’t be allowed in normal gameplay.
- Universal Object Controller: In the prototype, most object types had their own set of components. Ships shared ship components, modules had module components, and even though drones were basically ships themselves, they were also their own class. This was a by-product of making a rushed prototype, and it worked for what it needed to do. But, in the main build, where everything needs to talk to everything, I’ve opened that concept way up. Now, anything that is built in space is a “Space Object”, each managed by an ObjectController. So every module, every ship, every asteroid, and every drone originates from the same class.
From here, I could utilise inheritance to define every subset of object, then every subset of that type, and so on until I have carefully curated classes for every object in the game, but I believe this method would restrict modding in the future. As such, I’ve opted to instead have a system that checks and adds sub-controllers. A drone, for instance, would be an ObjectController with the Entity sub-controller and then the Drone and Flight sub-controllers under that. When spawned, the object looks at itself, and then processes its behaviour automatically based on its controllers and their settings. This method allows everything to talk to everything, because everything is the same base class as everything else! It not only makes much of the coding easier, but also lets us do some wild stuff, such as converting modules into drifting debris and ripping weapons off ships to use on our own station! This process also works better with the Modification Data I mentioned earlier, as it means that we can load any base resource and apply modifications to the various controllers, even add and remove controllers, to create custom new objects.
I’ve obviously done a lot more than mentioned here, these were just some of the cooler things I’ve worked on lately that also don’t give away any of the cooler secret new ideas I have for the main build. I’m sure that, if you’re a Godot master, or just a more seasoned developer than I, much of this is either trivial or possibly even a less efficient process to something you’ve devised yourself. But for me, this is all fantastic progress as I build a much more powerful game! I will admit I am truly loving being able to work on this project full-time, I genuinely enjoy my work, even when it can be stressful hahaha! I’m excited to show everyone how the new build has been coming along later this year, but till next time, all the best~! ^^