From fbf242fcb3b3b18018ea192a7ebca7c8e6a35985 Mon Sep 17 00:00:00 2001
From: Justin Young <62815737+jyoung4242@users.noreply.github.com>
Date: Fri, 2 Jan 2026 11:43:56 -0500
Subject: [PATCH] docs: New How to tutorial for ECS Primer (#3644)
Not only is this initial content under the 'How To's' section of Tutorials,
This changes the folder structure of that 'section' of the docs to clean it up a bit (just my opinon)
This has a docusaurus tweak in the config to update the default state of the docs to collapsed, which (in my opinion) is a lot easier to read/navigate
Please review in the cloudfare how this 'looks' to you...
I will be building out the examples next to 'ensure' accuracy of content
Here is example of new Tutorials menu behavior;
---
.../BreakOut}/00-breakout.mdx | 4 +-
.../BreakOut/images}/breakout-final.png | Bin
.../BreakOut/images}/breakout-partial.png | Bin
.../Excalibird}/00-intro.mdx | 0
.../Excalibird}/00-step-ts-env.mdx | 0
.../Excalibird}/01-step-start-engine.mdx | 0
.../Excalibird}/02-step-bird-actor.mdx | 0
.../Excalibird}/03-step-bird-and-ground.mdx | 0
.../Excalibird}/04-step-flying-bird.mdx | 0
.../Excalibird}/05-step-plumbing-pipes.mdx | 0
.../Excalibird}/06-step-refactor-to-scene.mdx | 0
.../07-step-refactor-to-constants.mdx | 0
.../Excalibird}/08-step-periodic-pipes.mdx | 0
.../Excalibird}/09-step-scoring-points.mdx | 0
.../Excalibird}/10-step-game-over.mdx | 0
.../Excalibird}/11-step-images-graphics.mdx | 0
.../Excalibird}/12-step-bird-graphics.mdx | 0
.../Excalibird}/13-step-pipe-graphics.mdx | 0
.../Excalibird}/14-step-ground-graphics.mdx | 0
.../Excalibird}/15-step-sound-music.mdx | 0
.../Excalibird}/16-voila.mdx | 0
.../Excalibird}/images/bird-old.png | Bin
.../Excalibird}/images/bird.ico | Bin
.../Excalibird}/images/bird.png | Bin
.../Excalibird}/images/ground.aseprite | Bin
.../Excalibird}/images/ground.png | Bin
.../Excalibird}/images/pipe.aseprite | Bin
.../Excalibird}/images/pipe.ico | Bin
.../Excalibird}/images/pipe.png | Bin
.../Excalibird}/images/social.jpg | Bin
.../Excalibird}/sounds/fail.wav | Bin
.../Excalibird}/sounds/flap.wav | Bin
.../Excalibird}/sounds/flap2.wav | Bin
.../Excalibird}/sounds/score.wav | Bin
.../Excalibird}/sounds/two_left_socks.ogg | Bin
.../How-to's/ECS Primer/00-ecs-primer.mdx | 32 ++++
.../How-to's/ECS Primer/01-what-is-ECS.mdx | 140 +++++++++++++++++
.../ECS Primer/02-custom components.mdx | 83 +++++++++++
.../How-to's/ECS Primer/03-custom-systems.mdx | 141 ++++++++++++++++++
.../ECS Primer/04-systemless-components.mdx | 44 ++++++
.../How-to's/ECS Primer/code/ecs-1.ts | 117 +++++++++++++++
.../How-to's/ECS Primer/images/ecs.png | Bin 0 -> 22518 bytes
.../How-to's/ECS Primer/images/keypress.png | Bin 0 -> 4385 bytes
.../How-to's/ECS Primer/images/movement.gif | Bin 0 -> 576714 bytes
.../00-tutorials/How-to's/_catagory_.json | 10 ++
.../_category_.json | 2 +-
site/docusaurus.config.ts | 2 +-
47 files changed, 571 insertions(+), 4 deletions(-)
rename site/docs/{00-tutorial => 00-tutorials/BreakOut}/00-breakout.mdx (98%)
rename site/docs/{00-tutorial => 00-tutorials/BreakOut/images}/breakout-final.png (100%)
rename site/docs/{00-tutorial => 00-tutorials/BreakOut/images}/breakout-partial.png (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/00-intro.mdx (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/00-step-ts-env.mdx (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/01-step-start-engine.mdx (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/02-step-bird-actor.mdx (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/03-step-bird-and-ground.mdx (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/04-step-flying-bird.mdx (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/05-step-plumbing-pipes.mdx (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/06-step-refactor-to-scene.mdx (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/07-step-refactor-to-constants.mdx (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/08-step-periodic-pipes.mdx (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/09-step-scoring-points.mdx (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/10-step-game-over.mdx (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/11-step-images-graphics.mdx (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/12-step-bird-graphics.mdx (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/13-step-pipe-graphics.mdx (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/14-step-ground-graphics.mdx (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/15-step-sound-music.mdx (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/16-voila.mdx (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/images/bird-old.png (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/images/bird.ico (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/images/bird.png (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/images/ground.aseprite (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/images/ground.png (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/images/pipe.aseprite (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/images/pipe.ico (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/images/pipe.png (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/images/social.jpg (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/sounds/fail.wav (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/sounds/flap.wav (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/sounds/flap2.wav (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/sounds/score.wav (100%)
rename site/docs/{00-tutorial => 00-tutorials/Excalibird}/sounds/two_left_socks.ogg (100%)
create mode 100644 site/docs/00-tutorials/How-to's/ECS Primer/00-ecs-primer.mdx
create mode 100644 site/docs/00-tutorials/How-to's/ECS Primer/01-what-is-ECS.mdx
create mode 100644 site/docs/00-tutorials/How-to's/ECS Primer/02-custom components.mdx
create mode 100644 site/docs/00-tutorials/How-to's/ECS Primer/03-custom-systems.mdx
create mode 100644 site/docs/00-tutorials/How-to's/ECS Primer/04-systemless-components.mdx
create mode 100644 site/docs/00-tutorials/How-to's/ECS Primer/code/ecs-1.ts
create mode 100644 site/docs/00-tutorials/How-to's/ECS Primer/images/ecs.png
create mode 100644 site/docs/00-tutorials/How-to's/ECS Primer/images/keypress.png
create mode 100644 site/docs/00-tutorials/How-to's/ECS Primer/images/movement.gif
create mode 100644 site/docs/00-tutorials/How-to's/_catagory_.json
rename site/docs/{00-tutorial => 00-tutorials}/_category_.json (88%)
diff --git a/site/docs/00-tutorial/00-breakout.mdx b/site/docs/00-tutorials/BreakOut/00-breakout.mdx
similarity index 98%
rename from site/docs/00-tutorial/00-breakout.mdx
rename to site/docs/00-tutorials/BreakOut/00-breakout.mdx
index ac9db4d6..149d0d37 100644
--- a/site/docs/00-tutorial/00-breakout.mdx
+++ b/site/docs/00-tutorials/BreakOut/00-breakout.mdx
@@ -115,7 +115,7 @@ game.add(paddle);
Open up your favorite browser and you should see something like this:
-
+
That’s neat, but this game is way more fun if things move around. Let’s make the paddle follow the mouse around in the x direction. The paddle will be centered on the mouse cursor.
@@ -319,7 +319,7 @@ ball.on("exitviewport", () => {
});
```
-
+
Congratulations! You have just created your first game in Excalibur! You can download this example [here](https://github.com/excaliburjs/sample-breakout).
diff --git a/site/docs/00-tutorial/breakout-final.png b/site/docs/00-tutorials/BreakOut/images/breakout-final.png
similarity index 100%
rename from site/docs/00-tutorial/breakout-final.png
rename to site/docs/00-tutorials/BreakOut/images/breakout-final.png
diff --git a/site/docs/00-tutorial/breakout-partial.png b/site/docs/00-tutorials/BreakOut/images/breakout-partial.png
similarity index 100%
rename from site/docs/00-tutorial/breakout-partial.png
rename to site/docs/00-tutorials/BreakOut/images/breakout-partial.png
diff --git a/site/docs/00-tutorial/00-intro.mdx b/site/docs/00-tutorials/Excalibird/00-intro.mdx
similarity index 100%
rename from site/docs/00-tutorial/00-intro.mdx
rename to site/docs/00-tutorials/Excalibird/00-intro.mdx
diff --git a/site/docs/00-tutorial/00-step-ts-env.mdx b/site/docs/00-tutorials/Excalibird/00-step-ts-env.mdx
similarity index 100%
rename from site/docs/00-tutorial/00-step-ts-env.mdx
rename to site/docs/00-tutorials/Excalibird/00-step-ts-env.mdx
diff --git a/site/docs/00-tutorial/01-step-start-engine.mdx b/site/docs/00-tutorials/Excalibird/01-step-start-engine.mdx
similarity index 100%
rename from site/docs/00-tutorial/01-step-start-engine.mdx
rename to site/docs/00-tutorials/Excalibird/01-step-start-engine.mdx
diff --git a/site/docs/00-tutorial/02-step-bird-actor.mdx b/site/docs/00-tutorials/Excalibird/02-step-bird-actor.mdx
similarity index 100%
rename from site/docs/00-tutorial/02-step-bird-actor.mdx
rename to site/docs/00-tutorials/Excalibird/02-step-bird-actor.mdx
diff --git a/site/docs/00-tutorial/03-step-bird-and-ground.mdx b/site/docs/00-tutorials/Excalibird/03-step-bird-and-ground.mdx
similarity index 100%
rename from site/docs/00-tutorial/03-step-bird-and-ground.mdx
rename to site/docs/00-tutorials/Excalibird/03-step-bird-and-ground.mdx
diff --git a/site/docs/00-tutorial/04-step-flying-bird.mdx b/site/docs/00-tutorials/Excalibird/04-step-flying-bird.mdx
similarity index 100%
rename from site/docs/00-tutorial/04-step-flying-bird.mdx
rename to site/docs/00-tutorials/Excalibird/04-step-flying-bird.mdx
diff --git a/site/docs/00-tutorial/05-step-plumbing-pipes.mdx b/site/docs/00-tutorials/Excalibird/05-step-plumbing-pipes.mdx
similarity index 100%
rename from site/docs/00-tutorial/05-step-plumbing-pipes.mdx
rename to site/docs/00-tutorials/Excalibird/05-step-plumbing-pipes.mdx
diff --git a/site/docs/00-tutorial/06-step-refactor-to-scene.mdx b/site/docs/00-tutorials/Excalibird/06-step-refactor-to-scene.mdx
similarity index 100%
rename from site/docs/00-tutorial/06-step-refactor-to-scene.mdx
rename to site/docs/00-tutorials/Excalibird/06-step-refactor-to-scene.mdx
diff --git a/site/docs/00-tutorial/07-step-refactor-to-constants.mdx b/site/docs/00-tutorials/Excalibird/07-step-refactor-to-constants.mdx
similarity index 100%
rename from site/docs/00-tutorial/07-step-refactor-to-constants.mdx
rename to site/docs/00-tutorials/Excalibird/07-step-refactor-to-constants.mdx
diff --git a/site/docs/00-tutorial/08-step-periodic-pipes.mdx b/site/docs/00-tutorials/Excalibird/08-step-periodic-pipes.mdx
similarity index 100%
rename from site/docs/00-tutorial/08-step-periodic-pipes.mdx
rename to site/docs/00-tutorials/Excalibird/08-step-periodic-pipes.mdx
diff --git a/site/docs/00-tutorial/09-step-scoring-points.mdx b/site/docs/00-tutorials/Excalibird/09-step-scoring-points.mdx
similarity index 100%
rename from site/docs/00-tutorial/09-step-scoring-points.mdx
rename to site/docs/00-tutorials/Excalibird/09-step-scoring-points.mdx
diff --git a/site/docs/00-tutorial/10-step-game-over.mdx b/site/docs/00-tutorials/Excalibird/10-step-game-over.mdx
similarity index 100%
rename from site/docs/00-tutorial/10-step-game-over.mdx
rename to site/docs/00-tutorials/Excalibird/10-step-game-over.mdx
diff --git a/site/docs/00-tutorial/11-step-images-graphics.mdx b/site/docs/00-tutorials/Excalibird/11-step-images-graphics.mdx
similarity index 100%
rename from site/docs/00-tutorial/11-step-images-graphics.mdx
rename to site/docs/00-tutorials/Excalibird/11-step-images-graphics.mdx
diff --git a/site/docs/00-tutorial/12-step-bird-graphics.mdx b/site/docs/00-tutorials/Excalibird/12-step-bird-graphics.mdx
similarity index 100%
rename from site/docs/00-tutorial/12-step-bird-graphics.mdx
rename to site/docs/00-tutorials/Excalibird/12-step-bird-graphics.mdx
diff --git a/site/docs/00-tutorial/13-step-pipe-graphics.mdx b/site/docs/00-tutorials/Excalibird/13-step-pipe-graphics.mdx
similarity index 100%
rename from site/docs/00-tutorial/13-step-pipe-graphics.mdx
rename to site/docs/00-tutorials/Excalibird/13-step-pipe-graphics.mdx
diff --git a/site/docs/00-tutorial/14-step-ground-graphics.mdx b/site/docs/00-tutorials/Excalibird/14-step-ground-graphics.mdx
similarity index 100%
rename from site/docs/00-tutorial/14-step-ground-graphics.mdx
rename to site/docs/00-tutorials/Excalibird/14-step-ground-graphics.mdx
diff --git a/site/docs/00-tutorial/15-step-sound-music.mdx b/site/docs/00-tutorials/Excalibird/15-step-sound-music.mdx
similarity index 100%
rename from site/docs/00-tutorial/15-step-sound-music.mdx
rename to site/docs/00-tutorials/Excalibird/15-step-sound-music.mdx
diff --git a/site/docs/00-tutorial/16-voila.mdx b/site/docs/00-tutorials/Excalibird/16-voila.mdx
similarity index 100%
rename from site/docs/00-tutorial/16-voila.mdx
rename to site/docs/00-tutorials/Excalibird/16-voila.mdx
diff --git a/site/docs/00-tutorial/images/bird-old.png b/site/docs/00-tutorials/Excalibird/images/bird-old.png
similarity index 100%
rename from site/docs/00-tutorial/images/bird-old.png
rename to site/docs/00-tutorials/Excalibird/images/bird-old.png
diff --git a/site/docs/00-tutorial/images/bird.ico b/site/docs/00-tutorials/Excalibird/images/bird.ico
similarity index 100%
rename from site/docs/00-tutorial/images/bird.ico
rename to site/docs/00-tutorials/Excalibird/images/bird.ico
diff --git a/site/docs/00-tutorial/images/bird.png b/site/docs/00-tutorials/Excalibird/images/bird.png
similarity index 100%
rename from site/docs/00-tutorial/images/bird.png
rename to site/docs/00-tutorials/Excalibird/images/bird.png
diff --git a/site/docs/00-tutorial/images/ground.aseprite b/site/docs/00-tutorials/Excalibird/images/ground.aseprite
similarity index 100%
rename from site/docs/00-tutorial/images/ground.aseprite
rename to site/docs/00-tutorials/Excalibird/images/ground.aseprite
diff --git a/site/docs/00-tutorial/images/ground.png b/site/docs/00-tutorials/Excalibird/images/ground.png
similarity index 100%
rename from site/docs/00-tutorial/images/ground.png
rename to site/docs/00-tutorials/Excalibird/images/ground.png
diff --git a/site/docs/00-tutorial/images/pipe.aseprite b/site/docs/00-tutorials/Excalibird/images/pipe.aseprite
similarity index 100%
rename from site/docs/00-tutorial/images/pipe.aseprite
rename to site/docs/00-tutorials/Excalibird/images/pipe.aseprite
diff --git a/site/docs/00-tutorial/images/pipe.ico b/site/docs/00-tutorials/Excalibird/images/pipe.ico
similarity index 100%
rename from site/docs/00-tutorial/images/pipe.ico
rename to site/docs/00-tutorials/Excalibird/images/pipe.ico
diff --git a/site/docs/00-tutorial/images/pipe.png b/site/docs/00-tutorials/Excalibird/images/pipe.png
similarity index 100%
rename from site/docs/00-tutorial/images/pipe.png
rename to site/docs/00-tutorials/Excalibird/images/pipe.png
diff --git a/site/docs/00-tutorial/images/social.jpg b/site/docs/00-tutorials/Excalibird/images/social.jpg
similarity index 100%
rename from site/docs/00-tutorial/images/social.jpg
rename to site/docs/00-tutorials/Excalibird/images/social.jpg
diff --git a/site/docs/00-tutorial/sounds/fail.wav b/site/docs/00-tutorials/Excalibird/sounds/fail.wav
similarity index 100%
rename from site/docs/00-tutorial/sounds/fail.wav
rename to site/docs/00-tutorials/Excalibird/sounds/fail.wav
diff --git a/site/docs/00-tutorial/sounds/flap.wav b/site/docs/00-tutorials/Excalibird/sounds/flap.wav
similarity index 100%
rename from site/docs/00-tutorial/sounds/flap.wav
rename to site/docs/00-tutorials/Excalibird/sounds/flap.wav
diff --git a/site/docs/00-tutorial/sounds/flap2.wav b/site/docs/00-tutorials/Excalibird/sounds/flap2.wav
similarity index 100%
rename from site/docs/00-tutorial/sounds/flap2.wav
rename to site/docs/00-tutorials/Excalibird/sounds/flap2.wav
diff --git a/site/docs/00-tutorial/sounds/score.wav b/site/docs/00-tutorials/Excalibird/sounds/score.wav
similarity index 100%
rename from site/docs/00-tutorial/sounds/score.wav
rename to site/docs/00-tutorials/Excalibird/sounds/score.wav
diff --git a/site/docs/00-tutorial/sounds/two_left_socks.ogg b/site/docs/00-tutorials/Excalibird/sounds/two_left_socks.ogg
similarity index 100%
rename from site/docs/00-tutorial/sounds/two_left_socks.ogg
rename to site/docs/00-tutorials/Excalibird/sounds/two_left_socks.ogg
diff --git a/site/docs/00-tutorials/How-to's/ECS Primer/00-ecs-primer.mdx b/site/docs/00-tutorials/How-to's/ECS Primer/00-ecs-primer.mdx
new file mode 100644
index 00000000..0ed5f814
--- /dev/null
+++ b/site/docs/00-tutorials/How-to's/ECS Primer/00-ecs-primer.mdx
@@ -0,0 +1,32 @@
+---
+title: ECS Primer
+slug: /ecs-primer
+section: Tutorials
+---
+
+## Introduction
+
+ExcaliburJS ECS Tutorial Series: Introduction
+Welcome to ECS in ExcaliburJS
+
+If you’ve ever wondered how modern games organize everything from enemies and bullets to UI elements, you’ve probably heard of ECS — Entity Component Systems. ECS is a way of structuring game code so it’s modular, scalable, and flexible, without the headaches of deep inheritance hierarchies.
+
+ExcaliburJS embraces ECS principles in its core design. Understanding ECS is the key to making clean, maintainable, and fun games with Excalibur.
+
+### What You’ll Learn in This Series
+
+This tutorial series breaks ECS down step by step:
+
+1. Page 1 – ECS Fundamentals:
+Learn what ECS is, why it matters, and how it compares to traditional object-oriented game design.
+
+2. Page 2 – Components:
+How to create your own components, attach them to actors, and keep your data clean and reusable.
+
+3. Page 3 – Systems:
+How systems drive your game logic, query entities, and respond to component state.
+
+4. Page 4 – Systemless Components
+
+By the end of the series, you’ll understand how to design game features using ECS, make your code modular, and create behaviors that are easy to expand and maintain.
+
diff --git a/site/docs/00-tutorials/How-to's/ECS Primer/01-what-is-ECS.mdx b/site/docs/00-tutorials/How-to's/ECS Primer/01-what-is-ECS.mdx
new file mode 100644
index 00000000..8ada6102
--- /dev/null
+++ b/site/docs/00-tutorials/How-to's/ECS Primer/01-what-is-ECS.mdx
@@ -0,0 +1,140 @@
+---
+title: What is an ECS
+slug: /what-is-ecs
+section: Tutorials
+---
+
+import ecspng from './images/ecs.png';
+
+
+
+## Why ECS Matters
+
+Game design often relies on inheritance: you make a Player class, an Enemy class, maybe a FlyingEnemy subclass, and so on. This can get messy quickly:
+
+- Hard to reuse logic
+- Difficult to add new behaviors
+- Easy to introduce bugs when changing one class
+
+ECS flips the model:
+
+- Entities are dumb containers
+- Components are small, focused data
+- Systems do the heavy lifting
+
+## A Quick Peek at ECS in Action
+
+```ts
+const player = new ex.Actor({ x: 100, y: 100 });
+
+//Attach the component to the player
+player.addComponent(new HealthComponent());
+
+//Attach the system to the engine
+engine.world.add(new HealthSystem());
+
+player.get(HealthComponent).damage(20);
+```
+
+Even this small snippet shows the ECS philosophy:
+
+- The player has a health component
+- A system observes and reacts to the health
+- Logic is centralized and predictable, not scattered across your game objects
+
+Let's break this down so we map ECS fundamentals to Excalibur's implementation.
+
+## Entities
+
+Excalibur has an [entity](/docs/entities) class, its simply a container of components.
+
+Actors are 'premade' entities that come with several commonly used components on them already
+
+- Colliders Component
+- Transform Component
+- Body Component
+- Motion Component
+- Pointer Component
+- Actions Component
+- Graphics Component
+
+These allow Actors to have, out of the box, these behaviors: movement, shape/size, hit things, have sprites shown, and respond to mouse clicks.
+
+We will discuss how to make your own custom Components in this series.
+
+## Components
+
+[Components](/docs/components) are simply a collection of data tied to a behavior.
+
+For example: for the Motion Component you have:
+
+- velocity (vel)
+- acceleration (acc)
+- maxVel
+- torque
+- inerta
+
+These are all 'values' and data that are bolted on to an entity that allows the Motion System to use that data to 'move' the entity over time.
+
+## Systems
+
+[Systems](/docs/systems) are where the business logic resides. The process and workflow of a system is as follows.
+
+1. On each update, the system collects ALL entities with the respective components on it needed
+
+ - These are called [Queries](/docs/queries), the 'search' for all impacted entities
+
+2. For each entity that is affected by the System, it uses the systems logic to modify any or all of the data associated with that entity
+
+This is best explained in an example.
+
+```ts
+import { Actor, Engine, Component, System, SystemType } from 'excalibur';
+
+// 1. Component: Health
+class HealthComponent extends Component {
+ current = 100; // <---------- THIS IS THE DATA (COMPONENT)
+
+ constructor(){
+ super();
+ }
+
+ damage(amount: number) { //<---- you can do this too ;)
+ this.current = Math.max(0, this.current - amount);
+ }
+}
+
+// 2. System: Check health and kill actor if zero <----THIS IS THE BEHAVIOR (SYSTEM)
+class HealthSystem extends System {
+ systemType = SystemType.Update;
+ query: Query;
+
+ constructor(world: World) {
+ super();
+ this.query = world.query([HealthComponent]);
+ }
+
+ update(world: any, delta: number) {
+ for (const entity of world.queryEntities(this.types)) { //<--- LOOP THROUGH ALL ENTITIES FOR THIS SYSTEM
+ const health = entity.get(HealthComponent);
+ if (health.current <= 0) {
+ entity.kill();
+ console.log(`${entity.name} has died!`);
+ }
+ }
+ }
+}
+
+// 3. ENTITY SETUP
+const player = new Actor({ x: 100, y: 100, width: 32, height: 32 }); // < ----- THIS IS THE ENITY
+player.addComponent(new HealthComponent());
+
+// 4. Damage the player
+player.get(HealthComponent).damage(50); // player still alive
+player.get(HealthComponent).damage(60); // triggers system → player dies
+
+```
+
+What makes ECS so useful is that if you have a certian mechanic that can be re-used for different Actors or Entities, then slap a component on it and the system will just 'pickup' that behavior for that actor, makes it super easy to extend behaviors without re-writing code.
+
+In this example, you can give any Actor the ability to have health and die simply by adding the component to it.
\ No newline at end of file
diff --git a/site/docs/00-tutorials/How-to's/ECS Primer/02-custom components.mdx b/site/docs/00-tutorials/How-to's/ECS Primer/02-custom components.mdx
new file mode 100644
index 00000000..f8f257c7
--- /dev/null
+++ b/site/docs/00-tutorials/How-to's/ECS Primer/02-custom components.mdx
@@ -0,0 +1,83 @@
+---
+title: Custom Components
+slug: /custom-components
+section: Tutorials
+---
+
+import keypress from './images/keypress.png';
+
+## Custom Components
+
+Components are the building blocks of ECS. They store an entity’s state in a modular way. By creating custom components, you can:
+
+- Encapsulate reusable data
+- Expose helper methods to manipulate that data
+- Keep logic separate from systems (or complement systems later)
+
+A component should describe “what an entity has”, not “what it does.” Systems are where behavior happens — but components can have convenience methods that make life easier.
+
+There are two ways you can build out a component:
+
+1. The traditional way
+2. [Systemless Components](./systemless-components) (advanced)
+
+Let's focus on traditional components first
+
+### Example: Keyboard Control
+
+
+
+```ts
+import { Component, Engine } from 'excalibur';
+
+type Directions = 'Left'|'Right'|'Up'|'Down';
+
+export class PlayerMovementComponent extends Component {
+ _enabled: boolean = true;
+ speed = 150;
+ heldKeys: Directions[] = [];
+ keyboard: Keyboard;
+
+ constructor(engine: Engine,speed?:number){
+ super();
+ if(speed) this.speed = speed;
+ this.keyboard = engine.input.keyboard;
+ }
+
+ set enable(enable: boolean){
+ this._enabled = enable;
+ }
+
+ get enable(){
+ return this._enabled;
+ }
+
+```
+
+To add this component to your Actor that you want controlled by keypress, simply:
+
+```ts
+import { Engine, Actor, Color } from 'excalibur';
+import { PlayerMovementComponent } from './PlayerMovementComponent';
+import { MovementSystem } from './MovementSystem';
+
+const engine = new Engine();
+const player = new Actor({ x: 100, y: 100, width: 32, height: 32, color: Color.Red });
+player.addComponent(new PlayerMovementComponent(engine, 100));
+
+engine.add(player);
+engine.start();
+```
+
+and to access this componet in your code you simply use:
+
+```ts
+let keyboardControl = player.get(PlayerMovementCompontent);
+// now keyboardControl has the speed, enable, and heldKeys properties on it!
+// also it will have the engine reference to the keyboard input
+```
+
+We will look at the keyboard system in the next section.
+
+
+
diff --git a/site/docs/00-tutorials/How-to's/ECS Primer/03-custom-systems.mdx b/site/docs/00-tutorials/How-to's/ECS Primer/03-custom-systems.mdx
new file mode 100644
index 00000000..49ed85b2
--- /dev/null
+++ b/site/docs/00-tutorials/How-to's/ECS Primer/03-custom-systems.mdx
@@ -0,0 +1,141 @@
+---
+title: Wiring Custom Components
+slug: /wiring-up-your-custom-components
+section: Tutorials
+---
+import moveGif from './images/movement.gif';
+import sample1 from '!!raw-loader!./code/ecs-1.ts';
+
+## Connecting the Component to the System
+
+Let's continue the keyboard control system from the previous page.
+
+If we define our System as:
+
+:::note
+I'm using WASD keys here to improve the user experience of the Playground below.
+I'm not using arrow keys as it moves the browser.
+:::
+
+
+```ts
+// KeyBoardControlSystem.ts
+
+import { System, Engine, Actor, Keys, MotionComponent, SystemType, World, Vector } from 'excalibur';
+import { PlayerMovementComponent } from './PlayerMovementComponent';
+
+export class KeyboardControlSystem extends System {
+ public systemType = SystemType.Update;
+ query: Query;
+
+ constructor(world: World) {
+ super();
+ // this tells the query which entities are needed
+ this.query = world.query([PlayerMovementComponent, MotionComponent]);
+ }
+
+ update() {
+ for (const entity of this.query.entities) {
+ // get your components
+ const keyboardControl = entity.get(PlayerMovementComponent);
+ const movementComponent = entity.get(MotionComponent);
+
+ // check enable flag on keyboard control
+ if (!keyboardControl.enable) break;
+ let speed = keyboardControl.speed;
+
+ // set tracked velocity to zero
+ let vX = 0;
+ let vY = 0;
+
+ // Check the input keys...
+
+ //Up
+ if (keyboardControl.keyboard.isHeld(Keys.W)) {
+ // set held direction
+ if (!keyboardControl.heldKeys.includes("Up")) keyboardControl.heldKeys.push("Up");
+ vY += -speed;
+ } else {
+ // clear held direction
+ const index = keyboardControl.heldKeys.indexOf("Up");
+ if (index !== -1) keyboardControl.heldKeys.splice(index, 1);
+ }
+
+ //Down
+ if (keyboardControl.keyboard.isHeld(Keys.S)) {
+ // set held direction
+ if (!keyboardControl.heldKeys.includes("Down")) keyboardControl.heldKeys.push("Down");
+ vY += speed;
+ } else {
+ // clear held direction
+ const index = keyboardControl.heldKeys.indexOf("Down");
+ if (index !== -1) keyboardControl.heldKeys.splice(index, 1);
+ }
+
+ //Left
+ if (keyboardControl.keyboard.isHeld(Keys.A)) {
+ // set held direction
+ if (!keyboardControl.heldKeys.includes("Left")) keyboardControl.heldKeys.push("Left");
+ vX += -speed;
+ } else {
+ // clear held direction
+ const index = keyboardControl.heldKeys.indexOf("Left");
+ if (index !== -1) keyboardControl.heldKeys.splice(index, 1);
+ }
+
+ //Right
+ if (keyboardControl.keyboard.isHeld(Keys.D)) {
+ // set held direction
+ if (!keyboardControl.heldKeys.includes("Right")) keyboardControl.heldKeys.push("Right");
+ vX += speed;
+ } else {
+ // clear held direction
+ const index = keyboardControl.heldKeys.indexOf("Right");
+ if (index !== -1) keyboardControl.heldKeys.splice(index, 1);
+ }
+
+ //Set velocity on entity
+ movementComponent.vel = new Vector(vX, vY);
+ }
+ }
+}
+```
+### Key takeaways
+
+- We are using a Query to collect all impacted entities
+- We are grabbing their components
+- We are performing our 'logic' to manipulate the data for each update tik
+
+## Implementing the system
+
+Systems get added to each [Scene](/docs/scenes) that uses them. For the sake of this example, we'll add it to the root scene.
+
+```ts
+const game = new ex.Engine({
+ width: 800, // the width of the canvas
+ height: 600, // the height of the canvas
+ canvasElementId: '', // the DOM canvas element ID, if you are providing your own
+ displayMode: ex.DisplayMode.FitScreen, // the display mode
+ pointerScope: ex.PointerScope.Document // the scope of capturing pointer (mouse/touch) events
+});
+
+let rootWorld = game.currentScene.world;
+// add your new system to the scene's world
+rootWorld.add(new KeyboardControlSystem(rootWorld))
+
+game.start();
+
+```
+
+Now if you test this out, the actor you place the component on should move with the WASD Keys, try it below!
+
+## Try it out, using Excalibur Playground
+
+:::note
+Note on importing. There are two ways of importing Excalibur, you can import `* as ex` which is in the playground sample.
+Or you can import like above, and each specific module you want to pull in. It is up to you, we show both here.
+:::
+
+
+
+Finally, we'll discuss Systemless Components next
diff --git a/site/docs/00-tutorials/How-to's/ECS Primer/04-systemless-components.mdx b/site/docs/00-tutorials/How-to's/ECS Primer/04-systemless-components.mdx
new file mode 100644
index 00000000..028fbe74
--- /dev/null
+++ b/site/docs/00-tutorials/How-to's/ECS Primer/04-systemless-components.mdx
@@ -0,0 +1,44 @@
+---
+title: Using Systemless Components
+slug: /systemless-components
+section: Tutorials
+---
+
+## Systemless Components
+
+Excalibur has a unique feature that not all ECS's contain. You can extend the gameclock update tick to the component. What this unlocks, is the ability to build simple logic into your component and skip the building of a system.
+
+This is explained very nicely in this [article](https://mattjennings.io/blog/systemless-components-with-excalibur-js) by Matt Jennings:
+
+Let's setup the example:
+
+```ts
+import { Component, Entity, ActorEvents} from 'excalibur';
+
+export class MyCustomSystemlessComponent extends Component {
+ constructor(){
+ super();
+ }
+
+ onAdd(owner: Entity): void {
+ owner.on("preupdate", this.update.bind(this));
+ }
+
+ onRemove(previousOwner: Entity): void {
+ // this ensures you don't have a memory leak by creating a bunch of hanlders that don't get removed
+ previousOwner.off("preupdate", this.update.bind(this));
+ }
+
+ update(event: ActorEvents["preupdate"]){
+ // this fires with parent onPreUpdate()
+
+ let engine = event.engine; // engine reference
+ let elapsed = event.elapsed; // elapsed time since last update
+
+ //... Add your behavior logic here that would normally be in a System
+
+ }
+}
+```
+
+The magic shows up in a variety of ways. First let's wire up the onPreUpdate() tick from the owner actor. We do this by using the built in onAdd and onRemove methods for the Component class. Finally, you can add your 'system' logic in the update method. This is handy if you have a simple system you want to add, and this reduces the boilerplate a bit!
diff --git a/site/docs/00-tutorials/How-to's/ECS Primer/code/ecs-1.ts b/site/docs/00-tutorials/How-to's/ECS Primer/code/ecs-1.ts
new file mode 100644
index 00000000..511e13ff
--- /dev/null
+++ b/site/docs/00-tutorials/How-to's/ECS Primer/code/ecs-1.ts
@@ -0,0 +1,117 @@
+import * as ex from 'excalibur';
+type Directions = "Left" | "Right" | "Up" | "Down";
+
+// Component Definition
+class PlayerMovementComponent extends ex.Component {
+ _enabled: boolean = true;
+ speed = 150;
+ heldKeys: Directions[] = [];
+ keyboard: ex.Keyboard;
+
+ constructor(engine: ex.Engine, speed?: number) {
+ super();
+ if (speed) this.speed = speed;
+ this.keyboard = engine.input.keyboard;
+ }
+
+ set enable(enable: boolean) {
+ this._enabled = enable;
+ }
+
+ get enable() {
+ return this._enabled;
+ }
+}
+
+// System Definition
+class KeyboardControlSystem extends ex.System {
+ public systemType = ex.SystemType.Update;
+ query: ex.Query;
+
+ constructor(world: ex.World) {
+ super();
+ // this tells the query which entities are needed
+ this.query = world.query([PlayerMovementComponent, ex.MotionComponent]);
+ }
+
+ update() {
+ for (const entity of this.query.entities) {
+ // get your components
+ const keyboardControl = entity.get(PlayerMovementComponent);
+ const movementComponent = entity.get( ex.MotionComponent);
+
+ // enable flag on keyboard control
+ if (!keyboardControl.enable) break;
+ let speed = keyboardControl.speed;
+
+ // set tracked velocity to zero
+ let vX = 0;
+ let vY = 0;
+
+ // Check the input keys...
+
+ //Up
+ if (keyboardControl.keyboard.isHeld( ex.Keys.W)) {
+ // set held direction
+ if (!keyboardControl.heldKeys.includes("Up")) keyboardControl.heldKeys.push("Up");
+ vY += -speed;
+ } else {
+ // clear held direction
+ const index = keyboardControl.heldKeys.indexOf("Up");
+ if (index !== -1) keyboardControl.heldKeys.splice(index, 1);
+ }
+
+ //Down
+ if (keyboardControl.keyboard.isHeld( ex.Keys.S)) {
+ // set held direction
+ if (!keyboardControl.heldKeys.includes("Down")) keyboardControl.heldKeys.push("Down");
+ vY += speed;
+ } else {
+ // clear held direction
+ const index = keyboardControl.heldKeys.indexOf("Down");
+ if (index !== -1) keyboardControl.heldKeys.splice(index, 1);
+ }
+
+ //Left
+ if (keyboardControl.keyboard.isHeld( ex.Keys.A)) {
+ // set held direction
+ if (!keyboardControl.heldKeys.includes("Left")) keyboardControl.heldKeys.push("Left");
+ vX += -speed;
+ } else {
+ // clear held direction
+ const index = keyboardControl.heldKeys.indexOf("Left");
+ if (index !== -1) keyboardControl.heldKeys.splice(index, 1);
+ }
+
+ //Right
+ if (keyboardControl.keyboard.isHeld( ex.Keys.D)) {
+ // set held direction
+ if (!keyboardControl.heldKeys.includes("Right")) keyboardControl.heldKeys.push("Right");
+ vX += speed;
+ } else {
+ // clear held direction
+ const index = keyboardControl.heldKeys.indexOf("Right");
+ if (index !== -1) keyboardControl.heldKeys.splice(index, 1);
+ }
+
+ //Set velocity on entity
+ movementComponent.vel = new ex.Vector(vX, vY);
+ }
+ }
+}
+
+const game = new ex.Engine({
+ canvasElementId: 'preview-canvas',
+ displayMode: ex.DisplayMode.FitContainer,
+ width: 600,
+ height: 400
+});
+
+let rootWorld = game.currentScene.world;
+// add your new system to the scene's world
+rootWorld.add(new KeyboardControlSystem(rootWorld));
+
+const player = new ex.Actor({ x: 300, y: 200, width: 32, height: 32, color: ex.Color.Red });
+player.addComponent(new PlayerMovementComponent(game, 100));
+game.add(player);
+game.start();
\ No newline at end of file
diff --git a/site/docs/00-tutorials/How-to's/ECS Primer/images/ecs.png b/site/docs/00-tutorials/How-to's/ECS Primer/images/ecs.png
new file mode 100644
index 0000000000000000000000000000000000000000..7495fc993caf64287db2790d6b2328dc237a52f4
GIT binary patch
literal 22518
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
YYVwa+dTrtn3=9kmp00i_>zopr08lpWE&u=k
literal 0
HcmV?d00001
diff --git a/site/docs/00-tutorials/How-to's/ECS Primer/images/keypress.png b/site/docs/00-tutorials/How-to's/ECS Primer/images/keypress.png
new file mode 100644
index 0000000000000000000000000000000000000000..a28aaea11b74f291f05619bb1d6dfbe66c9342be
GIT binary patch
literal 4385
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