Using Core Data to Power KOZMONAUT

Whenever I begin a new project, I find ways to expand on what I've done in the past and use different frameworks I have little to no experience with.

In phobium, the loot system, news system, bounty system, and daily/weekly Store and Level rotation is powered by a combination of UserDefaults and a hosted JSON. The call to retrieve the JSON is a simple network request and then subsequent parsing. It contains all of the data for the week and is intended to be cycled/updated on my end every week.

With KOZMONAUT, I wanted something more robust and flexible, and the answer to that was both Core Data and CloudKit.

With phobium, my loot system was extremely basic.  There were only so many loot items that could be obtained, and they would only go into certain slots within the Inventory screen. If I ever decided to add more loot items, it would be a huge undertaking. Basically, there are True and False type values assigned to each loot item I made (i.e. Do you have the Epic Health Reverie?).  Not very flexible.

With Core Data, I am able to create the database model and incorporate possible values for each bonus of an item, the name, the price, the icon, the rarity, special effects, and more. When a loot item is going to be rewarded, either via RNG from vanquishing a monster or boss, or from a quest reward, I can randomly generate all properties of the loot item or set it statically for quest rewards. Then, it goes into the database and when the Inventory screen is pulled up a query is made to fill in all of the obtained loot. Much more flexible!

With CloudKit, this is taken a step further, and the Core Data database will be hosted in the player's iCloud account, meaning that they can switch phones and still have their loot accessible. This is a great tool from Apple to give that feeling of player data being saved in a server. This also gives me the flexibility to assess if it makes sense to port to other platforms such as iPadOS and macOS.

The Quest/Daily Bounty system works the same way. Bounties (called Missions) in KOZMONAUT are saved to their own database as well, and have properties such as objective, name, reward, and progress. In the game when completing the objectives of a Quest or Mission there is a ticker which shows progress and periodically updates the database item with the new progress. Again, this gives me the flexibility to easily add new types of quests and missions and not have to hard-code each possible bounty or quest unless it's a completely new type of objective and not something like Boss Vanquishes, Monster Vanquishes, Perfect Waves, Spacedust Collected, or Cosmic Elements Collected. These are all scoped down to individual planets as well to make sure they all get some kind of play. Planets in KOZMONAUT are the equivalent of levels and they have sub-levels that are different modes (Casual for lazy-tapping and Mine Defense as a high-risk, high-reward skill-based mode).

KOZMONAUT is written using a combination of SceneKit and SpriteKit. SceneKit is Apple's first party 3D rendering SDK and I prefer it mainly because it forces me to figure things out since the community and documentation isn't quite as plentiful as Unity and Unreal Engine. The download sizes are also very, very, very low compared to equivalent games made with third party engines.

When making queries to the Core Data database within the game itself, there was random crashing. I've found that when my games crash randomly and Xcode gives the generic error with the hexcodes deep in the SDK it is a thread/memory issue.

Sure enough, that's what it was.

It's important that Core Data runs on the main thread because of possible data corruption. You do not want two threads interacting with your database simultaneously. Core Data, on its own volition, on the main thread sometimes does not happen when SceneKit is hogging up the thread on its own. In the future, I will be off-loading Core Data work from the gameplay altogether and utilizing a queuing system which will hold changes to be made into the database to update progress outside of the game. I will utilize UserDefaults to hold on to those changes until next menu load in the event of a crash as well, to make sure no progress is ever lost.

Here is an example of how I force all Core Data operations into the Main Thread.

    let appDelegate: AppDelegate
    if Thread.current.isMainThread {
        appDelegate = UIApplication.shared.delegate as! AppDelegate
    } else {
        appDelegate = DispatchQueue.main.sync {
            UIApplication.shared.delegate as! AppDelegate
        }
    }
Ensuring Core Data is accessed from Main Thread

AppDelegate being where the persistent store is declared, or in my case the NSPersistentCloudKitContainer because it's syncing with the user's iCloud.

CloudKit also lets me save UserDefaults in the same fashion, where game progress and more can be saved across devices or retrieved in the event the player loses their device or uninstalls the game and comes back. This is basically turning UserDefaults into NSUbiquitousKey which is the synced version of UserDefaults but still operates as a key-value dictionary.

Finally, CloudKit also offers Public Database functionality, which means I can update News items to be shown to users with announcements or patch notes and reach out to them directly through the game (but nothing annoying like Push Notifications!).

I can also control the Exchange, which is a Vendor system that allows users to buy Loot Items for in-game currency in case their RNG is unlucky. With CloudKit, I can add in new static loot items and set the price and bonuses. I could also potentially have sales and promotions to make them more powerful or cost less.

As of now, to keep things as automated as possible from my end, all potential store items are loaded into the Public Database but have an availability property coinciding with days of the week for when they're available to purchase. This allows me to have a timer in game that shows when the next Stock refresh will be and players will hopefully feel an urge to check in daily to see if any upgrades are available.

All in all, the systems in place for KOZMONAUT's back-end are much more refined than phobium, and more importantly I've learned a lot in terms of thread safety with Core Data and building a much improved loot and inventory system.