Skip to content

Commit

Permalink
Set up ECS and render a player on screen.
Browse files Browse the repository at this point in the history
  • Loading branch information
avinashv committed Jul 2, 2023
1 parent aec1c11 commit 6ff0aa4
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 6 deletions.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ This repository is participating in the 2023 edition of the [RoguelikeDev event]

### Week 1 - 4 July 2023 - Setup, drawing an @, and moving around

- Getting this done a bit in advance because I know I am going to be busy over this year's sprint!
- The Malwoden tutorial doesn't really spend any time explaining how to set up the development environment--it just asks for some files to be copy/pasted. I've used `vite` for packaging and am just using the `vanilla-ts` template.
- I actually don't have any experience with Typescript, and I learned Javascript during the pre-jQuery era, so modern front-end is pretty new to me. For now, it has been relatively straightforward to just learn as I've gone along.
- I don't understand typing strictness in Typescript. The `queries` in any inherited `System` class should be typed `SystemQueries` but the tutorial doesn't do this and it doesn't throw any errors. There also seems to be no requirement to have a consistent style for semicolon requirement! I presume the settings in `tsconfig.json` control this, but I don't understand why there is no singular standard for the language.
- A lot of logic keeps being shoved into the `main.ts` file and I am expecting a serious refactor down the road.
- Malwoden itself seems to get out of the way--I enjoy this.
- The ECS system is simple but seems sufficient--compared to Rust's `specs` or `legion` this feels much better for a beginner.

### Week 2 - 11 July 2023 - The object system and generating your first map

### Week 3 - 18 July 2023 - Field of view, placing enemies, and attacking
Expand All @@ -26,7 +34,7 @@ This repository is participating in the 2023 edition of the [RoguelikeDev event]

### Week 7 - 15 August 2023 - Monster/item progression and equipment

### Week 8 - 22 July 2023 - Sharing your game
### Week 8 - 22 July 2023 - Sharing your gamex

## Design Specification

Expand Down
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"vite": "^4.3.9"
},
"dependencies": {
"malwoden": "^0.5.0"
"malwoden": "^0.5.0",
"ecsy": "^0.4.2"
}
}
35 changes: 35 additions & 0 deletions src/components.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Component, ComponentSchema, Types } from 'ecsy';
import { Terminal } from 'malwoden';

/**
* Position component
* @param x x-coordinate
* @param y y-coordinate
*/
export class Position extends Component<Position> {
x = 0;
y = 0;

static schema: ComponentSchema = {
x: { type: Types.Number },
y: { type: Types.Number },
};
}

/**
* Renderable component
* @param glyph `Terminal.Glyph` for rendering on the canvas
*/
export class Renderable extends Component<Renderable> {
// glyph can never be undefined
glyph!: Terminal.Glyph;

static schema: ComponentSchema = {
glyph: { type: Types.Ref },
};
}

/**
* Player component
*/
export class Player extends Component<Player> {}
44 changes: 40 additions & 4 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { Terminal } from "malwoden";
import { Terminal, Color } from 'malwoden';
import { World, Entity } from 'ecsy';

import * as Components from './components';
import * as Systems from './systems';

/**
* The main game engine.
Expand All @@ -10,12 +14,19 @@ export class Game {
public static readonly CHAR_WIDTH = 16;
public static readonly CHAR_HEIGHT = 16;

world: World;
terminal: Terminal.RetroTerminal;
lastTime: number;

player: Entity;

constructor() {
this.world = new World();
this.terminal = this.createTerminal();
this.lastTime = performance.now();

this.registerECS();
this.player = this.initWorld();
}

/**
Expand All @@ -36,15 +47,39 @@ export class Game {
});
}

/**
* Register the ECS system into the game World.
*/
registerECS() {
this.world
.registerComponent(Components.Position)
.registerComponent(Components.Renderable)
.registerComponent(Components.Player)

.registerSystem(Systems.RenderSystem, this);
}

/**
* Initializes the World with a player Entity.
* @returns Entity
*/
initWorld(): Entity {
const player = this.world
.createEntity()
.addComponent(Components.Player)
.addComponent(Components.Position, { x: 5, y: 5 })
.addComponent(Components.Renderable, { glyph: new Terminal.Glyph('@', Color.Yellow) });

return player;
}

/**
* The engine's update tick.
* @param delta Time passed since last tick
* @param time Current time
*/
tick(delta: number, time: number) {
this.terminal.clear();
this.terminal.writeAt({ x: 1, y: 1 }, 'Hello, world!');
this.terminal.render();
this.world.execute(delta, time);
}

/**
Expand All @@ -60,6 +95,7 @@ export class Game {
}
}

// Modify the Window to include the Game object.
declare global {
interface Window {
game: Game;
Expand Down
1 change: 1 addition & 0 deletions src/systems/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { RenderSystem } from './render';
44 changes: 44 additions & 0 deletions src/systems/render.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { World, System, SystemQueries } from 'ecsy';

import { Game } from '../main';
import * as Components from '../components';

/**
* Rendering system
*/
export class RenderSystem extends System {
game: Game;

constructor(world: World, game: Game) {
super(world, game);
this.game = game;
}

static queries: SystemQueries = {
renderables: {
components: [Components.Renderable, Components.Position],
},
};

/**
* Render any entities that have Position and Renderable components.
*/
execute() {
// run the query
const { results } = this.queries.renderables;

// clear before the render pass
this.game.terminal.clear();

// draw each entity
for (const entity of results) {
const position = entity.getComponent(Components.Position)!;
const renderable = entity.getComponent(Components.Renderable)!;

this.game.terminal.drawGlyph(position, renderable.glyph)
}

// render to canvas
this.game.terminal.render();
}
}

0 comments on commit 6ff0aa4

Please sign in to comment.