From f1184207dcea3bfc7f3b52dd7721f58ead6a0c4f Mon Sep 17 00:00:00 2001 From: Evan Hemsley <2342303+ehemsley@users.noreply.github.com> Date: Tue, 21 May 2019 17:49:04 -0700 Subject: [PATCH] rebuilding site Tue, May 21, 2019 5:49:04 PM --- public/404.html | 18 +- public/categories/index.html | 252 ++++++++- public/index.html | 252 ++++++++- public/index.json | 49 +- public/index.xml | 46 ++ public/sitemap.xml | 36 +- public/tags/index.html | 252 ++++++++- public/why/architecture/ecs/index.html | 562 +++++++++++++++++++ public/why/architecture/hyper_ecs/index.html | 560 ++++++++++++++++++ public/why/architecture/index.html | 271 +++++++-- public/why/architecture/index.xml | 50 +- public/why/architecture/mess/index.html | 548 ++++++++++++++++++ public/why/architecture/oop/index.html | 555 ++++++++++++++++++ public/why/index.html | 256 +++++++-- public/why/index.xml | 4 +- public/why/the_hard_way/index.html | 540 ++++++++++++++++++ public/why/the_hard_way/index.xml | 15 + 17 files changed, 4080 insertions(+), 186 deletions(-) create mode 100644 public/why/architecture/ecs/index.html create mode 100644 public/why/architecture/hyper_ecs/index.html create mode 100644 public/why/architecture/mess/index.html create mode 100644 public/why/architecture/oop/index.html create mode 100644 public/why/the_hard_way/index.html create mode 100644 public/why/the_hard_way/index.xml diff --git a/public/404.html b/public/404.html index 9cd7b0b..bdcce24 100644 --- a/public/404.html +++ b/public/404.html @@ -9,16 +9,16 @@ 404 Page not found - - - - - - - - + + + + + + + + - + + + + + + + + + + +
+
+
+ +
+
+ + + + +
+
+ +
+
+ + +
+
+ +
+ +
+ +
+ +

+ + ECS +

+ + + + + + +

ECS stands for Entity-Component-System. It is based on two fundamental principles:

+ +
    +
  • There should be complete separation between data and logic.
  • +
  • Objects should be created via composition and not inheritance.
  • +
+ +

Components are the most basic element. They are simply containers of related data. In a 2D game we could have a PositionComponent with an x and y value. Components do not contain logic, though they might have callbacks to deal with side effects, for example creating or destroying bodies in a physics simulator.

+ +

Entities are generic objects, which have a unique ID and a collection of Components. Entities have no inherent behavior or properties of their own, but are granted these by Components. We can add, remove, or modify Components of Entities as necessary during the simulation.

+ +

Systems are responsible for reading and modifying Entities and Components. For example, a MotionSystem might look at Entities that have both a PositionComponent and VelocityComponent, and update the PositionComponent based on the information in the VelocityComponent.

+ +

Notice, in our above example, that this gives us a tremendous amount of flexibility. We can completely remove the VelocityComponent from an Entity while the game is running, and the Entity will simply stop moving - nothing else will break, because nothing has to rely on an Entity specifically containing a VelocityComponent! This is the power of composition and de-coupling at work.

+ +

Unfortunately, ECS is not without its problems.

+ +

Suppose we have a game where the player character can move left or right, and also has a special ability that lets them teleport forward over a short distance. We could put all of this logic in the same System, but most Entities that move around are not going to have this behavior, so it makes sense to have a separate system. In other words, we have introduced multiple systems that need to manipulate the position of objects.

+ +

We could have a TeleportSystem that immediately sets the x and y value of the PositionComponent so it is in front of the player’s current position. But if the TeleportSystem runs before our regular Motion System, the Position Component will be overwritten by the regular Motion System and we will not see the teleportation behavior occur, even though the system is running!

+ +

This kind of bug is known as a race condition. Race conditions are one of the worst kinds of bug to handle, because it is incredibly hard to trace values being over-written by other values. In my experience, race conditions are probably a good 80% of what game programmers deal with. No wonder we are so frustrated all the time!

+ +

In my opinion, being forced to think about the order in which Systems run is a form of tight coupling, because it means you have to consider the behavior of other systems when writing a new system to avoid introducing bugs.

+ +

This example also illustrates another problem with ECS: in Systems that have similar behavior, we don’t really have a nice structure to share that behavior.

+ +

I have created a variant of ECS that intends to address these and other problems. Let me tell you about it.

+ + +
+ +
+ + + +
+ + +
+ + + +
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + diff --git a/public/why/architecture/hyper_ecs/index.html b/public/why/architecture/hyper_ecs/index.html new file mode 100644 index 0000000..5a755aa --- /dev/null +++ b/public/why/architecture/hyper_ecs/index.html @@ -0,0 +1,560 @@ + + + + + + + + + + + + Hyper ECS :: Encompass Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ + + + +
+
+ +
+
+ + +
+
+ +
+ +
+ +
+ +

