Sometimes an education is a bad thing, like when you commit to a framework and then learn about the existence of a possible other way that might help make things easier.
In Project Universe, I have a lot of data. The whole movement and NPC thing I’ve been dealing with has been ancillary to the fact that the game is about numbers: how much you buy, how much you move, how much you sell, how much you earn. The smallest bit of data, then, is the commodity item (so far). This is represented by a data structure that serves as a bucket for values like item name, weight, rarity, and base value. If we walk up the hierarchy chain of how the data is used, a step above this is the shop definition data, then station definition data, and finally sector definition data. All of these elements are non-dynamic, meaning that once they are defined, they cannot be changed (water filters aren’t going to change into gold ore no matter what actions the player takes in the game).
Right now the definition of these elements is represented as values assigned to class instances, which are then saved to List<T> instances inside a singleton data controller. This makes my entire “database” available anywhere in the game at any time to anyone who needs to look up the properties of an item or the name of the only station in the current sector. I thought this was pretty clever, since at one point I had this data in CSV format, exported from Excel, and loaded into the game when the player clicked the NEW GAME button. My in-memory database saved a step (the loading) and was easier to manage by having all of the data in plain text in more or less a single massive file.
Yesterday I learned about ScriptableObjects, or rather had ScriptableObjects clarified for me. I had heard of them before, but I had apparently believed they were only interesting to those who were extending the Unity editor by creating their own editor panels, and didn’t serve any greater function. I love and appreciate the fact that Unity allows you to create your own editor panels, but I was having enough trouble learning Unity to bother diverting my attention to another scripting paradigm to investigate ScriptableObjects and what they could do for me.
In short, ScriptableObjects are…it’s bad form to say this, but physical data objects in the project. Technically, anything you append to Unity’s Project bucket is a data object (colloquially referred to as assets), but SO’s behave kind of like the entire structure of how I’m setting up my in-memory database. For example…
We have an item, say the above mentioned water filter. This data object is represented by a class called CommodityObject. We instantiate a new copy of CommodityObject and set it’s name (“Water Filter”), weight, base price, rarity, and so on via the class constructor. That instance is added to a List<CommodityObject> called CommodityObjectMasterList, and that list is exposed through it’s parent class which uses the singleton pattern. When I need to access the water filter object and it’s data, I can query the CommodityObjectMasterList for a CommodityObject whose name equals “Water Filter”.
What you don’t see is that the singleton script is attached to a DataController empty game object which has the DontDestroyOnLoad() directive invoked so this data barge persists across multiple scenes. That means there’s a game object with a singleton component attached that contains the definition of a generic List<T> that holds any number of instances of CommodityObject. All of this is relatively “black box” in that if you want to change the base cost of a water filter, you need to edit the script that defines the object, scroll to the commodity definition section, locate the instance of “Water Filter”, and modify the property for base cost.
Scriptable Objects can operate mostly the same way. I’d define the CommodityObject as a class that exposes the properties I need. I would then create a Scriptable Object which exposes a List<CommodityObject> property. This becomes my “database”, and will be saved to disk as a .asset file. Technically, I could then whip up some kind of behind-the-scenes population which generates items, names, and values, but the real strength here is that I can use Unity’s editor extensiblity to craft a UI, dock it within Unity itself, and edit my CommodityObject database in a nice UI whenever I need it.
When I need to access this data in the game, I could technically rely on the singleton method, although that seems optional at this point. Instead, I could use a SceneController paradigm and load up my item database from the .asset file on disk, expose that resulting database as a public property, and access whatever commodity item I need at the point when I need it. If resource loading and saving each scene is a concern, then I could load the data from disk at the start of the game using the singleton pattern and DoNotDestroyOnLoad() in a DataController paradigm and have all data available at all times.
The bottleneck right now is learning how to extend Unity to make the UI I’d need to manage this functionality. I found a pretty good but very basic example of SO’s and management UI that I’m going to study, and then will have to deviate from the main project to create a side-project where I can work on my UI crafting skills. The good thing about Unity is that once I have something, I can export the bits I need and import them into my main project. I’ll probably branch my Git project for this specific aspect, as I will need to make sure the SO methods can do everything my original database design can do before I commit to it.