I'm writing this after a follower on Mastodon had trouble understanding a thread I made about this. Basically, I was comparing Unity's paradigms to Godot's. Unity is a very popular game engine, particularly among indie developers. Godot is a similar engine, but open-source. It seems to be less popular, but has definitely been a common topic in my YouTube recommendations.
I thought the difference was interesting enough to talk and write about, but this is definitely an area where terminology can make it difficult to follow. This is an attempt to provide enough background on the “problem” of games and game engines, and provide a basic description of how these two engines differ in the paradigms used.
I am assuming very little mathematical, physical, and computer science knowledge of the reader. I hope this is readable even if you flunked geometry, didn't take physics, and never have programmed a computer in your life. If you already know the subject of a section, you can move on. Or not, I'm not a cop.
I have only read the Godot documentation, I have not put it into practice yet. This may mean that I have too rosy a picture of it. We'll see, I plan to use Godot for my next project.
What Is a Game?
You might have noticed scare quotes around the word “problem” above. They are there because one might question how games are a “problem”. They're supposed to be fun, after all! But in software design, we often refer to the goals of a piece of software as a “problem space”. For example, the problem space of a word processor can be described as allowing a user to enter, edit, save, and print formatted text. This section describes the problem space of games.
A video game, at its most general, is a computer program that aims to provide an entertaining experience to its user or users (a player or players) through an interactive simulation of a world. This is possibly an over-broad definition, but I'm choosing it because it most accurately represents what a game provides to a player. It may even be a non-exhaustive definition, because I haven't thought of one, but I bet there's an exception out there. We could pick apart this definition, but for now, I'm going to move on.
What Is a Game Engine?
The important part to notice about the above definition is the “interactive simulation of a world”. This is ultimately the most major subset of the problem space that a game engine attempts to solve. That is, a game engine provides an user interface for instantiating and defining the (possibly) interactive simulation of a world. I say possibly, because while a game engine is definitely interactive to a developer, it may not require input on the part of the player. You may also notice that I'm skipping over the “entertaining experience” part. This is because game engines can be used for much more than games, and I've used Unity for business software before.
But what does it mean to “simulate a world”?
In short, a world describes a space of positions and a set of positioned entities within it. A simulation will instantiate, destroy, and update these entities as the simulation runs. This may feel more like a problem of physics, but to be honest, that's all a game is.
You may quibble that a game may not need to have a set of positions defined in physical space. But you're jumping ahead. Yes, the space does not have to be the traditional Euclidean geometric world of Newtonian Physics. But a game will have a space. A text adventure has an abstract map of locations and their connections. Even a board game could be reduced to the space of connected graphs. It's all math, baby!
In general, however, the world that is simulated by games is a traditional Euclidean world of Newtonian Physics. Usually in three dimensions, but it can also be in two dimensions. (Maybe one day four or more dimensions?). Probably worth describing this Euclidean world of Newtonian Physics.
The World as Described by Euclid and Newton
A Euclidean world is a space where the geometrical rules of Euclid apply. It's too deep a subject to dive in deeply, but it starts from a definition of a point in space, defines lines and line segments between points, angles between those lines, and so on and so forth. It can be a two-dimensional, or a three-dimensional space, or honestly even higher dimensions. But games tend to use two or three. This discussion will assume three.
For the purposes of math it can be understood as setting an origin point in the space. This position is assigned three numbers, in particular, zeros. Each number represents the distance along three orthogonal axes (singular axis) that intersect that point. Orthogonal is complicated, but you can understand it as at 90-degree angles to each other geometrically. Traditionally, these axes are called the x-axis, y-axis, and z-axis.
The three numbers together are called a vector, with x, y, and z components. Any position in the space can be represented as such a vector, called the coordinates. If I'm at the origin (0, 0, 0) and I move 1 unit along the x-axis, I have moved to (1, 0, 0). If I then move 3 units along the y-axis, I have moved to (1, 3, 0). You can move fractional units, and even “negative units”, so if I moved 2.5 units along the negative z-axis after that, I have moved to (1, 3, -2.5). Units are arbitrary distances, but it's often useful to use meters as the units.
That's Euclidean space. Newtonian Physics builds off of that to describe how point masses move and interact in Euclidean space. Basically, given a point mass at (1, 3, -2.5) with a velocity per time unit of (2, 2, 2), it will calculate that it will be at (3, 5, -0.5) on the next time unit. Time units are also arbitrary, but seconds are usually most useful.
If it hits another object, that object will have force applied to it causing it to accelerate, and the original object will lose momentum causing it to decelerate. It covers a lot of basic models for understanding objects we might encounter on Earth and even in Outer Space, and is much too broad to get into here.
Interestingly enough, the real world is neither Euclidean or Newtonian, which was Einstein's big discovery. Gravity bends space throwing off the calculations. At a small enough level, atoms and smaller particles do not act Newtonian at all, and then when you get into quantum levels, it gets really complicated. But within games, we usually stick to these well-tested and familiar rules. This is even true for physics simulations, because while we know reality is not Newtonian, the effects at the size of everyday objects in the gravity well of the Earth are close enough to the calculations to be useful.
Now while your head is swimming, I'm going to talk about entities, which you have probably forgotten was in the definition all the way up above.
OK, then, you may quibble that a game may not need to have entities. Which I guess is true, but then it's a pretty static simulation of nothing. It may be useful for meditation, but not so much for games. Entities are what make a simulation interesting.
Entities in games have two guaranteed things:
- A current position in the space of the game
- A set of rules defined in code that explains how to update the entity.
Now, the rules technically don't have to be defined on the entity itself, they can be part of the main logic of the game program. But it's a useful place to define them, nonetheless, because it matches how we think of the problems more.
Entities include things like the player and non-player characters, projectiles, particle effects, that is, representation of physical objects in the world. But entities may also be abstract and external to the world, like the music that plays in the background, or a score display. Or most importantly, the (main) camera, which represents the final view that will be rendered to a player. OK, that's probably enough definition, but I need to describe a design paradigm that Unity and Godot both follow.
The Entity-Component System
A common design for game engines, and the one used by Unity and Godot is called the entity-component system. It adds another thing that entities have called components. A component is basically the set of rules for updating an entity described above but abstracted in such a way that it can be shared by entities. Components tend to have accessible parameters that alter behavior and can be set at design time or run time.
This allows you to reuse common behavior between individual entities while customizing which behavior an entity needs. For example, a player character entity could have a camera component that records their view, an input component that updates the entity position on input, a mesh component that is drawn to represent them, and so on. A non-player character would not need the camera component or input component, but would have an AI component that updates their position and the mesh component. Both characters can have a vital stats component that represents their hit points, and can be modified by entities and components to represent damage or healing they take.
A Hierarchy of Entities with Components
As the simulation of a world gets more complex, it is useful to place entities in a hierarchy of entities. For example, rather than having sibling player character and player gun entities that you both need to keep track of, it can be easier to have the gun entity be a child of the player entity.
This hierarchy does not have to be static. If the player drops the gun, you can remove the gun as a child. If a player gets into a vehicle, you can make the player a child of the vehicle, and then remove them as a child when they get out.
There is another benefit of this system as far as physics calculations go. It allows you to define a reoriented space around the the position of the entity represented by what's called a transformation matrix. This allows you to describe something in the entity's local space, such as 5 meters in front of it, and then calculate what that set of coordinates in would be in the world space. You can also nest this down even further, arbitrarily deep. Performing these complicated calculations is one of the things a game engine performs.
Components can similarly be altered over time, perhaps changing parameters, or even destroyed, created, or moved to other entities. For example, if a character catches fire, you can add a fire component to that entity that lowers health and renders a flame around the entity. Once the fire dies out, the component can be removed.
How the Entity-Component System Is Abstracted by Unity and Godot
OK, that's the last of the preliminaries. Now I'm going to talk about the meat of what I wanted to discuss, how this entity-component system is abstracted in both Unity and Godot. Note that capitalized terms in these discussions represent specialized terms within their respective engines.
Let's start from the top. In Unity, an initial world of entities is defined in a Scene. That is, there's a shared space, and a list of entities in that space. This can be roughly thought of as a level or area of a game, and a game may move between then. For example, if the player character walks into a house, you can move to a separate house Scene from the overworld Scene, and then back out.
Scenes also may be less specific areas of the game, such as a battlefield Scene that is moved to when combat happens, or even non-world “areas” such as a menu Scene that is moved to when the game starts up.
In Unity you have one current main Scene, but you can also additively load a Scene to another. For example, rather than having the interior of a building loaded all the time, or having to unload the overworld Scene when the player enters it, you can additively load the interior Scene when the door is opened, and then unload it when the player leaves.
Scenes can also be used to separate actual game development work, having one group of people working on one Scene for one part of the world, and another group of people working on another Scene for another part of the world, and then connecting those two different Scenes during gameplay.
Game Objects and Components
The entities are referred to as Game Objects, and their position and orientation represented in their transformation matrix, which is handled by the required Transform Component on it. Easily enough, components are just called Components in Unity.
Other Components, both defined by Unity developers, and by a game developer can be added to Game Objects to describe further behavior. A Component is defined through a Script, which is a C# class.
For example, A Sprite Component can be added to a Game Object representing a non-player character, and you can write your own AI Component to update the Sprite and Game Object.
Game Objects can be bespoke entities defined in a Scene, but often, we want to be able to create many instances of equivalent Game Objects. Rather than placing every single monster in a scene separately, you can define a monster Prefab, which can be instantiated as a Game Object many times. Perhaps a spawn point is defined, and each new monster Game Object is instantiated there from the monster Prefab as the player plays the game.
Putting It All Together
So In Unity, you have files that represent Scenes, that have a set of Game Objects, that have Components. Components have their behavior described and implemented by Scripts, which are the code for that component (C# classes implementing certain methods that will be called by the engine). Game Objects may be defined in a Prefab, which can be used to instantiate many instances of the same Game Object.
Godot is very similar to Unity in its design, but uses a simpler system of just Scenes and Nodes, which combine and mix some of the objects described up above.
Scenes in Godot are very similar to Unity Scenes, but the entities in a Scene are represented as Nodes, with a single root Node at the top. We'll get to Nodes in a bit. Scenes in Godot can also be additively loaded, so the root Node of another Scene can be loaded into the current scene.
A Node, including the root Node of a Scene, are the entities. They have a position in space (it's not a separate component like in Unity), and a list of child Nodes.
However, the child Nodes of a Node are also the components of the entity. A Node can be specialized by the Godot developers or a game developer in this sense. Each Node may have a Script attached, which is a C# class describing its behavior.
For example, an abstract non-player character Node would represent the entity, and have a specialized Sprite Node that will be drawn to represent it, as well as a specialized AI Node written by the game developer to update the parent Node and Sprite Node.
Godot does not have the concept of Prefabs like Unity does. Rather instantiating a set of entities can be done by loading another Scene into the current Scene. A Scene used like this is referred to as a Packed Scene in the code, but it is just a Scene. So for a level of a game, you would have a Scene for that level, that will load another Scene's root node (perhaps a Scene for a monster) as an entity.
Putting It All Together
Stepping back, you can think of a Scene in Godot as defining a root Node with specialized child Nodes with their own specialized child Nodes. One Scene's root Node will be loaded as the current scene, and other Scenes may be loaded to instantiate Nodes in that Scene. Nodes are defined by Scripts, which is just code in a C# class.
Comparing and Contrasting
In Godot, there is no separation between entities and components like in Unity. The Godot Node serves as both a Unity Game Object and Unity Component.
Additionally, there is technically no true separation between a Godot Node and a Godot Scene during run time, because each Godot Scene defines one root Node and its children. A Godot Scene, then, can be understood as a predefined Node that can be designed during development, and loaded and instantiated at run time.
In comparison, Unity maintains the separation between Scene, Game Object (entity), and Component. Scenes have one or more child Game Objects, which have one or more Components, as well as their own child Game Objects. Predefined Game Objects can be described in a Prefab during development, and then loaded and instantiated at run time.
There are arguments for both systems that I can think of. Usually, in object-oriented programming that it's beneficial to be explicit about what would be a world, and what would be an entity, and what would be a component, even if they can be abstracted as the same thing. This is mainly because they have distinct purposes, and the different name and type helps a developer understand how to use it.
On the other hand, I have often found the distinction in Unity to be very muddy in reality, to the point I think abstracting them as Godot does can make it clearer. In Unity, a C# class represents a Component. Components are always attached to Game Objects, and the attached Game Object can be accessed and manipulated. But in all cases, except for the default Transform Component, you have to query for a particular Game Object's Component and operate on that. And that code goes within the Component that you attach to the Game Object that you define in the Scene. It's somewhat circular.
Godot on the other hand reduces both the distinction between entity and component as well as scene and entity to just Nodes. Nodes have Nodes, which define position and behavior (no separate Transform Component), and you can query and interact with other Nodes on other Nodes. You load Packed Scene as the root Node for the current scene, which may load other Packed Scenes as Nodes for entities. And so on. The code for each Node is defined as a C# class. The circularity is removed, because it's Nodes all the way down.
The real reason this interests me is because I've found moving between Scenes in Unity to be tricky, as well as maintaining the state of Scenes that were previously visited. You end up making a singleton game manager Game Object that you pass from Scene to Scene, and you have to be careful to not create a competing one. I feel like in Godot, you can maintain the game manager as one of the children Nodes of the current root Node, and then load other Nodes as siblings. Well, I'm going to test that out, and I'll be sure to let you know.
That was a lot, but the basic idea is that Godot reduces Unity's system of four types of objects into two, which can really be understood as reducing it down to one. It's an interesting difference, and you may hopefully have enough knowledge to decide which you prefer.