diff --git a/content/blog/on-ecs/index.md b/content/blog/on-ecs/index.md
index 905f102..dabadc9 100644
--- a/content/blog/on-ecs/index.md
+++ b/content/blog/on-ecs/index.md
@@ -2,6 +2,13 @@
{% block article %}
+I've been using ECS on and off for a couple years now. I definitely haven't fully commited to it and I still have a lot to learn, but I'm really enjoying it. Learning it was a struggle though and it was hard to wrap my head around what *exactly* it was. I want to share why I decide to learn ECS, what I've learned, and what I feel it's good for.
+
+
+
+ I am *not* an expert with ECS. I wouldn't even call myself good. I have a very fundamental understanding that's enough to allow me to make small games. Please don't take anything here as objective. Go do your own research and learning, it's worth it.
+
+
## Theseus' Shield
In the context of game design and development, object oriented data modeling can feel intuitive. Players, NPCs, and Enemies can all derive from a common "Creature" class which handle things like health and damage. Weapons, armor, and consumables can derive from a common Item class which handle inventory management.
@@ -284,14 +291,105 @@ One of the other nice things about `SystemAPI.Query` is that it returns an enume
Other than being more convenient, there are significant benefits to querying entities this way that will be covered in just a moment.
-## Going further (Schedules, Tags)
-Most ECS systems can query the state to refine the list of entities they receive to what is most pertinent. Some have scheduling and ordering systems for systems.
+## Clockwork
-## Performance
-its fast
+Given the range of implementations and libraries, there are also some other patterns that have emerged as somewhat standard. Queries are present in nearly every library I've used, but there's a couple other useful concepts.
-## Issues with ECS
-Mental modeling is hard. Complex queries are hard. ECS is hard.
+### Schedules
+
+Not only is it important to be able to request the exact entities which are relevant to a system, it's equally as important to ensure that systems execute at the proper time. Some ECS engines such as Bevy provide tools to order and schedule systems to run at specific times. It's possible to run them at different tick rates, when certain conditions are met, or before/after another system.
+
+### Tag components
+
+While these aren't *really* a separate concept from regular components, they're worth mentioning. Tag components have no data and exist only to tag an entity. "Player" would be a good example -- all their data would likely exist in other components: `Health`, `Transform`, etc -- but it would help with identifying the player in order to perform special tasks. or example, applying movement input from the controller to the player's velocity could be a case for tag components.
+
+### Performance
+
+One thing that I've deliberately not mentioned until now: ECS is usually *very* fast. I often see this touted as one of the main selling points of ECS. It's true that the performance is important to an application which is running anywhere from 60-144 times per second but I think there are *many* other benefits worth talking about.
+
+Many of the performance benefits of ECS are due to its overlap with data-oriented design concepts. One example of data-oriented design, parallel arrays, is a common implementation in many ECS frameworks. I'm *especially* not qualified to speak on this issue because I've very little exposure to the concept separate from using it via ECS in game development, so I'll let others explain it.
+
+## Revisiting our design problems
+
+### Spiked Shield
+
+### Damage ordering
+
+### Health and UI
+
+Did I cherry pick these examples to highlight the strengths of ECS? Kind of, but it's not like there are any
+
+## Weaknesses
+
+Okay, yeah. I've had problems using it too. There are two stand-out pain points that I have with ECS and I will readily acknowledge that they're my own failings.
+
+### Modeling
+
+Although conceptually simple, I find it difficult to model games in ECS. This could be due to my inexperience with it. I have *20 years* of experience with traditional object-oriented modeling and only a couple of years with data-oriented modeling.
+
+I have trouble figuring out when to make new components, when to decouple data by making new entities, and how many things a system should be responsible for. It's *really* nice to have just a few extremely powerful tools rather than a seemingly infinite number of highly specific patterns, but it hasn't really ever been intuitive for me.
+
+### Complex queries
+
+This one ends up frustrating me a lot. Because queries are essentially a way of asking which entities have a specific set of components, it's often easy to end up with *very* complex systems that have to figure out relationships or context from the game state.
+
+This example will probably be a little contrived, but let's take a damage system. We will assume three components: `Weapon`, `Damage`, and `Health`. In our system, when a weapon has a physical collision with an entity that has a `Health` component, it will create a new entity with a `Damage` component. For simplicity's sake this component will only have two properties: `target` and `value` which are the entity ID of the damaged entity and the amount of damage respectively.
+
+What would the system for applying damage to health look like?
+
+```cs
+public void ApplyDamageSystem(ref SystemState state) {
+ // this is an object which can alter the state of the game world
+ // eg. adding and removing entities or components
+ EntityCommandBuffer ecb = new EntityCommandBuffer(Allocator.TempJob);
+
+ // this is effectively a query for every entity with a Health component
+ var allHealth = GetComponentDataFromEntity(false); // the bool argument is whether its read-only
+
+ foreach(var (damage, entity) in SystemAPI.Query>().WithEntityAccess()) {
+ if(!allHealth.HasComponent(damage.target)) {
+ return; // return if our target doesn't have health
+ }
+
+ var targetHealth = allHealth[damage.target].Value;
+ var newHealth = targetHealth.current - damage.value;
+ targetHealth.current = Mathf.Max(newHealth, 0);
+
+ // destroy the entity so it isn't processed on the next frame too
+ ecb.DestroyEntity(entity);
+ }
+
+ ecb.Playback(EntityManager);
+ ecb.Dispose();
+}
+```
+
+We have to create second query, check if our entity actualy has that component, apply our damage, and then remove the damage entity (which is acting like an event). Let's illustrate this with tables again.
+
+| Entity | Damage.Target | Damage.Value |
+| ------ | ------------- | ------------ |
+| 1 | 2 | 10 |
+
+This is our entity with the `Damage` component. `Target` holds an entity ID so we know exacty what is taking damage.
+
+| Entity | Health.Current | Health.Max |
+| ------ | -------------- | ---------- |
+| 0 | 70 | 100 |
+| 2 | 50 | 50 |
+
+These represent our entities with a `Health` component. We have two -- one that dealt damage and another that received damage. We're joining `Damage` and `Health` where `Entity HAS Health AND Damage.Target = Entity`.
+
+Could we do this differently?
+
+Instead of creating a separate entity for damage, we could add the component directly to the damaged entity. With this approach, other sources of damage will have to make sure to check for an existing instance of damage and modify that one if it exists, otherwise add a new one. This eliminates our join but increases the complexity of adding and removing multiple sources of damage to a single target.
+
+This is actually a pretty simple query too. It can get complicated quickly though I'm sure some of this is my own inexperience.
+
+## Anyway
+
+This post is getting way too long and I've already cut it in half. There's going to be a part two at some point.
+
+ECS is conceptually easy but really difficult to learn and master. Building an intuition for it takes time. As far as I'm concerned it's been worth it -- it's another tool to solve a diverse range of problems. If you're interested in learning and using ECS, especially for game development, I would highly recommend going out and using it in order to start building an intuition for it.
{% endblock article %}
diff --git a/static/css/blog.css b/static/css/blog.css
index 3ea6d15..15a7ed5 100644
--- a/static/css/blog.css
+++ b/static/css/blog.css
@@ -3,6 +3,8 @@
}
.callout {
+ margin-top: 1rem;
+ margin-bottom: 1rem;
padding: 0.5rem;
padding-left: 0.8rem;
border-radius: var(--border-radius);