The Unity RTS project has been progressing nicely, have the basic camera movement / unit selection and movement in place.
I’m at the point where I need to start thinking about some of the game’s metadata and plan out how the integration is going to work. Before we get started however, let’s dive under the hood a bit and take a look at what this means.
As I have detailed in previous posts, I am a big fan of having API layers for game metadata. Note that this does not necessarily mean having a live network-accessible API server (although it could), but could be represented by a static metadata layer that is effectively ‘baked’ into the game and loaded at runtime.
Game Metadata – a Quick Overview
To summarize what I mean by game metadata, let’s quickly go through the basic structure.
Any game has effectively 2 types of metadata:
- Dynamic Metadata
- Static Metadata
Dynamic metadata is stats created by the player as they progress through the game. This is commonly called the ‘Save Game’ or ‘Player Settings’ in a traditional game. I choose to use the word metadata because it keeps the terminology consistent from player-generated data and ‘game’ data.
Static Metadata (in comparison) is the game data that is required to execute the game. This is things like unit strengths / weapon stats / mission information and so on. You can include text in the game such as localization files in this category as well.
Both types of metadata are similar – we have an in-game structure (or class) that stores the particular information about the specific object that we want to populate when the game launches. Additionally in the case of dynamic metadata, the data structure is updated as the game executes in order to reflect the player’s current progress in the game.
Sounds simple, right? The question that we are faced with is: how is this metadata represented in a game, and how do we access it from our game code to produce the final game that is presented to the player.
The first thing that I typically do when building out systems like this is focus on what I call the ‘communication layer’. We can view the overall game system something like this:
While simple, the diagram above shows us the 3 primary layers in any game:
- Game Data
- API / Communication Layer
- Game System
The API / Communication Layer provides the interface for the game system to access the underlying game data.
In certain types of games, the API / Communication layer could represent communication to an external connection to a network-enabled service (ie an actual API), or it could represent local access within the game’s data.
However we choose to implement the backend metadata store, I find the simplest way to get started is by defining the interface first. Once we have the API defined, we can work forwards (towards the game systems) and backwards (towards the metadata store) independently. Depending on your team size, the back-end systems might be handled by server developers (working in Php / Java / Python etc) and the front-end game systems are implemented by the game engineers in Unity c# code.
The API provides the standardized access point, and both teams can work safely knowing that the contract between their work has been defined.
When designing data objects for a system like this, we often think of the objects in terms of the operations that need to be applied to them. A common term for the standard operations is called ‘CRUD‘ – Create, Read, Update and Delete.
From Wikipedia’s definition:
The acronym CRUD refers to all of the major functions that are implemented in relational database applications. Each letter in the acronym can map to a standard SQL statement, HTTP method or DDS operation:
Operation SQL HTTP DDS Create INSERT PUT / POST write Read (Retrieve) SELECT GET read / take Update (Modify) UPDATE PUT / PATCH write Delete (Destroy) DELETE DELETE dispose
Although a relational database provides a common persistence layer in software applications, numerous other persistence layers exist. CRUD functionality can be implemented with an object database, an XML database, flat text files, custom file formats, tape, or card, for example.
Sounds exactly like what we’re building, does it not? Well then ;}
Game API for RTS Pack
Now that we’ve gone through ‘how & why’, let’s start to look at the API that we’ll require for our RTS game system.
In it’s most basic form, our RTS has a couple of core systems / objects that we will require both static & dynamic metadata for. We can begin defining our API by looking at these objects.
- The Player (user)
- Units (unit)
- Buildings (building)
If we apply our ‘CRUD-dy’ design principles from above to these 3 object types, we get something like this:
- user/[id] – retrieve a particular user’s information
- user/create – create a new user
- user/delete – remove a user
- user/update – update a user’s information
- unit/[id] – read a unit’s metadata
- unit/create – create a new unit
- unit/delete – delete a unit
- unit/update – update a unit
- building/[id] – read a building’s metadata
- building/create – create a new building
- building/delete – delete a building
- building/update – update a building
When looking at API systems like this, we often have to consider how to handle a single object (as we have defined above), but also how we retrieve a list of objects. To handle lists, we simply add the plural form of the above entry points.
So in addition to the ‘user/unit/building’ endpoints that we have above, we add the plural, like so:
Typically we aren’t going to update a whole set of units / buildings or users at once, what we will do is call one of the ‘plural’ endpoints as defined above, which will retrieve us a list of individual id’s that we can use to update the individual entries as necessary.
The only exception that I’ve added above is the ‘/users/search’ endpoint, which would be used to search for a specific user id (or user name potentially) by the player in-game.
For a true ‘RESTful’ API, you might not even have the ‘get’ endpoint, but simply calling /users with an HTTP GET verb would retrieve a list of users. Because we don’t know what the implementation of our API is at this stage, I’m going to stick with more explicit endpoints that define the verb implied underneath.
So, at this point, we have all of the endpoints that we need to retrieve a list of users, units and buildings, as well as the individual information about each of the respective elements.
Now, one thing to clarify with our above example endpoints – the /users and /user endpoints are what I have defined above as ‘dynamic metadata’, where the /unit, /units, /building and /buildings endpoints are static metadata – namely the player(s) are going to generate dynamic information for the user endpoints, while we (as designers) are going to generate the metadata for the /unit and /building endpoints.
Pretty straightforward, yes?
As the image at the top describes, we have 3 layers to our architecture:
- Game Systems
- Game Data
We have defined what endpoints we require for our API, but haven’t really gone into any detail about what parameters we will pass are, and what gets returned by the associated endpoints. This is what we need to do next.
Most modern REST-style API’s use JSON as the format for the data transport. JSON is a very simple hierarchical text-based format that is easy to parse both on the client and the server in any language.
For my first-pass API, I’m going to implement everything in C# directly within Unity. If I choose to add a network API layer afterwards, the client-side implementation for reading the API results won’t change, just the transport layer for retrieving the JSON over the network will.
Stay tuned for the next post where I will demonstrate how this is all implemented in Unity!
Until then, keep developing!