top of page

Architect Your Engine - II. Subsystems

  • Writer: Yi Yin
    Yi Yin
  • May 1, 2018
  • 4 min read

Updated: May 4, 2018


This article answers a question: why the class hierarchy of sprite / game objects in Unity and Unreal looks like what it is now?


In the previous article, I talked about complex systems. To design a complex system, like a game engine, we should 1) clarify the boundary of each subsystem, and then 2) establish minimum necessary connections between subsystems.


The most tricky thing is that step 1) actually requires comprehensive understanding of step 2): you must separate each subsystem in the purpose of minimizing necessary connections and eliminating unnecessary connections.


Problem

Now, let's say we are about to design a class representing enemies, and another class representing the player(s). We'll talk in pseudo code:


// Pseudo code

Class Player{

float x;

float y;

//...

}


Class Enemy{

float x;

float y;

// ....

}


Soon, Doctor Yi, the designer, tells you that he wants to implement two types of enemies: 1) static enemies that attack you without moving, like the Photon Cannon in StarCraft; 2) dynamic enemies that attack and move. Therefore, you set up your class hierarchy like below - this time, let's use a graph:

ree

Very well. Both Doctor Yi the designer and you are satisfied. That night, Doctor Yi has a dream. He dreams that one day, his four little children will live in a nation... Eh-hem, that's Doctor MLK.


Anyways, after the dream, Doctor Yi, the evil designer feels a little bit lonely, because, unlike Doctor MLK, Doctor Yi has no children, no wife, and no girlfriend. Therefore, when he wakes up, he decides that the game will NOT be as lonely as him, which means, the game must have a "multiplayer" feeling. What does that mean?


For you, it means that under the "Player" class, you must derive three more classes: FriendPlayer that represents your friend who will connect to your computer via network and play this game together with you; CompPlayer that represents an AI ally player so that if you have no friend, the AI will play with you; and SelfPlayer, which is literally you. Now the class hierarchy looks like this:


ree

That day, after work, you feel a little bit tired. You wish you could have an MLK dream but you end up with even more challenge. Doctor Yi the Yi-vil designer changes again: this time, under CompPlayer, he has designed two additional types of it: StaticCompPlayer that represents an AI ally player that does not move, e.g., a friendly fixed cannon; and DynamicCompPlayer that represents a moving and attacking AI ally player. So your class hierarchy now looks like this:


ree

Now, I think you should have already noticed the problem. If you haven't, you are not sensitive enough to architectural design. We have StaticCompPlayer, StaticEnemy, DynamicCompPlayer, and DynamicEnemy. It means, for the pair of StaticCompPlayer and StaticEnemy, you are very likely to write same code twice. The same thing goes for the pair of DynamicCompPlayer and DynamicEnemy.


This is bad. If you write same code twice, it also means if you update one of them, you have to hand-change the other. Normally, if you meet such issue, it reflects some problem in a deeper level of your design.


Solution

Let's rethink this challenge. Sometimes, two similar things may turn out to be completely different in underlaying mechanism; and vice versa. This time, let's start from the programming side.


A program takes an input from the user (or other output source), and gives the output through its computational processes. Therefore, the engine should not care about such semantic different as "ally" vs "enemy". The engine cares about two things: 1) how this sprite is visually represented; 2) how this sprite is being controlled.


Therefore, the updated basic separation should be as below:

ree

The Sprite class represents the blueprint of a graphic/visual object. The object may be of any visible type: a UI button, an animated ally/enemy, a static background image, a tree, a butterfly, etc. The Controller class is in charge of updating the sprite's geo, starting/stopping animation, etc.


For the engine, according to most of modern 2D APIs, they distinguish between two types of sprite objects: static sprites and animated sprites. Two reasons for that: 1) the input of the two types are different: a series of image VS one single image; 2) the possible controls are different: for animated sprite, there are some additional controls like "start animation" and "end animation". Therefore, we may derive two classes from Sprite class:


ree

(The current trend is that many 2D game engines begin to hope that static sprites and animated sprites could be unified in user level, but that's another topic.)


For the controller part, again, the program should only care about the input and the computational processes. According to Doctor Yi the Yi-vil designer, there are 3 ways to control a sprite: by keyboard+mouse (KeyboardController), by your friend over the internet (NetworkController), and by in-game AI (AIController):


ree

Now we've established the second version. A controller object takes input from all possible sources, and manipulates its related sprite object. Also, we can instantiate one or several controller objects from Controller class to control multiple sprites - for instance, in one game, the player may control different avatars similarly; or in another game, the player controls both her avatar and a "shadow" of that avatar in synchronization. In that case, you should allow your colleagues to "attach" a controller object (normally represented by a file) to a sprite object - and have you noticed that this looks familiar to you? Yes, this is what Unity and Unreal do. You attach a controller script to a game object to better manipulate the game object.


I hope, after reading this article, you have known not only how Unity and Unreal work, but also why they work in that way.

Comments


YI YIN
Austin, Texas

Personal Email - if you like me

*Please replace "_at_" with "@"

kthtes_at_gmail.com

Studio's Email - if you like our games

*Please replace "_at_" with "@"

286studio_at_gmail.com

©2017 BY YI YIN

bottom of page