Tech-lead at Mojang
https://twitter.com/CrisXolt/status/1289402834748768261
@pirelenito
@marlonicus
yarn add parcel-bundler --dev
yarn parcel **/*.html
yarn init -y .
Mostly idle
Villager AI
Fire animation
Hunger
Day/Night cycle
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<title>ECS Talk</title>
<style>
body {
background-color: #1b363e;
margin: 0;
overflow: hidden;
}
#ball {
position: absolute;
background: #ffebea;
width: 40px;
height: 40px;
border-radius: 20px;
}
</style>
</head>
<body>
<div id="ball"></div>
</body>
<script src="./index.ts"></script>
</html>
index.html
const element = document.getElementById('ball')
const gameState = {
x: 0,
y: 0,
}
const gameLoop = () => {
const time = Date.now()
gameState.x = Math.sin(time / 1000) * 200 + 200
gameState.y = Math.cos(time / 1000) * 200 + 200
if (element) {
element.style.left = `${gameState.x}px`
element.style.top = `${gameState.y}px`
}
window.requestAnimationFrame(gameLoop)
}
gameLoop()
export default {}
index.ts
const gameLoop = () => {
// do game stuff...
window.requestAnimationFrame(gameLoop)
}
gameLoop()
const gameLoop = () => {
// enemy AI
// user controls
// physics system
// play sounds
// render the world
// render the UI
window.requestAnimationFrame(gameLoop)
}
gameLoop()
import GameState from './GameState'
import runInput from './runInput'
import runPlayerMovement from './runPlayerMovement'
import runPhysics from './runPhysics'
import runRendering from './runRendering'
const gameState: GameState = {
x: 0,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
}
let previousTime = Date.now()
const gameLoop = () => {
const time = Date.now()
const delta = time - previousTime
runInput(gameState, delta)
runPlayerMovement(gameState, delta)
runPhysics(gameState, delta)
runRendering(gameState, delta)
previousTime = time
window.requestAnimationFrame(gameLoop)
}
gameLoop()
import GameState from './GameState'
import runInput from './runInput'
import runPlayerMovement from './runPlayerMovement'
import runPhysics from './runPhysics'
import runRendering from './runRendering'
const gameState: GameState = {
x: 0,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
}
let previousTime = Date.now()
const gameLoop = () => {
const time = Date.now()
const delta = time - previousTime
runInput(gameState, delta)
runPlayerMovement(gameState, delta)
runPhysics(gameState, delta)
runRendering(gameState, delta)
previousTime = time
window.requestAnimationFrame(gameLoop)
}
gameLoop()
import GameState from './GameState'
export default function runInput(
gameState: GameState,
delta: number,
) {
const gamepad = navigator.getGamepads()[0]
if (gamepad) {
const buttons = gamepad.buttons
gameState.buttonUpPressed = buttons[12].pressed
gameState.buttonDownPressed = buttons[13].pressed
gameState.buttonLeftPressed = buttons[14].pressed
gameState.buttonRightPressed = buttons[15].pressed
}
}
import GameState from './GameState'
import runInput from './runInput'
import runPlayerMovement from './runPlayerMovement'
import runPhysics from './runPhysics'
import runRendering from './runRendering'
const gameState: GameState = {
x: 0,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
}
let previousTime = Date.now()
const gameLoop = () => {
const time = Date.now()
const delta = time - previousTime
runInput(gameState, delta)
runPlayerMovement(gameState, delta)
runPhysics(gameState, delta)
runRendering(gameState, delta)
previousTime = time
window.requestAnimationFrame(gameLoop)
}
gameLoop()
import GameState from './GameState'
const SPEED = 0.2
export default function runPlayerMovement(
gameState: GameState,
delta: number,
) {
const speed = SPEED * delta
if (gameState.buttonRightPressed) {
gameState.x += speed
}
if (gameState.buttonLeftPressed) {
gameState.x -= speed
}
if (gameState.buttonDownPressed) {
gameState.y += speed
}
if (gameState.buttonUpPressed) {
gameState.y -= speed
}
}
import GameState from './GameState'
import runInput from './runInput'
import runPlayerMovement from './runPlayerMovement'
import runPhysics from './runPhysics'
import runRendering from './runRendering'
const gameState: GameState = {
x: 0,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
}
let previousTime = Date.now()
const gameLoop = () => {
const time = Date.now()
const delta = time - previousTime
runInput(gameState, delta)
runPlayerMovement(gameState, delta)
runPhysics(gameState, delta)
runRendering(gameState, delta)
previousTime = time
window.requestAnimationFrame(gameLoop)
}
gameLoop()
import GameState from './GameState'
const GRAVITY = 0.1
export default function runPhysics(
gameState: GameState,
delta: number,
) {
if (gameState.y <= window.innerHeight - 40) {
gameState.y += GRAVITY * delta
}
}
import GameState from './GameState'
import runInput from './runInput'
import runPlayerMovement from './runPlayerMovement'
import runPhysics from './runPhysics'
import runRendering from './runRendering'
const gameState: GameState = {
x: 0,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
}
let previousTime = Date.now()
const gameLoop = () => {
const time = Date.now()
const delta = time - previousTime
runInput(gameState, delta)
runPlayerMovement(gameState, delta)
runPhysics(gameState, delta)
runRendering(gameState, delta)
previousTime = time
window.requestAnimationFrame(gameLoop)
}
gameLoop()
import GameState from './GameState'
export default function runRendering(
gameState: GameState,
delta: number,
) {
const element = document.getElementById('ball')
if (!element) return
element.style.left = `${gameState.x}px`
element.style.top = `${gameState.y}px`
}
const gameState: GameState = {
players: [
{
x: 0,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
},
{
x: 60,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
},
{
x: 100,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
},
],
}
const gameState: GameState = {
staticObjects: [
{
x: 25,
y: 72,
},
{
x: 18,
y: 120,
},
],
players: [
{
x: 0,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
},
{
x: 60,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
},
{
x: 100,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
},
{
x: 140,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
},
],
}
const gameState: GameState = {
players: [
{
x: 0,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
},
{
x: 60,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
},
],
}
const gameState: GameState = {
players: [
{
x: 0,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
},
{
x: 60,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
},
],
}
PlayerEntity
const gameState: GameState = {
players: [
{
x: 0,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
},
{
x: 60,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
},
],
}
PlayerEntity
PositionComponent
const gameState: GameState = {
players: [
{
x: 0,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
},
{
x: 60,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
},
],
}
PlayerEntity
GamepadComponent
const gameState: GameState = {
players: [
{
x: 0,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
},
{
x: 60,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
},
],
}
PlayerEntity
PositionComponent
GamepadComponent
const gameState: GameState = {
players: [
{
x: 0,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
},
{
x: 60,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
},
],
}
PlayerEntity
PositionComponent
GamepadComponent
PlayerEntity
PositionComponent
GamepadComponent
const gameLoop = () => {
const time = Date.now()
const delta = time - previousTime
runInput(gameState, delta)
runPlayerMovement(gameState, delta)
runPhysics(gameState, delta)
runRendering(gameState, delta)
previousTime = time
window.requestAnimationFrame(gameLoop)
}
const gameLoop = () => {
const time = Date.now()
const delta = time - previousTime
runInput(gameState, delta)
runPlayerMovement(gameState, delta)
runPhysics(gameState, delta)
runRendering(gameState, delta)
previousTime = time
window.requestAnimationFrame(gameLoop)
}
GamepadSystem
const gameLoop = () => {
const time = Date.now()
const delta = time - previousTime
runInput(gameState, delta)
runPlayerMovement(gameState, delta)
runPhysics(gameState, delta)
runRendering(gameState, delta)
previousTime = time
window.requestAnimationFrame(gameLoop)
}
GamepadSystem
PlayerMovementSystem
const gameLoop = () => {
const time = Date.now()
const delta = time - previousTime
runInput(gameState, delta)
runPlayerMovement(gameState, delta)
runPhysics(gameState, delta)
runRendering(gameState, delta)
previousTime = time
window.requestAnimationFrame(gameLoop)
}
GamepadSystem
PlayerMovementSystem
PhysicsSystem
const gameLoop = () => {
const time = Date.now()
const delta = time - previousTime
runInput(gameState, delta)
runPlayerMovement(gameState, delta)
runPhysics(gameState, delta)
runRendering(gameState, delta)
previousTime = time
window.requestAnimationFrame(gameLoop)
}
GamepadSystem
PlayerMovementSystem
PhysicsSystem
RenderingSystem
An Entity Component System for the web
Container for entities components and systems
import { World } from 'ecsy'
const world = new World()
let previousTime = Date.now()
const gameLoop = () => {
const time = Date.now()
const delta = time - previousTime
world.execute(delta, time)
previousTime = time
window.requestAnimationFrame(gameLoop)
}
gameLoop()
const gameState: GameState = {
x: 0,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
}
let previousTime = Date.now()
const gameLoop = () => {
const time = Date.now()
const delta = time - previousTime
runInput(gameState, delta)
runPlayerMovement(gameState, delta)
runPhysics(gameState, delta)
runRendering(gameState, delta)
previousTime = time
window.requestAnimationFrame(gameLoop)
}
gameLoop()
previous example
with ECS
import { World } from 'ecsy'
const world = new World()
let previousTime = Date.now()
const gameLoop = () => {
const time = Date.now()
const delta = time - previousTime
world.execute(delta, time)
previousTime = time
window.requestAnimationFrame(gameLoop)
}
gameLoop()
const gameState: GameState = {
x: 0,
y: 0,
buttonUpPressed: false,
buttonDownPressed: false,
buttonLeftPressed: false,
buttonRightPressed: false,
}
let previousTime = Date.now()
const gameLoop = () => {
const time = Date.now()
const delta = time - previousTime
runInput(gameState, delta)
runPlayerMovement(gameState, delta)
runPhysics(gameState, delta)
runRendering(gameState, delta)
previousTime = time
window.requestAnimationFrame(gameLoop)
}
gameLoop()
previous example
with ECS
import { World } from 'ecsy'
import PlayerComponent from '../components/PlayerComponent'
import RenderableComponent from '../components/RenderableComponent'
import RigidBodyComponent from '../components/RigidBodyComponent'
import GamepadComponent from '../components/GamepadComponent'
import PositionComponent from '../components/PositionComponent'
export default function createPlayerEntity(world: World, index: number = 0) {
const player = world.createEntity()
player.addComponent(PlayerComponent)
player.addComponent(RenderableComponent, { color: playerColors[index] })
player.addComponent(RigidBodyComponent)
player.addComponent(GamepadComponent, { index })
player.addComponent(PositionComponent, { x: index * 60 })
}
const playerColors = ['#BFBEFF', '#BEFFD8', '#F6FFBE', '#FFBECA']
entities/createPlayerEntity.ts
import { World } from 'ecsy'
import PlayerComponent from '../components/PlayerComponent'
import RenderableComponent from '../components/RenderableComponent'
import RigidBodyComponent from '../components/RigidBodyComponent'
import GamepadComponent from '../components/GamepadComponent'
import PositionComponent from '../components/PositionComponent'
export default function createPlayerEntity(world: World, index: number = 0) {
const player = world.createEntity()
player.addComponent(PlayerComponent)
player.addComponent(RenderableComponent, { color: playerColors[index] })
player.addComponent(RigidBodyComponent)
player.addComponent(GamepadComponent, { index })
player.addComponent(PositionComponent, { x: index * 60 })
}
const playerColors = ['#BFBEFF', '#BEFFD8', '#F6FFBE', '#FFBECA']
entities/createPlayerEntity.ts
import createPlayerEntity from './entities/createPlayerEntity'
// ...
const world = new World()
createPlayerEntity(world)
// ...
index.ts
import { Component, Types } from 'ecsy'
export default class PositionComponent extends Component<PositionComponent> {
x!: number
y!: number
static schema = {
x: { type: Types.Number, default: 0 },
y: { type: Types.Number, default: 0 },
}
}
components/PositionComponent.ts
import { Component, Types } from 'ecsy'
export default class PositionComponent extends Component<PositionComponent> {
x!: number
y!: number
static schema = {
x: { type: Types.Number, default: 0 },
y: { type: Types.Number, default: 0 },
}
}
components/PositionComponent.ts
import PositionComponent from './components/PositionComponent'
// ...
const world = new World()
world.registerComponent(PositionComponent)
// ...
index.ts
EntityA
PlayerComponent
RenderableComponent
RigidBodyComponent
GamepadComponent
PositionComponent
EntityB
PlayerComponent
RenderableComponent
RigidBodyComponent
GamepadComponent
PositionComponent
EntityD
RenderableComponent
RigidBodyComponent
PositionComponent
EntityE
RenderableComponent
RigidBodyComponent
PositionComponent
EntityF
RenderableComponent
RigidBodyComponent
PositionComponent
EntityC
RenderableComponent
PositionComponent
EntityA
PlayerComponent
RenderableComponent
RigidBodyComponent
GamepadComponent
PositionComponent
EntityB
PlayerComponent
RenderableComponent
RigidBodyComponent
GamepadComponent
PositionComponent
EntityD
RenderableComponent
RigidBodyComponent
PositionComponent
EntityE
RenderableComponent
RigidBodyComponent
PositionComponent
EntityF
RenderableComponent
RigidBodyComponent
PositionComponent
EntityC
RenderableComponent
PositionComponent
PhysicsSystem
Query: [RigidBodyComponent, PositionComponent]
EntityA
PlayerComponent
RenderableComponent
RigidBodyComponent
GamepadComponent
PositionComponent
EntityB
PlayerComponent
RenderableComponent
RigidBodyComponent
GamepadComponent
PositionComponent
EntityD
RenderableComponent
RigidBodyComponent
PositionComponent
EntityE
RenderableComponent
RigidBodyComponent
PositionComponent
EntityF
RenderableComponent
RigidBodyComponent
PositionComponent
EntityC
RenderableComponent
PositionComponent
GamepadSystem
Query: [GamepadComponent]
EntityA
PlayerComponent
RenderableComponent
RigidBodyComponent
GamepadComponent
PositionComponent
EntityB
PlayerComponent
RenderableComponent
RigidBodyComponent
GamepadComponent
PositionComponent
EntityD
RenderableComponent
RigidBodyComponent
PositionComponent
EntityE
RenderableComponent
RigidBodyComponent
PositionComponent
EntityF
RenderableComponent
RigidBodyComponent
PositionComponent
EntityC
RenderableComponent
PositionComponent
PlayerMovementSystem
Query: [PlayerComponent, GamepadComponent, PositionComponent]
EntityA
PlayerComponent
RenderableComponent
RigidBodyComponent
GamepadComponent
PositionComponent
EntityB
PlayerComponent
RenderableComponent
RigidBodyComponent
GamepadComponent
PositionComponent
EntityD
RenderableComponent
RigidBodyComponent
PositionComponent
EntityE
RenderableComponent
RigidBodyComponent
PositionComponent
EntityF
RenderableComponent
RigidBodyComponent
PositionComponent
EntityC
RenderableComponent
PositionComponent
RenderingSystem
Query: [PositionComponent, RenderableComponent]
import { System, Entity } from 'ecsy'
import PositionComponent from '../components/PositionComponent'
import RigidBodyComponent from '../components/RigidBodyComponent'
const GRAVITY = 0.1
export default class PhysicsSystem extends System {
runGravity(delta: number, entity: Entity) {
const position = entity.getMutableComponent(PositionComponent)
if (!position) return
if (position.y < window.innerHeight - 40) {
position.y += GRAVITY * delta
}
}
execute(delta: number) {
this.queries.rigidBodies.results.forEach((entity) => this.runGravity(delta, entity))
}
static queries = {
rigidBodies: {
components: [RigidBodyComponent, PositionComponent],
},
}
}
components/PhysicsComponent.ts
import { System, Entity } from 'ecsy'
import PositionComponent from '../components/PositionComponent'
import RigidBodyComponent from '../components/RigidBodyComponent'
const GRAVITY = 0.1
export default class PhysicsSystem extends System {
runGravity(delta: number, entity: Entity) {
const position = entity.getMutableComponent(PositionComponent)
if (!position) return
if (position.y < window.innerHeight - 40) {
position.y += GRAVITY * delta
}
}
execute(delta: number) {
this.queries.rigidBodies.results.forEach((entity) => this.runGravity(delta, entity))
}
static queries = {
rigidBodies: {
components: [RigidBodyComponent, PositionComponent],
},
}
}
components/PhysicsSystem.ts
import PhysicsSystem from './systems/PhysicsSystem'
// ...
const world = new World()
world.registerSystem(PhysicsSystem)
// ...
index.ts
import GameState from './GameState'
export default function runRendering(gameState: GameState, delta: number) {
const element = document.getElementById('ball')
if (!element) return
element.style.left = `${gameState.x}px`
element.style.top = `${gameState.y}px`
}
<body>
<div id="ball"></div>
</body>
RenderingSystemStateComponent
element: HTMLDIVElement
PositionComponent
x: number
y: number
import { SystemStateComponent, Types } from 'ecsy'
export default class RenderingSystemStateComponent extends SystemStateComponent<
RenderingSystemStateComponent
> {
element?: HTMLDivElement
static schema = {
element: { type: Types.Ref },
}
}
components/RenderingSystemStateComponent.ts
import { SystemStateComponent, Types } from 'ecsy'
export default class RenderingSystemStateComponent extends SystemStateComponent<
RenderingSystemStateComponent
> {
element?: HTMLDivElement
static schema = {
element: { type: Types.Ref },
}
}
components/RenderingSystemStateComponent.ts
import RenderingSystemStateComponent from './state-components/RenderingSystemStateComponent'
// ...
const world = new World()
world.registerComponent(RenderingSystemStateComponent)
// ...
index.ts
import { System } from 'ecsy'
export default class RenderingSystem extends System {
execute() {
}
static queries = {
}
}
systems/RenderingSystem.ts
import { System } from 'ecsy'
export default class RenderingSystem extends System {
execute() {
this.queries.uninitialized.added?.forEach((e) => this.addRenderable(e))
this.queries.positions.changed?.forEach((e) => this.updatePosition(e))
this.queries.initialized.removed?.forEach((e) => this.removeRenderable(e))
}
static queries = {
uninitialized: {
components: [
PositionComponent,
RenderableComponent,
Not(RenderingSystemStateComponent),
],
listen: { added: true },
},
positions: {
components: [PositionComponent, RenderingSystemStateComponent],
listen: { changed: true },
},
initialized: {
components: [RenderableComponent, RenderingSystemStateComponent],
listen: { removed: true },
},
}
}
import { System } from 'ecsy'
export default class RenderingSystem extends System {
execute() {
this.queries.uninitialized.added?.forEach((e) => this.addRenderable(e))
this.queries.positions.changed?.forEach((e) => this.updatePosition(e))
this.queries.initialized.removed?.forEach((e) => this.removeRenderable(e))
}
addRenderable(entity: Entity) {
const position = entity.getComponent(PositionComponent)
const renderable = entity.getComponent(RenderableComponent)
if (!position || !renderable) return
const element = document.createElement('div')
element.style.position = 'absolute'
element.style.background = renderable.color
element.style.width = '40px'
element.style.height = '40px'
element.style.borderRadius = '20px'
element.style.left = `${position.x}px`
element.style.top = `${position.y}px`
document.body.appendChild(element)
entity.addComponent(RenderingSystemStateComponent, { element })
}
static queries = {
uninitialized: {
components: [
PositionComponent,
RenderableComponent,
Not(RenderingSystemStateComponent),
],
listen: { added: true },
},
positions: {
components: [PositionComponent, RenderingSystemStateComponent],
listen: { changed: true },
},
initialized: {
components: [RenderableComponent, RenderingSystemStateComponent],
listen: { removed: true },
},
}
}
import { System } from 'ecsy'
export default class RenderingSystem extends System {
execute() {
this.queries.uninitialized.added?.forEach((e) => this.addRenderable(e))
this.queries.positions.changed?.forEach((e) => this.updatePosition(e))
this.queries.initialized.removed?.forEach((e) => this.removeRenderable(e))
}
updatePosition(entity: Entity) {
const stateComponent = entity.getComponent(RenderingSystemStateComponent)
const position = entity.getComponent(PositionComponent)
if (!position || !stateComponent || !stateComponent.element) return
stateComponent.element.style.left = `${position.x}px`
stateComponent.element.style.top = `${position.y}px`
}
addRenderable(entity: Entity) {
const position = entity.getComponent(PositionComponent)
const renderable = entity.getComponent(RenderableComponent)
if (!position || !renderable) return
const element = document.createElement('div')
element.style.position = 'absolute'
element.style.background = renderable.color
element.style.width = '40px'
element.style.height = '40px'
element.style.borderRadius = '20px'
element.style.left = `${position.x}px`
element.style.top = `${position.y}px`
document.body.appendChild(element)
entity.addComponent(RenderingSystemStateComponent, { element })
}
static queries = {
uninitialized: {
components: [
PositionComponent,
RenderableComponent,
Not(RenderingSystemStateComponent),
],
listen: { added: true },
},
positions: {
components: [PositionComponent, RenderingSystemStateComponent],
listen: { changed: true },
},
initialized: {
components: [RenderableComponent, RenderingSystemStateComponent],
listen: { removed: true },
},
}
}
import { System } from 'ecsy'
export default class RenderingSystem extends System {
execute() {
this.queries.uninitialized.added?.forEach((e) => this.addRenderable(e))
this.queries.positions.changed?.forEach((e) => this.updatePosition(e))
this.queries.initialized.removed?.forEach((e) => this.removeRenderable(e))
}
removeRenderable(entity: Entity) {
const stateComponent = entity.getComponent(RenderingSystemStateComponent)
if (!stateComponent) return
if (stateComponent.element) {
document.body.removeChild(stateComponent.element)
}
entity.removeComponent(RenderingSystemStateComponent)
}
updatePosition(entity: Entity) {
const stateComponent = entity.getComponent(RenderingSystemStateComponent)
const position = entity.getComponent(PositionComponent)
if (!position || !stateComponent || !stateComponent.element) return
stateComponent.element.style.left = `${position.x}px`
stateComponent.element.style.top = `${position.y}px`
}
addRenderable(entity: Entity) {
const position = entity.getComponent(PositionComponent)
const renderable = entity.getComponent(RenderableComponent)
if (!position || !renderable) return
const element = document.createElement('div')
element.style.position = 'absolute'
element.style.background = renderable.color
element.style.width = '40px'
element.style.height = '40px'
element.style.borderRadius = '20px'
element.style.left = `${position.x}px`
element.style.top = `${position.y}px`
document.body.appendChild(element)
entity.addComponent(RenderingSystemStateComponent, { element })
}
static queries = {
uninitialized: {
components: [
PositionComponent,
RenderableComponent,
Not(RenderingSystemStateComponent),
],
listen: { added: true },
},
positions: {
components: [PositionComponent, RenderingSystemStateComponent],
listen: { changed: true },
},
initialized: {
components: [RenderableComponent, RenderingSystemStateComponent],
listen: { removed: true },
},
}
}
https://github.com/pirelenito/ecsy-talk
https://github.com/macaco-maluco/thermal-runway
https://thermalrunway.macacomaluco.space
Criteria | Rank | Score | Raw Score |
---|---|---|---|
Fun | #309 | 3.722 | 3.722 |
Presentation | #1075 | 3.528 | 3.528 |
Overall | #1207 | 3.250 | 3.250 |
Originality | #3784 | 2.444 | 2.444 |
https://youtu.be/DMza7sGJ-Ro
https://youtu.be/-8_xWQgeqvU
@pirelenito
https://slides.com/pirelenito/ecsy-talk