[READ-ONLY] Mirror of https://github.com/excaliburjs/Excalibur. 🎮 Your friendly TypeScript 2D game engine for the web 🗡️ excaliburjs.com
excalibur excaliburjs game-development game-engine game-framework gamedev games html5-canvas typescript
2

Configure Feed

Select the types of activity you want to include in your feed.

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

<img width="322" height="788" alt="image" src="https://github.com/user-attachments/assets/45d90e92-814b-49d8-9045-54cd78a7dca8" />

Here is example of new Tutorials menu behavior;
<img width="414" height="638" alt="image" src="https://github.com/user-attachments/assets/1fd86b40-17f4-4953-935c-809e34fda599" />

authored by

Justin Young and committed by
GitHub
(Jan 2, 2026, 10:43 AM -0600) fbf242fc df841071

+571 -4
+2 -2
site/docs/00-tutorial/00-breakout.mdx site/docs/00-tutorials/BreakOut/00-breakout.mdx
··· 115 115 116 116 Open up your favorite browser and you should see something like this: 117 117 118 - ![Incomplete breakout screenshot](breakout-partial.png) 118 + ![Incomplete breakout screenshot](./images/breakout-partial.png) 119 119 120 120 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. 121 121 ··· 319 319 }); 320 320 ``` 321 321 322 - ![Final Breakout screenshot](breakout-final.png) 322 + ![Final Breakout screenshot](./images/breakout-final.png) 323 323 324 324 Congratulations! You have just created your first game in Excalibur! You can download this example [here](https://github.com/excaliburjs/sample-breakout). 325 325
site/docs/00-tutorial/00-intro.mdx site/docs/00-tutorials/Excalibird/00-intro.mdx
site/docs/00-tutorial/00-step-ts-env.mdx site/docs/00-tutorials/Excalibird/00-step-ts-env.mdx
site/docs/00-tutorial/01-step-start-engine.mdx site/docs/00-tutorials/Excalibird/01-step-start-engine.mdx
site/docs/00-tutorial/02-step-bird-actor.mdx site/docs/00-tutorials/Excalibird/02-step-bird-actor.mdx
site/docs/00-tutorial/03-step-bird-and-ground.mdx site/docs/00-tutorials/Excalibird/03-step-bird-and-ground.mdx
site/docs/00-tutorial/04-step-flying-bird.mdx site/docs/00-tutorials/Excalibird/04-step-flying-bird.mdx
site/docs/00-tutorial/05-step-plumbing-pipes.mdx site/docs/00-tutorials/Excalibird/05-step-plumbing-pipes.mdx
site/docs/00-tutorial/06-step-refactor-to-scene.mdx site/docs/00-tutorials/Excalibird/06-step-refactor-to-scene.mdx
site/docs/00-tutorial/07-step-refactor-to-constants.mdx site/docs/00-tutorials/Excalibird/07-step-refactor-to-constants.mdx
site/docs/00-tutorial/08-step-periodic-pipes.mdx site/docs/00-tutorials/Excalibird/08-step-periodic-pipes.mdx
site/docs/00-tutorial/09-step-scoring-points.mdx site/docs/00-tutorials/Excalibird/09-step-scoring-points.mdx
site/docs/00-tutorial/10-step-game-over.mdx site/docs/00-tutorials/Excalibird/10-step-game-over.mdx
site/docs/00-tutorial/11-step-images-graphics.mdx site/docs/00-tutorials/Excalibird/11-step-images-graphics.mdx
site/docs/00-tutorial/12-step-bird-graphics.mdx site/docs/00-tutorials/Excalibird/12-step-bird-graphics.mdx
site/docs/00-tutorial/13-step-pipe-graphics.mdx site/docs/00-tutorials/Excalibird/13-step-pipe-graphics.mdx
site/docs/00-tutorial/14-step-ground-graphics.mdx site/docs/00-tutorials/Excalibird/14-step-ground-graphics.mdx
site/docs/00-tutorial/15-step-sound-music.mdx site/docs/00-tutorials/Excalibird/15-step-sound-music.mdx
site/docs/00-tutorial/16-voila.mdx site/docs/00-tutorials/Excalibird/16-voila.mdx
+1 -1
site/docs/00-tutorial/_category_.json site/docs/00-tutorials/_category_.json
··· 1 1 { 2 - "label": "Tutorial", 2 + "label": "Tutorials", 3 3 "collapsed": true, 4 4 "position": 1, 5 5 "link": {
site/docs/00-tutorial/breakout-final.png site/docs/00-tutorials/BreakOut/images/breakout-final.png
site/docs/00-tutorial/breakout-partial.png site/docs/00-tutorials/BreakOut/images/breakout-partial.png
site/docs/00-tutorial/images/bird-old.png site/docs/00-tutorials/Excalibird/images/bird-old.png
site/docs/00-tutorial/images/bird.ico site/docs/00-tutorials/Excalibird/images/bird.ico
site/docs/00-tutorial/images/bird.png site/docs/00-tutorials/Excalibird/images/bird.png
site/docs/00-tutorial/images/ground.aseprite site/docs/00-tutorials/Excalibird/images/ground.aseprite
site/docs/00-tutorial/images/ground.png site/docs/00-tutorials/Excalibird/images/ground.png
site/docs/00-tutorial/images/pipe.aseprite site/docs/00-tutorials/Excalibird/images/pipe.aseprite
site/docs/00-tutorial/images/pipe.ico site/docs/00-tutorials/Excalibird/images/pipe.ico
site/docs/00-tutorial/images/pipe.png site/docs/00-tutorials/Excalibird/images/pipe.png
site/docs/00-tutorial/images/social.jpg site/docs/00-tutorials/Excalibird/images/social.jpg
site/docs/00-tutorial/sounds/fail.wav site/docs/00-tutorials/Excalibird/sounds/fail.wav
site/docs/00-tutorial/sounds/flap.wav site/docs/00-tutorials/Excalibird/sounds/flap.wav
site/docs/00-tutorial/sounds/flap2.wav site/docs/00-tutorials/Excalibird/sounds/flap2.wav
site/docs/00-tutorial/sounds/score.wav site/docs/00-tutorials/Excalibird/sounds/score.wav
site/docs/00-tutorial/sounds/two_left_socks.ogg site/docs/00-tutorials/Excalibird/sounds/two_left_socks.ogg
+32
site/docs/00-tutorials/How-to's/ECS Primer/00-ecs-primer.mdx
··· 1 + --- 2 + title: ECS Primer 3 + slug: /ecs-primer 4 + section: Tutorials 5 + --- 6 + 7 + ## Introduction 8 + 9 + ExcaliburJS ECS Tutorial Series: Introduction 10 + Welcome to ECS in ExcaliburJS 11 + 12 + 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. 13 + 14 + ExcaliburJS embraces ECS principles in its core design. Understanding ECS is the key to making clean, maintainable, and fun games with Excalibur. 15 + 16 + ### What You’ll Learn in This Series 17 + 18 + This tutorial series breaks ECS down step by step: 19 + 20 + 1. Page 1 – ECS Fundamentals: 21 + Learn what ECS is, why it matters, and how it compares to traditional object-oriented game design. 22 + 23 + 2. Page 2 – Components: 24 + How to create your own components, attach them to actors, and keep your data clean and reusable. 25 + 26 + 3. Page 3 – Systems: 27 + How systems drive your game logic, query entities, and respond to component state. 28 + 29 + 4. Page 4 – Systemless Components 30 + 31 + 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. 32 +
+140
site/docs/00-tutorials/How-to's/ECS Primer/01-what-is-ECS.mdx
··· 1 + --- 2 + title: What is an ECS 3 + slug: /what-is-ecs 4 + section: Tutorials 5 + --- 6 + 7 + import ecspng from './images/ecs.png'; 8 + 9 + <img src={ecspng} alt="ECS model" style={{ width: "700px" }} /> 10 + 11 + ## Why ECS Matters 12 + 13 + 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: 14 + 15 + - Hard to reuse logic 16 + - Difficult to add new behaviors 17 + - Easy to introduce bugs when changing one class 18 + 19 + ECS flips the model: 20 + 21 + - Entities are dumb containers 22 + - Components are small, focused data 23 + - Systems do the heavy lifting 24 + 25 + ## A Quick Peek at ECS in Action 26 + 27 + ```ts 28 + const player = new ex.Actor({ x: 100, y: 100 }); 29 + 30 + //Attach the component to the player 31 + player.addComponent(new HealthComponent()); 32 + 33 + //Attach the system to the engine 34 + engine.world.add(new HealthSystem()); 35 + 36 + player.get(HealthComponent).damage(20); 37 + ``` 38 + 39 + Even this small snippet shows the ECS philosophy: 40 + 41 + - The player has a health component 42 + - A system observes and reacts to the health 43 + - Logic is centralized and predictable, not scattered across your game objects 44 + 45 + Let's break this down so we map ECS fundamentals to Excalibur's implementation. 46 + 47 + ## Entities 48 + 49 + Excalibur has an [entity](/docs/entities) class, its simply a container of components. 50 + 51 + Actors are 'premade' entities that come with several commonly used components on them already 52 + 53 + - Colliders Component 54 + - Transform Component 55 + - Body Component 56 + - Motion Component 57 + - Pointer Component 58 + - Actions Component 59 + - Graphics Component 60 + 61 + These allow Actors to have, out of the box, these behaviors: movement, shape/size, hit things, have sprites shown, and respond to mouse clicks. 62 + 63 + We will discuss how to make your own custom Components in this series. 64 + 65 + ## Components 66 + 67 + [Components](/docs/components) are simply a collection of data tied to a behavior. 68 + 69 + For example: for the Motion Component you have: 70 + 71 + - velocity (vel) 72 + - acceleration (acc) 73 + - maxVel 74 + - torque 75 + - inerta 76 + 77 + 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. 78 + 79 + ## Systems 80 + 81 + [Systems](/docs/systems) are where the business logic resides. The process and workflow of a system is as follows. 82 + 83 + 1. On each update, the system collects ALL entities with the respective components on it needed 84 + 85 + - These are called [Queries](/docs/queries), the 'search' for all impacted entities 86 + 87 + 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 88 + 89 + This is best explained in an example. 90 + 91 + ```ts 92 + import { Actor, Engine, Component, System, SystemType } from 'excalibur'; 93 + 94 + // 1. Component: Health 95 + class HealthComponent extends Component { 96 + current = 100; // <---------- THIS IS THE DATA (COMPONENT) 97 + 98 + constructor(){ 99 + super(); 100 + } 101 + 102 + damage(amount: number) { //<---- you can do this too ;) 103 + this.current = Math.max(0, this.current - amount); 104 + } 105 + } 106 + 107 + // 2. System: Check health and kill actor if zero <----THIS IS THE BEHAVIOR (SYSTEM) 108 + class HealthSystem extends System { 109 + systemType = SystemType.Update; 110 + query: Query<typeof HealthComponent>; 111 + 112 + constructor(world: World) { 113 + super(); 114 + this.query = world.query([HealthComponent]); 115 + } 116 + 117 + update(world: any, delta: number) { 118 + for (const entity of world.queryEntities(this.types)) { //<--- LOOP THROUGH ALL ENTITIES FOR THIS SYSTEM 119 + const health = entity.get(HealthComponent); 120 + if (health.current <= 0) { 121 + entity.kill(); 122 + console.log(`${entity.name} has died!`); 123 + } 124 + } 125 + } 126 + } 127 + 128 + // 3. ENTITY SETUP 129 + const player = new Actor({ x: 100, y: 100, width: 32, height: 32 }); // < ----- THIS IS THE ENITY 130 + player.addComponent(new HealthComponent()); 131 + 132 + // 4. Damage the player 133 + player.get(HealthComponent).damage(50); // player still alive 134 + player.get(HealthComponent).damage(60); // triggers system → player dies 135 + 136 + ``` 137 + 138 + 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. 139 + 140 + In this example, you can give any Actor the ability to have health and die simply by adding the component to it.
+83
site/docs/00-tutorials/How-to's/ECS Primer/02-custom components.mdx
··· 1 + --- 2 + title: Custom Components 3 + slug: /custom-components 4 + section: Tutorials 5 + --- 6 + 7 + import keypress from './images/keypress.png'; 8 + 9 + ## Custom Components 10 + 11 + Components are the building blocks of ECS. They store an entity’s state in a modular way. By creating custom components, you can: 12 + 13 + - Encapsulate reusable data 14 + - Expose helper methods to manipulate that data 15 + - Keep logic separate from systems (or complement systems later) 16 + 17 + 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. 18 + 19 + There are two ways you can build out a component: 20 + 21 + 1. The traditional way 22 + 2. [Systemless Components](./systemless-components) (advanced) 23 + 24 + Let's focus on traditional components first 25 + 26 + ### Example: Keyboard Control 27 + 28 + <img src={keypress} alt="Pressing Keyboard" style={{ width: "500px" }} /> 29 + 30 + ```ts 31 + import { Component, Engine } from 'excalibur'; 32 + 33 + type Directions = 'Left'|'Right'|'Up'|'Down'; 34 + 35 + export class PlayerMovementComponent extends Component { 36 + _enabled: boolean = true; 37 + speed = 150; 38 + heldKeys: Directions[] = []; 39 + keyboard: Keyboard; 40 + 41 + constructor(engine: Engine,speed?:number){ 42 + super(); 43 + if(speed) this.speed = speed; 44 + this.keyboard = engine.input.keyboard; 45 + } 46 + 47 + set enable(enable: boolean){ 48 + this._enabled = enable; 49 + } 50 + 51 + get enable(){ 52 + return this._enabled; 53 + } 54 + 55 + ``` 56 + 57 + To add this component to your Actor that you want controlled by keypress, simply: 58 + 59 + ```ts 60 + import { Engine, Actor, Color } from 'excalibur'; 61 + import { PlayerMovementComponent } from './PlayerMovementComponent'; 62 + import { MovementSystem } from './MovementSystem'; 63 + 64 + const engine = new Engine(); 65 + const player = new Actor({ x: 100, y: 100, width: 32, height: 32, color: Color.Red }); 66 + player.addComponent(new PlayerMovementComponent(engine, 100)); 67 + 68 + engine.add(player); 69 + engine.start(); 70 + ``` 71 + 72 + and to access this componet in your code you simply use: 73 + 74 + ```ts 75 + let keyboardControl = player.get(PlayerMovementCompontent); 76 + // now keyboardControl has the speed, enable, and heldKeys properties on it! 77 + // also it will have the engine reference to the keyboard input 78 + ``` 79 + 80 + We will look at the keyboard system in the next section. 81 + 82 + 83 +
+141
site/docs/00-tutorials/How-to's/ECS Primer/03-custom-systems.mdx
··· 1 + --- 2 + title: Wiring Custom Components 3 + slug: /wiring-up-your-custom-components 4 + section: Tutorials 5 + --- 6 + import moveGif from './images/movement.gif'; 7 + import sample1 from '!!raw-loader!./code/ecs-1.ts'; 8 + 9 + ## Connecting the Component to the System 10 + 11 + Let's continue the keyboard control system from the previous page. 12 + 13 + If we define our System as: 14 + 15 + :::note 16 + I'm using WASD keys here to improve the user experience of the Playground below. 17 + I'm not using arrow keys as it moves the browser. 18 + ::: 19 + 20 + 21 + ```ts 22 + // KeyBoardControlSystem.ts 23 + 24 + import { System, Engine, Actor, Keys, MotionComponent, SystemType, World, Vector } from 'excalibur'; 25 + import { PlayerMovementComponent } from './PlayerMovementComponent'; 26 + 27 + export class KeyboardControlSystem extends System { 28 + public systemType = SystemType.Update; 29 + query: Query<typeof PlayerMovementComponent | typeof MotionComponent>; 30 + 31 + constructor(world: World) { 32 + super(); 33 + // this tells the query which entities are needed 34 + this.query = world.query([PlayerMovementComponent, MotionComponent]); 35 + } 36 + 37 + update() { 38 + for (const entity of this.query.entities) { 39 + // get your components 40 + const keyboardControl = entity.get(PlayerMovementComponent); 41 + const movementComponent = entity.get(MotionComponent); 42 + 43 + // check enable flag on keyboard control 44 + if (!keyboardControl.enable) break; 45 + let speed = keyboardControl.speed; 46 + 47 + // set tracked velocity to zero 48 + let vX = 0; 49 + let vY = 0; 50 + 51 + // Check the input keys... 52 + 53 + //Up 54 + if (keyboardControl.keyboard.isHeld(Keys.W)) { 55 + // set held direction 56 + if (!keyboardControl.heldKeys.includes("Up")) keyboardControl.heldKeys.push("Up"); 57 + vY += -speed; 58 + } else { 59 + // clear held direction 60 + const index = keyboardControl.heldKeys.indexOf("Up"); 61 + if (index !== -1) keyboardControl.heldKeys.splice(index, 1); 62 + } 63 + 64 + //Down 65 + if (keyboardControl.keyboard.isHeld(Keys.S)) { 66 + // set held direction 67 + if (!keyboardControl.heldKeys.includes("Down")) keyboardControl.heldKeys.push("Down"); 68 + vY += speed; 69 + } else { 70 + // clear held direction 71 + const index = keyboardControl.heldKeys.indexOf("Down"); 72 + if (index !== -1) keyboardControl.heldKeys.splice(index, 1); 73 + } 74 + 75 + //Left 76 + if (keyboardControl.keyboard.isHeld(Keys.A)) { 77 + // set held direction 78 + if (!keyboardControl.heldKeys.includes("Left")) keyboardControl.heldKeys.push("Left"); 79 + vX += -speed; 80 + } else { 81 + // clear held direction 82 + const index = keyboardControl.heldKeys.indexOf("Left"); 83 + if (index !== -1) keyboardControl.heldKeys.splice(index, 1); 84 + } 85 + 86 + //Right 87 + if (keyboardControl.keyboard.isHeld(Keys.D)) { 88 + // set held direction 89 + if (!keyboardControl.heldKeys.includes("Right")) keyboardControl.heldKeys.push("Right"); 90 + vX += speed; 91 + } else { 92 + // clear held direction 93 + const index = keyboardControl.heldKeys.indexOf("Right"); 94 + if (index !== -1) keyboardControl.heldKeys.splice(index, 1); 95 + } 96 + 97 + //Set velocity on entity 98 + movementComponent.vel = new Vector(vX, vY); 99 + } 100 + } 101 + } 102 + ``` 103 + ### Key takeaways 104 + 105 + - We are using a Query to collect all impacted entities 106 + - We are grabbing their components 107 + - We are performing our 'logic' to manipulate the data for each update tik 108 + 109 + ## Implementing the system 110 + 111 + 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. 112 + 113 + ```ts 114 + const game = new ex.Engine({ 115 + width: 800, // the width of the canvas 116 + height: 600, // the height of the canvas 117 + canvasElementId: '', // the DOM canvas element ID, if you are providing your own 118 + displayMode: ex.DisplayMode.FitScreen, // the display mode 119 + pointerScope: ex.PointerScope.Document // the scope of capturing pointer (mouse/touch) events 120 + }); 121 + 122 + let rootWorld = game.currentScene.world; 123 + // add your new system to the scene's world 124 + rootWorld.add(new KeyboardControlSystem(rootWorld)) 125 + 126 + game.start(); 127 + 128 + ``` 129 + 130 + Now if you test this out, the actor you place the component on should move with the WASD Keys, try it below! 131 + 132 + ## Try it out, using Excalibur Playground 133 + 134 + :::note 135 + Note on importing. There are two ways of importing Excalibur, you can import `* as ex` which is in the playground sample. 136 + Or you can import like above, and each specific module you want to pull in. It is up to you, we show both here. 137 + ::: 138 + 139 + <PlaygroundEmbed code={sample1} /> 140 + 141 + Finally, we'll discuss Systemless Components next
+44
site/docs/00-tutorials/How-to's/ECS Primer/04-systemless-components.mdx
··· 1 + --- 2 + title: Using Systemless Components 3 + slug: /systemless-components 4 + section: Tutorials 5 + --- 6 + 7 + ## Systemless Components 8 + 9 + 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. 10 + 11 + This is explained very nicely in this [article](https://mattjennings.io/blog/systemless-components-with-excalibur-js) by Matt Jennings: 12 + 13 + Let's setup the example: 14 + 15 + ```ts 16 + import { Component, Entity, ActorEvents} from 'excalibur'; 17 + 18 + export class MyCustomSystemlessComponent extends Component { 19 + constructor(){ 20 + super(); 21 + } 22 + 23 + onAdd(owner: Entity): void { 24 + owner.on("preupdate", this.update.bind(this)); 25 + } 26 + 27 + onRemove(previousOwner: Entity): void { 28 + // this ensures you don't have a memory leak by creating a bunch of hanlders that don't get removed 29 + previousOwner.off("preupdate", this.update.bind(this)); 30 + } 31 + 32 + update(event: ActorEvents["preupdate"]){ 33 + // this fires with parent onPreUpdate() 34 + 35 + let engine = event.engine; // engine reference 36 + let elapsed = event.elapsed; // elapsed time since last update 37 + 38 + //... Add your behavior logic here that would normally be in a System 39 + 40 + } 41 + } 42 + ``` 43 + 44 + 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!
+117
site/docs/00-tutorials/How-to's/ECS Primer/code/ecs-1.ts
··· 1 + import * as ex from 'excalibur'; 2 + type Directions = "Left" | "Right" | "Up" | "Down"; 3 + 4 + // Component Definition 5 + class PlayerMovementComponent extends ex.Component { 6 + _enabled: boolean = true; 7 + speed = 150; 8 + heldKeys: Directions[] = []; 9 + keyboard: ex.Keyboard; 10 + 11 + constructor(engine: ex.Engine, speed?: number) { 12 + super(); 13 + if (speed) this.speed = speed; 14 + this.keyboard = engine.input.keyboard; 15 + } 16 + 17 + set enable(enable: boolean) { 18 + this._enabled = enable; 19 + } 20 + 21 + get enable() { 22 + return this._enabled; 23 + } 24 + } 25 + 26 + // System Definition 27 + class KeyboardControlSystem extends ex.System { 28 + public systemType = ex.SystemType.Update; 29 + query: ex.Query<typeof PlayerMovementComponent | typeof ex.MotionComponent>; 30 + 31 + constructor(world: ex.World) { 32 + super(); 33 + // this tells the query which entities are needed 34 + this.query = world.query([PlayerMovementComponent, ex.MotionComponent]); 35 + } 36 + 37 + update() { 38 + for (const entity of this.query.entities) { 39 + // get your components 40 + const keyboardControl = entity.get(PlayerMovementComponent); 41 + const movementComponent = entity.get( ex.MotionComponent); 42 + 43 + // enable flag on keyboard control 44 + if (!keyboardControl.enable) break; 45 + let speed = keyboardControl.speed; 46 + 47 + // set tracked velocity to zero 48 + let vX = 0; 49 + let vY = 0; 50 + 51 + // Check the input keys... 52 + 53 + //Up 54 + if (keyboardControl.keyboard.isHeld( ex.Keys.W)) { 55 + // set held direction 56 + if (!keyboardControl.heldKeys.includes("Up")) keyboardControl.heldKeys.push("Up"); 57 + vY += -speed; 58 + } else { 59 + // clear held direction 60 + const index = keyboardControl.heldKeys.indexOf("Up"); 61 + if (index !== -1) keyboardControl.heldKeys.splice(index, 1); 62 + } 63 + 64 + //Down 65 + if (keyboardControl.keyboard.isHeld( ex.Keys.S)) { 66 + // set held direction 67 + if (!keyboardControl.heldKeys.includes("Down")) keyboardControl.heldKeys.push("Down"); 68 + vY += speed; 69 + } else { 70 + // clear held direction 71 + const index = keyboardControl.heldKeys.indexOf("Down"); 72 + if (index !== -1) keyboardControl.heldKeys.splice(index, 1); 73 + } 74 + 75 + //Left 76 + if (keyboardControl.keyboard.isHeld( ex.Keys.A)) { 77 + // set held direction 78 + if (!keyboardControl.heldKeys.includes("Left")) keyboardControl.heldKeys.push("Left"); 79 + vX += -speed; 80 + } else { 81 + // clear held direction 82 + const index = keyboardControl.heldKeys.indexOf("Left"); 83 + if (index !== -1) keyboardControl.heldKeys.splice(index, 1); 84 + } 85 + 86 + //Right 87 + if (keyboardControl.keyboard.isHeld( ex.Keys.D)) { 88 + // set held direction 89 + if (!keyboardControl.heldKeys.includes("Right")) keyboardControl.heldKeys.push("Right"); 90 + vX += speed; 91 + } else { 92 + // clear held direction 93 + const index = keyboardControl.heldKeys.indexOf("Right"); 94 + if (index !== -1) keyboardControl.heldKeys.splice(index, 1); 95 + } 96 + 97 + //Set velocity on entity 98 + movementComponent.vel = new ex.Vector(vX, vY); 99 + } 100 + } 101 + } 102 + 103 + const game = new ex.Engine({ 104 + canvasElementId: 'preview-canvas', 105 + displayMode: ex.DisplayMode.FitContainer, 106 + width: 600, 107 + height: 400 108 + }); 109 + 110 + let rootWorld = game.currentScene.world; 111 + // add your new system to the scene's world 112 + rootWorld.add(new KeyboardControlSystem(rootWorld)); 113 + 114 + const player = new ex.Actor({ x: 300, y: 200, width: 32, height: 32, color: ex.Color.Red }); 115 + player.addComponent(new PlayerMovementComponent(game, 100)); 116 + game.add(player); 117 + game.start();
site/docs/00-tutorials/How-to's/ECS Primer/images/ecs.png

This is a binary file and will not be displayed.

site/docs/00-tutorials/How-to's/ECS Primer/images/keypress.png

This is a binary file and will not be displayed.

site/docs/00-tutorials/How-to's/ECS Primer/images/movement.gif

This is a binary file and will not be displayed.

+10
site/docs/00-tutorials/How-to's/_catagory_.json
··· 1 + { 2 + "label": "How-to's", 3 + "position": 1, 4 + "collapsed": true, 5 + "link": { 6 + "type": "doc", 7 + "title": "How-To Series", 8 + "description": "Different Game Dev Topics Explained" 9 + } 10 + }
+1 -1
site/docusaurus.config.ts
··· 70 70 'classic', 71 71 { 72 72 docs: { 73 - sidebarCollapsed: false, 73 + sidebarCollapsed: true, 74 74 sidebarPath: './sidebars.ts', 75 75 // Please change this to your repo. 76 76 // Remove this to remove the "edit this page" links.