diff --git a/src/config.ts b/src/config.ts
index ba732e6..1d4e20f 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -16,7 +16,7 @@ export const themeConfig: ThemeConfig = {
centeredLayout: true, // Use centered layout (false for left-aligned)
themeToggle: false, // Show theme toggle button (uses system theme by default)
postListDottedDivider: false, // Show dotted divider in post list
- footer: false, // Show footer
+ footer: true, // Show footer
fadeAnimation: true // Enable fade animations
},
diff --git a/src/content/posts/the-ecs-system.md b/src/content/posts/the-ecs-system.md
index 01fb72e..acbf6fc 100644
--- a/src/content/posts/the-ecs-system.md
+++ b/src/content/posts/the-ecs-system.md
@@ -1,5 +1,338 @@
---
-title: 'Game Engine Series I - The Entity Component System'
+title: 'Game Engine Series I - Entity Component System'
pubDate: '2025-07-19'
tag: 'games'
----
\ No newline at end of file
+---
+
+#
+Welcome to the Game Engine Series.
+
+In this series of blog posts, I'll be diving into one of my favorite topics:
+**Game Engine Development**
+
+The idea came up when I was in the middle on developing my own game engine. While
+this blog serves as a purpose to teach and explain what I've learned
+to programmers, this blog is also a way for my to track my progress and refine my
+own understanding along the way.
+
+Whether you're just curious about how game engines work under the hood, or you're
+building one yourself. I hope this series gives you useful insights, ideas and
+maybe even some inspiration for your future projects, or even your own game engine.
+
+# Core Concepts Explained
+ECS stands for Entity Component System. It is a data-oriented
+design pattern that separates data (_components_) from behavior (_systems_), using
+entities as simple IDs to associate them.
+
+- **Entity** Consists of nothing but an ID. Think of it like a dumb label:
+ `Player = 1`, `Tree = 2`, `Boss1 = 523`
+- **Component** - Components are attached to entities and represent what that entity
+ is made of.
+ `Position { float x, y }`, `Health { int hp }`, `Level { uint8_t level }`
+- **System** - This can be a `physics system`, `rendering system`, or whatever your game
+ needs. These systems iterate over your stored entites and apply the systems to them.
+
+# Why use ECS over OOP?
+Don't get me wrong. ECS takes advantage of OOP as well, but the difference lies in the
+use of OOP. Traditionally, beginners learn one thing - inheritance (_including polymorphism_).
+I would say that it's quite primitive to have one base class called `Entity`,
+and another class called `Player`, which is derived from the base class `Entity`,
+so it would look something like this:
+
+```cpp
+class Entity {
+public:
+ Entity();
+ virtual ~Entity();
+
+ virtual void update() = 0;
+ virtual void render() = 0;
+}
+
+class Player : public Entity {
+private:
+ // Some vars, objects or whatever
+
+public:
+ Player() = default;
+ virtual ~Player() = default;
+
+ void update() override { /* Update logic of player */ }
+ void render() override { /* Render player */ }
+}
+```
+
+Would you notice what's wrong there? Imagine this shape:
+
+- Class `B` and class `C` both inherit from `A`.
+- Class `D` inherits from both `B` and `C`.
+
+So now, if `D` tries to access a member from `A`, which copy does it get?
+
+Here, let's look at another example:
+```cpp
+class Enemy {
+public:
+ void update() { /* generic update stuff */ }
+};
+
+class FlyingEnemy : public Enemy {
+public:
+ void update() { /* flying logic */ }
+};
+
+class ShootingEnemy : public Enemy {
+public:
+ void update() { /* shooting logic */ }
+};
+
+class FlyingShootingEnemy : public FlyingEnemy, public ShootingEnemy {
+public:
+ void update() {
+ FlyingEnemy::update();
+ ShootingEnemy::update();
+ // shit's messy
+ }
+};
+```
+
+- `FlyingShootingEnemy` now has two instances of `Enemy` in its inheritance tree.
+- If `Enemy` has members or state, they're being duplicated, which leads to ambiguity.
+
+The compiler gets confused, and so will you. Your project will end up in a virtual hell.
+But these aren’t the only problems you’ll face.
+
+As your project scales, this architecture will slam you into several brick walls:
+- Virtual dispatches are relatively slow
+ - Every virtual call to `update()` or `render()` goes through a so-called vtable.
+ A vtable or virtual table in C++ is a lookup table of function pointers maintained
+ by the compiler for each class that has virtual functions, meaning heavy use of
+ virtual functions will end up with a bunch of overhead, that can be avoided by ditching that
+ programming pattern.
+ - The diamond inheritance problem mentioned above.
+ - Poor caching
+ - OOP often scatters data all over memory because each object carries it's own data + vptr.
+ - Hard to Extend Beheavior Dynamically
+ - Would you like to add or remove abilities (e.g.: canShoot(), canWalk()) at runtime?
+ - And much more...
+
+See? I could go on about the issues with this approach, but these are the biggest brick walls you’ll hit.
+
+# The Solution
+It means moving away from traditional Object-Oriented Programming (OOP) patterns like inheritance and polymorphism,
+and instead starting to think in Data-Oriented design / programming (DOD).
+
+Instead of organizing your game logic around what objects are (e.g.: `class Player : public Entity`),
+data-oriented design focuses on _what data you operate on_ and _how to structure that data to make the CPU
+cache happy_. It's less about entities and more about the systems and data layout.
+
+Here is roughly what I came up with:
+
+1. First I created a Component.hpp file, exclusively for the components:
+```cpp
+#include
+#include "raylib.h"
+
+namespace DREAM {
+ struct Health { int health = 100; };
+
+ struct Attack { int attack = 1; };
+
+ struct Defense { int defense = 1; };
+
+ struct Level { uint8_t level = 1; };
+
+ struct Position { Vector2 position = {0.0f, 0.0f}; };
+
+ struct Velocity { Vector2 velocity = {0.0f, 0.0f}; };
+}
+```
+
+Then I implemented following in the ComponentManager:
+```cpp
+ using Entity = std::uint32_t;
+
+ class InterfaceComponentArray {
+ public:
+ virtual ~InterfaceComponentArray() = default;
+ virtual void entityDestroyed(Entity entity) = 0;
+ };
+```
+
+You might get a rough understanding on what I am trying to achieve here. Do you see the assignment of
+Entity just being a simple ID/number?
+
+The `InterfaceComponentArray` only knows about the method `entityDestroyed()`. It will make much more sense
+in a bit.
+
+After this we define the `ComponentArray` class:
+```cpp
+ template
+ class ComponentArray : public InterfaceComponentArray {
+ private:
+ std::unordered_map m_componentMap;
+
+ public:
+ ComponentArray() = default;
+ ~ComponentArray() override = default;
+
+ void insertData(Entity entity, const T& component) {
+ // Insert new data / component for entity
+ }
+
+ void removeData(Entity entity) {
+ // Remove data
+ }
+
+ T& getData(Entity entity) {
+ // Return data
+ }
+
+ bool hasData(Entity entity) const {
+ // Check if entity has any data
+ }
+
+ void entityDestroyed(Entity entity) override {
+ // Destroy entity's data
+ }
+ };
+```
+
+This provides a dedicated storage and dedicated API for each `ComponentArray` we create (e.g.: `ComponentArray`).
+
+Theoretically, the `ComponentManager` would work like this, but the user might have a hard time using the API.
+That's why we will create a `ComponentManager` class and wrap the `ComponentArray` methods in it:
+```cpp
+class ComponentManager {
+ private:
+ std::unordered_map> m_componentArrays;
+
+ template
+ std::shared_ptr> getComponentArray() {
+ const auto typeId = std::type_index(typeid(T));
+ auto it = m_componentArrays.find(typeId);
+ if (it == m_componentArrays.end()) {
+ fmt::print(
+ fmt::emphasis::bold | fmt::fg(fmt::color::red),
+ "[Error] ComponentManager::getComponentArray -> Component type {} not registered.\n",
+ typeId.name()
+ );
+ throw std::runtime_error("Component type not registered");
+ }
+ return std::static_pointer_cast>(it->second);
+ }
+
+ public:
+ ComponentManager() = default;
+ ~ComponentManager() = default;
+
+ template
+ void registerComponent() {
+ const auto typeId = std::type_index(typeid(T));
+ if (m_componentArrays.contains(typeId)) {
+ fmt::print(
+ fmt::emphasis::bold | fmt::fg(fmt::color::yellow),
+ "[Warning] ComponentManager::registerComponent -> Component type {} already registered.\n",
+ typeId.name()
+ );
+ return;
+ }
+ m_componentArrays.emplace(typeId, std::make_shared>());
+ }
+
+ template
+ void addComponent(Entity entity, const T& component) {
+ getComponentArray()->insertData(entity, component);
+ }
+
+ template
+ void removeComponent(Entity entity) {
+ getComponentArray()->removeData(entity);
+ }
+
+ template
+ T& getComponent(Entity entity) {
+ return getComponentArray()->getData(entity);
+ }
+
+ template
+ bool hasComponent(Entity entity) {
+ return getComponentArray()->hasData(entity);
+ }
+
+ void destroyEntity(Entity entity) {
+ for (auto& [_, componentArray] : m_componentArrays) {
+ componentArray->entityDestroyed(entity);
+ }
+ }
+ };
+```
+
+In this step we simply wrapped the `ComponentArray` methods around methods from the `ComponentManager`.
+After calling `registerComponent()`, a ComponentArray for the component of your need gets created.
+This map then gets stored in another map, which is located in the `ComponentManager` class.
+
+Using this API could look something like this:
+```cpp
+int main() {
+ ComponentManager componentManager;
+
+ // Register needed component types:
+ componentManager.registerComponent();
+ componentManager.registerComponent();
+ componentManager.registerComponent();
+
+ // Create some entities (For now just IDs, unless you have an EntityManager)
+ Entity player = 1;
+ Entity enemy = 2;
+
+ // Attach components to your entities
+ componentManager.addComponent(player, Position{ 100.0f, 200.0f });
+ componentManager.addComponent(player, Velocity{ 1.5f, 0.0f });
+ componentManager.addComponent(player, Health{ 50 });
+
+ componentManager.addComponent(enemy, Position{ 150.0f, 100.0f });
+ componentManager.addComponent(enemy, Velocity{ 1.5f, 0.0f });
+ componentManager.addComponent(enemy, Health{ 20 });
+
+ // Simple movement "system"
+ // Imagine you have a list of all entities with both Position & Velocity
+ for (Entity e : { player, enemy }) {
+ if (componentManager.hasComponent(e) &&
+ componentManager.hasComponent(e)) {
+ auto& pos = componentManager.getComponent(e);
+ auto& vel = componentManager.getComponent(e);
+ pos.position.x += vel.velocity.x;
+ pos.position.y += vel.velocity.y;
+ }
+ }
+
+ componentManager.removeComponent(enemy);
+ componentManager.destroyEntity(enemy);
+}
+```
+
+That's quite a lot to read and grasp, right? Don't worry. ECS design can be tricky at first, but once
+you grasp the full concept, it will stick in your mind and I will guarantee you that it's awesome.
+
+All this code made it possible to work with entities in a flexible & drastically improved way.
+Also keep in mind that this code only should be used for reference and not blatant copying,
+because I left some parts out on purpose to avoid blasting you a couple of hundred lines of code
+in your face.
+
+
+# Common Pitfalls
+You’ve probably encountered this already, but it’s worth repeating.
+Plenty of developers lose their way in the jungle by over-engineering their code and end up abusing
+ECS like they do with inheritance or polymorphism.
+
+Also - don't overuse components. Too many tiny ones can bloat your systems with excessive iterations
+and seriously hurt performance.
+
+# Conclusion
+There we are at the end of today's post. You probably need to digest the amount of information you've
+just obtained, but that's totally fine. As mentioned previously, even though I showed you the basics
+of an ECS, learning a topic like this is messy at first, especially since it requires 'rethinking'
+how code can be structured so generically.
+
+Hopefully, I will see you next time on future posts :)
\ No newline at end of file
diff --git a/src/styles/post.css b/src/styles/post.css
index 3e8e1c8..0c7809e 100644
--- a/src/styles/post.css
+++ b/src/styles/post.css
@@ -107,6 +107,7 @@
height: auto;
display: block;
margin: 2em 0;
+ border-radius: 5px;
}
.img-placeholder {