+ + Hyper ECS +

+ + + + + + +

Hyper ECS is a new architecture pattern that attempts to address some common issues with standard ECS.

+ +

The core of the architecture is the introduction of a new construct to ECS: the Message.

+ +

A Message is fundamentally a variant of Component, in that it only contains data. But, it is designed to be temporary and is discarded at the end of each frame. It is used to communicate useful information between Systems.

+ +

We also introduce some extra information to Systems. Each System must declare the Messages that it Reads, the Messages that it Emits, and the Components that it Mutates.

+ +

Let’s go back to our earlier example.

+ +

We have TransformComponent, which contains position and orientation data, and VelocityComponent, which contains an x and y component for linear motion.

+ +

Our MotionDetecterSystem reads each Entity that has both a TransformComponent and a VelocityComponent, and emits a MotionMessage, which contains a reference to the specific TransformComponent and the x and y velocity given by the VelocityComponent.

+ +

We also have a TeleportSystem that needs to teleport the character forward a bit. Let’s say when the player presses the X button, a TeleportMessage is fired. The TeleportSystem reads this message and emits a MotionMessage in response.

+ +

Now we have our MotionSystem. The MotionSystem declares that it Mutates the TransformComponent, reads the MotionMessages that apply to each TransformComponent, and applies them simultaneously, adding their x and y values to the TransformComponent. VoilĂ ! No race conditions! And we can re-use similar behaviors easily without re-writing code by consolidating Messages.

+ +

You might be wondering: how does the game know which order these systems need to be in?

+ +

With the power of graph theory, we can construct an order for our Systems so that any System which Emits a certain Message runs before any System that Reads the same Message. This means, when you write behavior for your game, you never have to specify the order in which your Systems run. You simply write code, and the Systems run in a valid order, every time, without surprising you.

+ +

Of course, to accomplish this, there are some restrictions that your Systems must follow.

+ +

Systems are not allowed to create message cycles: if System A emits Message B, which is read by System B which emits Message C, which is read by System A, then we cannot create a valid ordering of Systems. This is not a flaw in the architecture: A message cycle is simply evidence that you haven’t quite thought through what your Systems are doing, and can generally be easily eliminated by the introduction of a new System.

+ +

Two separate systems are not allowed to Mutate the same Component. Obviously, if we allowed this, we would introduce the possibility of two Systems changing the same component, creating a race condition. If we have two Systems where it makes sense to change the same Component, we can create a new Message and System to consolidate the changes, and avoid race conditions.

+ +

If you are used to programming games in an object-oriented way, you will likely find the ECS pattern counter-intuitive at first. But once you learn to think in a Hyper ECS way, you will be shocked at how flexible and simple your programs become.

+ + +
+ +
+ + + +
+ + +
+ + + +
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + diff --git a/public/why/architecture/index.html b/public/why/architecture/index.html index 8be04ba..b30e3ba 100644 --- a/public/why/architecture/index.html +++ b/public/why/architecture/index.html @@ -9,23 +9,23 @@ - :: Encompass Docs + Architecture :: Encompass Docs - - - - - - - - - + + + + + + + + + - + - + + + + + + + + + + +
+
+
+ +
+
+ + + + +
+
+ +
+
+ + +
+
+ +
+ +
+ +
+ +

+ + The Messy Basement +

+ + + + + + +

Unexpected behavior jumps out at you constantly. You feel like you’re playing whack-a-mole with bugs. You can’t remember where you put anything and keeping everything sorted is a constant nightmare.

+ +

Uh oh. You’re in a messy basement.

+ +

Some characteristics of the messy basement:

+ +
    +
  • Magic values, aka putting numbers directly in the source code.
  • +
  • Game objects directly manipulating each other’s values willy-nilly.
  • +
  • Multiple objects that keep track of and depend on each other’s state.
  • +
+ +

The key characteristic of this structure is that there almost doesn’t seem to be any structure at all.

+ +

Shockingly, some game engines and frameworks not only fail to prevent this kind of architecture, they actually encourage it!

+ +

I don’t need to say much more about this kind of architecture. It’s obviously bad. Let’s move on.

+ + +
+ +
+ + + +
+ + +
+ + + +
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + diff --git a/public/why/architecture/oop/index.html b/public/why/architecture/oop/index.html new file mode 100644 index 0000000..99acfbe --- /dev/null +++ b/public/why/architecture/oop/index.html @@ -0,0 +1,555 @@ + + + + + + + + + + + + OOP :: Encompass Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ + + + +
+
+ +
+
+ + +
+
+ +
+ +
+ +
+ +

