This week, I pretty much broke my whole unity game down… to rebuild it better! Unity3D Engine design musings inside.
Unity3d Problems
I built most of my game following the Unity3D official tutorials, treating them as sort of “best practices.” Until I realized they were kinda bad practices actually. The idea of independent, self-managing game objects is great for small game prototypes, but breaks down quickly with the slightest bit of complexity.
Cue conflicting responses to key pressers, GUIs stacking on top of each other, and plenty of errors because something that other things depended on hasn’t been initialized yet. Mmm, fun!
(Before I get started, gotta give credit to DevMag’s article on Unity Best Practices for a lot of good tips and inspirations)
Game Programming Refactoring
So I basically broke my whole game down and refactored the crap out of my code, giving my codebase a structure but maintaining flexibility and reusability. But I didn’t want to re-implement my own complete Engine in the background that uses GameObjects as just rendering/physics avatars, as then I would lose many advantages of Unity (like test-play-in-editor from any point, or automatic serialization). I’m not sure if my new approach is gonna work, but I think it’s a good baseline.
Here’s how it works in a nutshell: I have a persistent GameController singleton for high level stuff (Return to Main Menu, Quit To Desktop, Log/Config etc.), that spawns a persistent GameData singleton and has a bunch of self-sufficient “System” components (like ConversationSystem, QuestSystem, GameVariableSystem etc.). Each of those systems is strictly LOGIC ONLY, but adds its own Data Component (i.e. QuestSystemData) to the GameData object as well. Finally, each Unity Scene also has a non-persistent SceneLogic GameObject singleton (with its own GameSystem components) that controls the actual gameplay in that scene. All these are of course polymorphic so custom game(modes) can override some functionality.
So when I drop into any Scene at run time, GameController is automatically created to handle high-level functionality, but it’s the actual SceneLogic that controls the whole gameplay, calling/creating various GameSystems (and their Data) only when actually needed. The SceneLogic and Systems get flushed between scenes, but the GameController and GameData always persist with the whatever data the GameSystems added to it.
The benefit?I can have completely different game modes between Scenes that still share common functionality and remember data across levels to effectively “link” everything together. I can reuse Systems in different games in the future. I can jump into any scene from editor and play it. Even if the player transitions to a scene that uses a completely different game mode (different logic/systems), when returning to the old one, all the needed info is still available. And the GameData object itself can be neatly serialized to implement the basis of Save Games. Huzzah!
Game Menus
With all this new structure in place, I’ve been working on the overall “packaging around the game” with menus – inventory screens, pause menu, the startup screen etc. All working properly with menu and scene transitions, different game modes in different scenes, persistent data between level loads, and no logic or input conflicts!
I also added an in-game, color-coded Development console to help testing standalone builds later down the line.
There’s something really rewarding about starting and quitting your whole game via proper menus, rather than the Unity Editor :)
Next Week Plan
I still need to finish some of the refactoring and menus stuff, and add the bare-bones plot with an End-game screen. At that point, the game will be finally playable from A-to-Z, and I can start an agile development with iterations, woot!
Intrigued? PLAY KARASKI NOW!
Or Follow at
Karaski: What Goes Up... is a an open-ended "Who-Dun-It" mystery adventure onboard a sabotaged 1920s Airship where the player is as likely a culrpit as the suspects. Deus Ex meets Clue with a bit of Telltale!