Today I managed to make some progress on my engine for the first time in a while. A bunch of changes I wanted to make occurred to me as I continued playing Fallout 4.
I don’t want a randomly generated world. Part of the major issue with Fallout 4 is the lack of a clear, coherent storyline, and a randomly generated world would only be more arbitrary and banal. I want to create a plot, rich characters, and have the user’s actions change the game world in the way you see in Deus Ex and the original Fallout series. I’m not sure which is a taller order: a well-functioning procedurally generated world a la Dwarf Fortress, or a well-designed, coherent game experience with a rewarding and engaging storyline.
Regardless, if I want to go in this direction, I need to be able to add static content—quests, characters, items, and etc—to my game engine. 90% of it isn’t geared for this sort of retrieval of pre-created content. Earlier versions of the engine dealt with assets like sprites, images, object metadata and the like pretty well. This version strips off all of the image loading and sprite sheet code in favor of a CP437-based user interface and viewport. There was even an iteration where quests were randomly generated; they were just never exposed to the game world.
Up until this point I had never gotten to the point of adding things like quests. I’ve decided to take a close look at how Bethesda’s engines deal with content like that. Based on what I know about the Construction Kit, forms, and the like, I think quests are simply another kind of component. There are flags specific to how quests are built, such as the number of stages. There are also hooks for code to be inserted, such as what should happen when a certain stage in the quest is reached. Since I already have Python bolted into my engine, I don’t see why I can’t write scripts that attach to component objects in this way.
Of course, all of this means that I need some way of adding this content into what will eventually become the resources on disk. Which means:
Saving and Loading
In order to modify a set world, I need to be able to serialize it to disk, and allow the engine to deserialize it on the other end. Artemis, the entity component system implementation I’m using, has a library that combines its knowledge about how to store and load entities in the world with the Kryo serialization framework.
There was a lot of trial and error in watching the serializer walk down the object graph and attempt to serialize everything it came across. Once I realized it was painless to write custom serializers, everything went a bit more smoothly, though some of the shotgun surgery designed to get heavy or transient objects out of the way of the serializing framework will probably pay off in the long run. Investigating what objects stored references to what in their fields exposed a lot of places where logic should have been migrated to one of the component systems.
In addition, there were a ton of assumptions about the order in which important objects were created. At one point or another, the high level objects which need to be interleaved are:
Universe, which contains some metadata about the game world as a whole, as well as a grid-based reference system for all of the cells in the universe. It’s supported by a lightweight
UniverseParametersconfiguration governing the seed and other important universal values. It is also responsible for huge swaths of the procedural generation algorithm.
Worldis the abstraction specific to Artemis; the manager through which entities, components, and systems are manipulated.
UiStateControlleris meant to be where all of the back and forth between the UI and the game world take place. This is part of the aftermath of decoupling UI code from the game’s run loop, which was itself part of the aftermath of enabling a way to alternate between real-time and turn-based simulation.
StateManageris my thin wrapper around the
World, and is responsible for ensuring that all systems interested in getting events from entities are subscribed.
In the original implementation there was a clear and obvious order to how these things were instantiated. Now that the universe can be pre-loaded, and the world is configured independently from the rest of the game state, the fragile connections between these objects becomes a lot more pronounced. That will probably require some refactoring down the line.
My Own Construction Kit
Finally, and one of the biggest consequences of re-thinking the game’s content, the need for a custom editor becomes apparent. Outside of the game engine itself, I need a way to deserialize a saved world and attach components to objects. In this way, the editor can save changes to the objects in the game world, and then the modified world can be loaded in. This mechanism is exactly the same as the save/load mechanism.
Of course, I’m going into this blind, with no real conception of the shortcomings of Bethesda’s approach. Perhaps this makes my game more moldable, perhaps less so.
There’s still a huge distance to cover in making this work properly. How do I split up game content into separate files? How can I have one new file overwrite components in old files? I’m not sure any of this is impossible, but it’s going to be a lot of work to come up with this scheme at the same time I’m trying to design the editor to put it into action. We’ll see what happens next.