+ + OOP +

+ + + + + + +

They call it OOP because it was a mistake. –Unknown

+ +

You are probably very familiar with OOP, or object-oriented programming, as a game designer. It is the structural idea behind most games as they are written today, though this is slowly changing.

+ +

OOP is a structure based on the concept of Objects. Objects contain both data, referred to as properties, and logic, referred to as methods.

+ +

Object orientation is an intuitive idea when it comes to building simulation-oriented applications such as video games. We think of each “thing” in the game as a self-contained object which can be acted upon externally via methods. For example, in the game Asteroids, we could think of the game this way: the ship is an object, the bullets the ship fires are objects, the asteroids are objects, and so on.

+ +

Unfortunately, things aren’t quite this simple when it comes to more complex games.

+ +

As programmers we want to re-use code as much as possible. Every bit of duplication is an opportunity for bugs to lurk in our program. Object-oriented code accomplishes re-use with a concept called inheritance. With inheritance, classes can be partially based on other classes. Maybe a Ball class has a position and a velocity and a bounciness property. A BouncyBall would inherit from Ball and have a greater value in its bounciness property. Simple enough, right?

+ +

But we soon run into problems. In game development we often wish to mix-and-match behaviors. Suppose I have an object where it would make sense to inherit from two different classes. Now… we are hosed! Why? If two parent classes have a property or a method with the same name, now our child object has no idea what to do. Object-oriented systems, in fact, forbid multiple inheritance. So we end up having to share code via helper functions, or giant manager classes, or other awkward patterns.

+ +

We also run into an issue called tight coupling. Objects that reference each other’s properties or methods directly become a problem when we change the structure of those objects in any way. If we modify the structure of object B, and object A references object B, then we have to also modify object A. In a particularly poorly structured system, we might have to modify a dozen objects just to make a slight modification to the behavior of a single object.

+ +

Tight coupling is our worst nightmare as game programmers. Games are, by nature, extremely complex simulations. The more coupling we have between objects, the more of the entire environment of the game we have to understand before we can pick apart the specific behavior of a single object, let alone the whole game itself. It is very possible that we can surprise ourselves by unexpectedly changing the behavior of different objects when modifying a single object. And we hate surprises.

+ +

We want our architecture to encourage us to create as little coupling as possible between different elements of the game, so that we can look at any individual element of the game and easily understand how it behaves without needing to deeply understand the other elements of the game. We also wish to modify behavior without introducing unexpected changes in other seemingly unrelated objects. OOP works in very specific encapsulated cases, but it is not a satisfactory structure for implementing game behavior.

+ +

It turns out there is an architecture that addresses our desire to be able to compose objects out of several different behaviors. Let’s learn about it.

+ + +
+ +
+ + + +
+ + +
+ + + +
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + diff --git a/public/why/index.html b/public/why/index.html index 96f2f99..2032e71 100644 --- a/public/why/index.html +++ b/public/why/index.html @@ -9,23 +9,23 @@ - Why :: Encompass Docs + Why? :: Encompass Docs - - - - - - - - - + + + + + + + + + - + - + + + + + + + + + + +
+
+
+ +
+
+ + + + +
+
+ +
+
+ + +
+
+ +
+ +
+ +
+ +

+ + The Hard Way +

+ + + + + + + +

If you’re reading this, you have probably been frustrated by code you wrote.

+ +

Maybe a feature that you thought would take a few hours took a few days. Or a few weeks.

+ +

Maybe a bug you fixed a month ago came back and you can’t stand the idea of trying to fix it again.

+ +

Maybe you stare at your screen for hours wondering where a new structure could possibly fit into your program.

+ +

Maybe you find yourself screaming WHY!!!! at your computer and throwing something across the room.

+ +

I’m here to tell you that coding a game doesn’t have to be this hard.

+ +

Just say it with me. It doesn’t have to be this hard. It doesn’t!

+ +

The road to recovery starts with some basic structural ideas and a little bit of discipline.

+ +

Come along with me, won’t you? Let’s write some good code.

+ + + + + +
+ +
+ + +
+ + +
+ + + +
+ +
+
+
+ + + + + + + + + + + + + + + + + + + diff --git a/public/why/the_hard_way/index.xml b/public/why/the_hard_way/index.xml new file mode 100644 index 0000000..d98bbd2 --- /dev/null +++ b/public/why/the_hard_way/index.xml @@ -0,0 +1,15 @@ + + + + The Hard Way on Encompass Docs + http://example.org/why/the_hard_way/ + Recent content in The Hard Way on Encompass Docs + Hugo -- gohugo.io + en-us + Tue, 21 May 2019 15:20:55 -0700 + + + + + + \ No newline at end of file