Service oriented data driven game system

I have been building a service oriented, decoupled game engine loosly based on the principles of MVC (Model View Controller). My motivation to take this approach has been to avoid being forced to port my games whenever I choose to change the "view engine". The view engine could also be called the renderer. I have gone from Managed DirectX to XNA, OpenGL, WPF, Unity3D and SharpDX. I have realized that if I keep this up, I will never complete anymore projects. So far, one game, 4 years ago. And that was a very simple game.

(The basic concept on an decoupled / service oriented game framework can be read here.)

Of course, a part of it has to do with me being obsessed by building systems. On this long road, it has become clearer and clearer of how playing and thinking about games have distorted my perception of a game is. 

I am humble. My misconception is common knowledge, but it's easy to be fooled and drawn in by the beautiful pixels. It has has been an iterative process to arrive at this state of mind. The more I rebuilt and changed my engines, the more I realized how important the data was. Now, the system is much more about managing and distributing data to specific parts, than to render and shade triangles. The triangles are but a possible presentation of the simulation.

There is nothing but data. A game is just a system, simulating and mutating data. It's easy to fantasize in polygons since it represents a world we are familiar with. Data is a bit more abstract and dry. The conclusion is that games are subject to the same rules about data as a word processor or a case management system.

Data philosophy and strategy

Origins and endpoints of data

Data have origins and endpoints. An origin is when the data is created or generated. An endpoint is where the data will arrive. I find this important, since it determines the flow of data through your system. One could have all data as static variabled, but that creates dependencies and dependencies can make a system complex and inflexible. If there's anything I've learned from my regular job as a programmer, it's that decoupled systems with less dependcies are more flexible and easier to manage in the long run.

Supporting data

The origin of data is when the data is created or loaded. Supporting data would be data that is general for the game. It exists without a current game session. Supporting data could be lists of possible weapons, loot drop tables, character sheets, maps, etc, etc.

Simulation data

A game sessions is an ongoing simulation and all data, that is unique to that simulation is simulation data. The origin of simulation data is the start of a new simulation or when you load a previous simulation state.

Compartmentalized data for GameObjects

In Unity3D, you have something called GameObjects. They are part of a graph. I will use GameObjects as an example since they are well known. They are your typical node in a scene graph.

You can put properties on your GameObjects so they are exposed in the inspector, but putting them on the root object is a bad idea. They become tied to the GameObject and unwieldy. What you want is to have a container for the data. For example "GameObjectData" for an actor. That way, when you save your game, you can just collect all the data from all the GameObjects and serialize that in a tree or whatever structure you see fit. Remember to mark the type of GameObject the data was connected to. With that information you can create a GameObject of that type and assign/load the data.

There should be a clear distintion between data, game logic and rendering. The way I see it is that the GameObject is born from the data. The data is the parent. The GameObject can only borrows the data for the durarion of it's lifecycle. If the data for an GameObject is changed, it needs to be stored/synced to the GameObjectData. Examples of data that come from an outside system to be stored into GameObjectData is positions and velocity from the physics system.

Messaging and game engine services

Another important aspect of compartmentalized data is when you want to transmit the smallest amount possible. There are many reasons for this, but it's most clear when it comes to a server client scenario. You typically want to lay as little load on the connection as possible to reduce bandwidth usage and lag. The less data you have, the less time to serialize, deserialize, encrypt and decrypt.

The data delivery is structured around a message system. The message system is responsible for delivering the right data to the right service. Examples of services are AIService, PhysicsService and AnimationService.

Client and server layers

The system has also been broken down into two logical layers. Server and client. The server layer is responsible for the origin of simulation data. The client layer is responsible for input data, such as velocities of players and commands from the UI. These layers exist even if you're running a single player game, they're just on the same machine and the messages don't pass through a network protocol.

Both the client and server contains the most of the supporting data, since that data is bundled with the game. So if you want to connect to someone elses server, the simulation data for that session is transmitted from that server to you. 

If you run a single player campain, the client and server exists on the same machine. In that case, there's only one physics system since there's only one machine. A remote machine would have an own instance of the physics system to simulate movement between updates from the server.

Conclusion

Realize that a game is just visualized data. Nothing more. Treat the view as a pure visualization of data. Nothing more. The moment you take on a bigger type of game, you will get so many benefits out of putting the data first. Your system will be easier to tier and structure, easier to port to new tech and platforms. Encapsulate the whole game/simulation in data and never have any dependencies to the view.

Imagine and draw how the data flows. And how it ideally should flow =). Create services/components that take the data and create the the visual elements (meshes, etc) that represents that data element. Create services that inputs data into the simulation (keypresses, etc, etc).

And, use your best judgement. Nothing is written in stone.