encompass-cs-docs/public/index.json

71 lines
14 KiB
JSON
Raw Normal View History

[
{
"uri": "http://example.org/why/",
"title": "Why?",
"tags": [],
"description": "",
"content": " Chapter 1 Why? A question that many of us find ourselves asking when coding a game.\n"
},
{
"uri": "http://example.org/why/the_hard_way/",
"title": "The Hard Way",
"tags": [],
"description": "",
"content": "If you\u0026rsquo;re reading this, you have probably been frustrated by code you wrote.\nMaybe a feature that you thought would take a few hours took a few days. Or a few weeks.\nMaybe a bug you fixed a month ago came back and you can\u0026rsquo;t stand the idea of trying to fix it again.\nMaybe you stare at your screen for hours wondering where a new structure could possibly fit into your program.\nMaybe you find yourself screaming WHY!!!! at your computer and throwing something across the room.\nI\u0026rsquo;m here to tell you that coding a game doesn\u0026rsquo;t have to be this hard.\nJust say it with me. It doesn\u0026rsquo;t have to be this hard. It doesn\u0026rsquo;t!\nThe road to recovery starts with some basic structural ideas and a little bit of discipline.\nCome along with me, won\u0026rsquo;t you? Let\u0026rsquo;s write some good code.\n"
},
{
"uri": "http://example.org/why/architecture/mess/",
"title": "The Messy Basement",
"tags": [],
"description": "",
"content": "Unexpected behavior jumps out at you constantly. You feel like you\u0026rsquo;re playing whack-a-mole with bugs. You can\u0026rsquo;t remember where you put anything and keeping everything sorted is a constant nightmare.\nUh oh. You\u0026rsquo;re in a messy basement.\nSome characteristics of the messy basement:\n Magic values, aka putting numbers directly in the source code. Game objects directly manipulating each other\u0026rsquo;s values willy-nilly. Multiple objects that keep track of and depend on each other\u0026rsquo;s state. The key characteristic of this structure is that there almost doesn\u0026rsquo;t seem to be any structure at all.\nShockingly, some game engines and frameworks not only fail to prevent this kind of architecture, they actually encourage it!\nI don\u0026rsquo;t need to say much more about this kind of architecture. It\u0026rsquo;s obviously bad. Let\u0026rsquo;s move on.\n"
},
{
"uri": "http://example.org/why/architecture/oop/",
"title": "OOP",
"tags": [],
"description": "",
"content": "They call it OOP because it was a mistake. \u0026ndash;Unknown\nYou 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.\nOOP is a structure based on the concept of Objects. Objects contain both data, referred to as properties, and logic, referred to as methods.\nObject orientation is an intuitive idea when it comes to building simulation-oriented applications such as video games. We think of each \u0026ldquo;thing\u0026rdquo; 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.\nUnfortunately, things aren\u0026rsquo;t quite this simple when it comes to more complex games.\nAs 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?\nBut 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\u0026hellip; 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.\nWe also run into an issue called tight coupling. Objects that reference each other\u0026rsquo;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.\nTight 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.\nWe 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.\nIt turns out there is an architecture that addresses our desire to be able to compose objects out of several different behaviors. Let\u0026rsquo;s learn about it.\n"
},
{
"uri": "http://example.org/why/architecture/",
"title": "Architecture",
"tags": [],
"description": "",
"content": "Architecture simply means the basic structure of a program, and how things are added to the program. It is the foundation of everything you build.\nAll programs that you write have some kind of architecture, even if you haven\u0026rsquo;t really thought about it much. Let\u0026rsquo;s go over some different architectures and talk about their features.\n"
},
{
"uri": "http://example.org/why/architecture/ecs/",
"title": "ECS",
"tags": [],
"description": "",
"content": "ECS stands for Entity-Component-System. It is based on two fundamental principles:\n 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.\nEntities 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.\nSystems 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.\nNotice, 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.\nUnfortunately, ECS is not without its problems.\nSuppose 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.\nWe could have a TeleportSystem that immediately sets the x and y value of the PositionComponent so it is in front of the player\u0026rsquo;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!\nThis 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!\nIn 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.\nThis example also illustrates another problem with ECS: in Systems that have similar behavior, we don\u0026rsquo;t really have a nice structure to share that behavior.\nI have created a variant of ECS that intends to address these and other problems. Let me tell you about it.\n"
},
{
"uri": "http://example.org/why/architecture/hyper_ecs/",
"title": "Hyper ECS",
"tags": [],
"description": "",
"content": "Hyper ECS is a new architecture pattern that attempts to address some common issues with standard ECS.\nThe core of the architecture is the introduction of a new construct to ECS: the Message.\nA 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.\nWe 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.\nLet\u0026rsquo;s go back to our earlier example.\nWe have TransformComponent, which contains position and orientation data, and VelocityComponent, which contains an x and y component for linear motion.\nOur 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.\nWe also have a TeleportSystem that needs to teleport the character forward a bit. Let\u0026rsquo;s say when the player presses the X button, a TeleportMessage is fired. The TeleportSystem reads this message and emits a MotionMessage in response.\nNow 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.\nYou might be wondering: how does the game know which order these systems need to be in?\nWith 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.\nOf course, to accomplish this, there are some restrictions that your Systems must follow.\nSystems 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\u0026rsquo;t quite thought through what your Systems are doing, and can generally be easily eliminated by the introduction of a new System.\nTwo 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.\nIf 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.\n"
},
{
"uri": "http://example.org/",
"title": "Encompass",
"tags": [],
"description": "",
"content": " Encompass encompass is a powerful engine-agnostic framework to help you code games, or other kinds of simulations.\nObject-oriented code is messy and rapidly becomes unmaintainable.\nencompass lets you write clean, de-coupled code so you can spend more time on your game design and less time fixing bugs.\nencompass is currently available with a TypeScript implementation that fully supports transpilation to Javascript and Lua. A C# implementation is forthcoming.\n"
},
{
"uri": "http://example.org/categories/",
"title": "Categories",
"tags": [],
"description": "",
"content": ""
},
{
"uri": "http://example.org/tags/",
"title": "Tags",
"tags": [],
"description": "",
"content": ""
}]