add particles
This commit is contained in:
parent
ce06fa7c29
commit
823620b2a6
@ -15,6 +15,7 @@ import {
|
|||||||
Render,
|
Render,
|
||||||
Collision,
|
Collision,
|
||||||
GridSpawner,
|
GridSpawner,
|
||||||
|
Life,
|
||||||
} from "./systems";
|
} from "./systems";
|
||||||
|
|
||||||
export class TheAbstractionEngine {
|
export class TheAbstractionEngine {
|
||||||
@ -49,6 +50,7 @@ export class TheAbstractionEngine {
|
|||||||
new GridSpawner(),
|
new GridSpawner(),
|
||||||
new Collision(),
|
new Collision(),
|
||||||
new Render(this.ctx),
|
new Render(this.ctx),
|
||||||
|
new Life(),
|
||||||
].forEach((system) => this.game.addSystem(system));
|
].forEach((system) => this.game.addSystem(system));
|
||||||
|
|
||||||
const player = new Player();
|
const player = new Player();
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
import { Component, ComponentNames } from ".";
|
import { Component, ComponentNames } from ".";
|
||||||
|
import { Game } from "..";
|
||||||
|
import { Entity } from "../entities";
|
||||||
|
|
||||||
export class Colliding extends Component {
|
export class Colliding extends Component {
|
||||||
constructor() {
|
public onCollision?: (game: Game, entity: Entity) => void;
|
||||||
|
|
||||||
|
constructor(onCollision?: (game: Game, entity: Entity) => void) {
|
||||||
super(ComponentNames.Colliding);
|
super(ComponentNames.Colliding);
|
||||||
|
|
||||||
|
this.onCollision = onCollision;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,4 +11,5 @@ export namespace ComponentNames {
|
|||||||
export const GridSpawn = "GridSpawn";
|
export const GridSpawn = "GridSpawn";
|
||||||
export const Text = "Text";
|
export const Text = "Text";
|
||||||
export const LambdaTerm = "LambdaTerm";
|
export const LambdaTerm = "LambdaTerm";
|
||||||
|
export const Life = "Life";
|
||||||
}
|
}
|
||||||
|
11
src/engine/components/Life.ts
Normal file
11
src/engine/components/Life.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { Component, ComponentNames } from ".";
|
||||||
|
|
||||||
|
export class Life extends Component {
|
||||||
|
public alive: boolean = true;
|
||||||
|
|
||||||
|
constructor(alive: boolean) {
|
||||||
|
super(ComponentNames.Life);
|
||||||
|
|
||||||
|
this.alive = alive;
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,12 @@ import { Component, ComponentNames } from ".";
|
|||||||
import type { Dimension2D, DrawArgs, Coord2D } from "../interfaces";
|
import type { Dimension2D, DrawArgs, Coord2D } from "../interfaces";
|
||||||
import { clamp } from "../utils";
|
import { clamp } from "../utils";
|
||||||
|
|
||||||
export class Sprite extends Component {
|
export interface Renderable {
|
||||||
|
update(dt: number): void;
|
||||||
|
draw(ctx: CanvasRenderingContext2D, drawArgs: DrawArgs): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Sprite extends Component implements Renderable {
|
||||||
private sheet: HTMLImageElement;
|
private sheet: HTMLImageElement;
|
||||||
|
|
||||||
private spriteImgPos: Coord2D;
|
private spriteImgPos: Coord2D;
|
||||||
|
@ -12,3 +12,4 @@ export * from "./Colliding";
|
|||||||
export * from "./GridSpawn";
|
export * from "./GridSpawn";
|
||||||
export * from "./Text";
|
export * from "./Text";
|
||||||
export * from "./LambdaTerm";
|
export * from "./LambdaTerm";
|
||||||
|
export * from "./Life";
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Entity, EntityNames } from ".";
|
import { Entity, EntityNames } from ".";
|
||||||
|
import { Game } from "..";
|
||||||
import { BoundingBox, Colliding, Grid, Sprite } from "../components";
|
import { BoundingBox, Colliding, Grid, Sprite } from "../components";
|
||||||
import { IMAGES, SPRITE_SPECS, SpriteSpec, Sprites } from "../config";
|
import { IMAGES, SPRITE_SPECS, SpriteSpec, Sprites } from "../config";
|
||||||
import { Coord2D } from "../interfaces";
|
import { Coord2D } from "../interfaces";
|
||||||
@ -13,7 +14,7 @@ export class Curry extends Entity {
|
|||||||
|
|
||||||
this.addComponent(new Grid(gridPosition));
|
this.addComponent(new Grid(gridPosition));
|
||||||
|
|
||||||
this.addComponent(new Colliding());
|
this.addComponent(new Colliding(this.collisionHandler));
|
||||||
|
|
||||||
this.addComponent(
|
this.addComponent(
|
||||||
new BoundingBox(
|
new BoundingBox(
|
||||||
@ -42,4 +43,11 @@ export class Curry extends Entity {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private collisionHandler(game: Game, entity: Entity) {
|
||||||
|
if (entity.name === EntityNames.Player) {
|
||||||
|
game.removeEntity(this.id);
|
||||||
|
game.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ export abstract class Entity {
|
|||||||
this.hooks.get(name)?.remove();
|
this.hooks.get(name)?.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
public getComponent<T extends Component>(name: string): T {
|
public getComponent<T>(name: string): T {
|
||||||
if (!this.hasComponent(name)) {
|
if (!this.hasComponent(name)) {
|
||||||
throw new Error("Entity does not have component " + name);
|
throw new Error("Entity does not have component " + name);
|
||||||
}
|
}
|
||||||
|
@ -7,4 +7,5 @@ export namespace EntityNames {
|
|||||||
export const LockedDoor = "LockedDoor";
|
export const LockedDoor = "LockedDoor";
|
||||||
export const Curry = "Curry";
|
export const Curry = "Curry";
|
||||||
export const FunctionApplication = "FunctionApplication";
|
export const FunctionApplication = "FunctionApplication";
|
||||||
|
export const Particles = "Particles";
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,16 @@
|
|||||||
import { Entity, EntityNames } from ".";
|
import { Entity, EntityNames, Particles } from ".";
|
||||||
import { BoundingBox, Colliding, Grid, Sprite } from "../components";
|
import { Game } from "..";
|
||||||
|
import {
|
||||||
|
BoundingBox,
|
||||||
|
Colliding,
|
||||||
|
Grid,
|
||||||
|
Sprite,
|
||||||
|
ComponentNames,
|
||||||
|
} from "../components";
|
||||||
import { IMAGES, SPRITE_SPECS, SpriteSpec, Sprites } from "../config";
|
import { IMAGES, SPRITE_SPECS, SpriteSpec, Sprites } from "../config";
|
||||||
import { Coord2D } from "../interfaces";
|
import { Coord2D } from "../interfaces";
|
||||||
|
import { Grid as GridSystem, SystemNames } from "../systems";
|
||||||
|
import { colors } from "../utils";
|
||||||
|
|
||||||
export class LockedDoor extends Entity {
|
export class LockedDoor extends Entity {
|
||||||
private static spriteSpec: SpriteSpec = SPRITE_SPECS.get(
|
private static spriteSpec: SpriteSpec = SPRITE_SPECS.get(
|
||||||
@ -13,7 +22,7 @@ export class LockedDoor extends Entity {
|
|||||||
|
|
||||||
this.addComponent(new Grid(gridPosition));
|
this.addComponent(new Grid(gridPosition));
|
||||||
|
|
||||||
this.addComponent(new Colliding());
|
this.addComponent(new Colliding(this.handleCollision.bind(this)));
|
||||||
|
|
||||||
this.addComponent(
|
this.addComponent(
|
||||||
new BoundingBox(
|
new BoundingBox(
|
||||||
@ -42,4 +51,36 @@ export class LockedDoor extends Entity {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handleCollision(game: Game, entity: Entity) {
|
||||||
|
if (entity.name !== EntityNames.Key) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
game.removeEntity(this.id);
|
||||||
|
game.removeEntity(entity.id);
|
||||||
|
|
||||||
|
const grid = this.getComponent<Grid>(ComponentNames.Grid);
|
||||||
|
const gridSystem = game.getSystem<GridSystem>(SystemNames.Grid);
|
||||||
|
const { dimension } = gridSystem;
|
||||||
|
const particles = new Particles({
|
||||||
|
center: gridSystem.gridToScreenPosition(grid.gridPosition),
|
||||||
|
spawnerDimensions: {
|
||||||
|
width: dimension.width / 2,
|
||||||
|
height: dimension.height / 2,
|
||||||
|
},
|
||||||
|
particleCount: 20,
|
||||||
|
spawnerShape: "rectangle",
|
||||||
|
particleShape: "rectangle",
|
||||||
|
particleMeanSpeed: 0.35,
|
||||||
|
particleSpeedVariance: 0.15,
|
||||||
|
particleMeanLife: 80,
|
||||||
|
particleMeanSize: 3,
|
||||||
|
particleSizeVariance: 1,
|
||||||
|
particleLifeVariance: 20,
|
||||||
|
particleColors: [colors.yellow, colors.lightYellow],
|
||||||
|
});
|
||||||
|
|
||||||
|
game.addEntity(particles);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
194
src/engine/entities/Particles.ts
Normal file
194
src/engine/entities/Particles.ts
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
import { Entity, EntityNames } from ".";
|
||||||
|
import {
|
||||||
|
BoundingBox,
|
||||||
|
Component,
|
||||||
|
ComponentNames,
|
||||||
|
Life,
|
||||||
|
Renderable,
|
||||||
|
} from "../components";
|
||||||
|
import { Coord2D, Dimension2D, DrawArgs } from "../interfaces";
|
||||||
|
import { colors } from "../utils";
|
||||||
|
import { normalRandom } from "../utils/random";
|
||||||
|
|
||||||
|
export interface ParticleSpawnOptions {
|
||||||
|
spawnerDimensions: Dimension2D;
|
||||||
|
center: Coord2D;
|
||||||
|
spawnerShape: "circle" | "rectangle";
|
||||||
|
particleShape: "circle" | "rectangle";
|
||||||
|
particleCount: number;
|
||||||
|
particleMeanLife: number;
|
||||||
|
particleLifeVariance: number;
|
||||||
|
particleMeanSize: number;
|
||||||
|
particleSizeVariance: number;
|
||||||
|
particleMeanSpeed: number;
|
||||||
|
particleSpeedVariance: number;
|
||||||
|
particleColors: Array<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_PARTICLE_SPAWN_OPTIONS: ParticleSpawnOptions = {
|
||||||
|
spawnerDimensions: { width: 0, height: 0 },
|
||||||
|
center: { x: 0, y: 0 },
|
||||||
|
spawnerShape: "circle",
|
||||||
|
particleShape: "circle",
|
||||||
|
particleCount: 50,
|
||||||
|
particleMeanLife: 200,
|
||||||
|
particleLifeVariance: 50,
|
||||||
|
particleMeanSize: 12,
|
||||||
|
particleSizeVariance: 1,
|
||||||
|
particleMeanSpeed: 2,
|
||||||
|
particleSpeedVariance: 1,
|
||||||
|
particleColors: [colors.gray, colors.aqua, colors.lightAqua],
|
||||||
|
};
|
||||||
|
|
||||||
|
interface Particle {
|
||||||
|
position: Coord2D;
|
||||||
|
velocity: Coord2D;
|
||||||
|
dimension: Dimension2D;
|
||||||
|
color: string;
|
||||||
|
life: number;
|
||||||
|
shape: "circle" | "rectangle";
|
||||||
|
}
|
||||||
|
|
||||||
|
class ParticleRenderer extends Component implements Renderable {
|
||||||
|
private particles: Array<Particle>;
|
||||||
|
private onDeath?: () => void;
|
||||||
|
|
||||||
|
constructor(particles: Array<Particle> = [], onDeath?: () => void) {
|
||||||
|
super(ComponentNames.Sprite);
|
||||||
|
|
||||||
|
this.particles = particles;
|
||||||
|
this.onDeath = onDeath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public update(dt: number) {
|
||||||
|
this.particles = this.particles.filter((particle) => {
|
||||||
|
particle.position.x += particle.velocity.x * dt;
|
||||||
|
particle.position.y += particle.velocity.y * dt;
|
||||||
|
particle.life -= dt;
|
||||||
|
return particle.life > 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.particles.length === 0 && this.onDeath) {
|
||||||
|
this.onDeath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public draw(ctx: CanvasRenderingContext2D, _drawArgs: DrawArgs) {
|
||||||
|
for (const particle of this.particles) {
|
||||||
|
ctx.fillStyle = particle.color;
|
||||||
|
if (particle.shape === "circle") {
|
||||||
|
ctx.beginPath();
|
||||||
|
|
||||||
|
ctx.ellipse(
|
||||||
|
particle.position.x,
|
||||||
|
particle.position.y,
|
||||||
|
particle.dimension.width / 2,
|
||||||
|
particle.dimension.height / 2,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
Math.PI * 2,
|
||||||
|
);
|
||||||
|
ctx.fill();
|
||||||
|
} else {
|
||||||
|
ctx.fillRect(
|
||||||
|
particle.position.x - particle.dimension.width / 2,
|
||||||
|
particle.position.y - particle.dimension.height / 2,
|
||||||
|
particle.dimension.width,
|
||||||
|
particle.dimension.height,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Particles extends Entity {
|
||||||
|
constructor(options: Partial<ParticleSpawnOptions>) {
|
||||||
|
super(EntityNames.Particles);
|
||||||
|
|
||||||
|
const spawnOptions = {
|
||||||
|
...DEFAULT_PARTICLE_SPAWN_OPTIONS,
|
||||||
|
...options,
|
||||||
|
};
|
||||||
|
const particles = Array(options.particleCount)
|
||||||
|
.fill(0)
|
||||||
|
.map(() => Particles.spawnParticle(spawnOptions));
|
||||||
|
|
||||||
|
this.addComponent(new Life(true));
|
||||||
|
this.addComponent(
|
||||||
|
new ParticleRenderer(particles, () => {
|
||||||
|
const life = this.getComponent<Life>(ComponentNames.Life);
|
||||||
|
life.alive = false;
|
||||||
|
this.addComponent(life);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.addComponent(
|
||||||
|
new BoundingBox(
|
||||||
|
{
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
width: spawnOptions.spawnerDimensions.width,
|
||||||
|
height: spawnOptions.spawnerDimensions.height,
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static spawnParticle(options: ParticleSpawnOptions) {
|
||||||
|
const angle = Math.random() * Math.PI * 2;
|
||||||
|
const speed = normalRandom(
|
||||||
|
options.particleMeanSpeed,
|
||||||
|
options.particleSpeedVariance,
|
||||||
|
);
|
||||||
|
const life = normalRandom(
|
||||||
|
options.particleMeanLife,
|
||||||
|
options.particleLifeVariance,
|
||||||
|
);
|
||||||
|
const size = normalRandom(
|
||||||
|
options.particleMeanSize,
|
||||||
|
options.particleSizeVariance,
|
||||||
|
);
|
||||||
|
const color =
|
||||||
|
options.particleColors[
|
||||||
|
Math.floor(Math.random() * options.particleColors.length)
|
||||||
|
];
|
||||||
|
const position = {
|
||||||
|
x: options.center.x + Math.cos(angle) * options.spawnerDimensions.width,
|
||||||
|
y: options.center.y + Math.sin(angle) * options.spawnerDimensions.height,
|
||||||
|
};
|
||||||
|
if (options.spawnerShape === "rectangle") {
|
||||||
|
// determine a random position on the edge of the spawner based on the angle
|
||||||
|
const halfWidth = options.spawnerDimensions.width / 2;
|
||||||
|
const halfHeight = options.spawnerDimensions.height / 2;
|
||||||
|
|
||||||
|
if (angle < Math.PI / 4 || angle > (Math.PI * 7) / 4) {
|
||||||
|
position.x += halfWidth;
|
||||||
|
position.y += Math.tan(angle) * halfWidth;
|
||||||
|
} else if (angle < (Math.PI * 3) / 4) {
|
||||||
|
position.y += halfHeight;
|
||||||
|
position.x += halfHeight / Math.tan(angle);
|
||||||
|
} else if (angle < (Math.PI * 5) / 4) {
|
||||||
|
position.x -= halfWidth;
|
||||||
|
position.y -= Math.tan(angle) * halfWidth;
|
||||||
|
} else {
|
||||||
|
position.y -= halfHeight;
|
||||||
|
position.x -= halfHeight / Math.tan(angle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
position,
|
||||||
|
velocity: {
|
||||||
|
x: Math.cos(angle) * speed,
|
||||||
|
y: Math.sin(angle) * speed,
|
||||||
|
},
|
||||||
|
color,
|
||||||
|
life,
|
||||||
|
dimension: { width: size, height: size },
|
||||||
|
shape: options.particleShape,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -7,3 +7,5 @@ export * from "./LambdaFactory";
|
|||||||
export * from "./Key";
|
export * from "./Key";
|
||||||
export * from "./LockedDoor";
|
export * from "./LockedDoor";
|
||||||
export * from "./Curry";
|
export * from "./Curry";
|
||||||
|
export * from "./FunctionApplication";
|
||||||
|
export * from "./Particles";
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { System, SystemNames } from ".";
|
import { System, SystemNames } from ".";
|
||||||
import { Game } from "..";
|
import { Game } from "..";
|
||||||
import { Entity, EntityNames } from "../entities";
|
import { Entity, EntityNames } from "../entities";
|
||||||
import { BoundingBox, ComponentNames, Grid } from "../components";
|
import { BoundingBox, Colliding, ComponentNames, Grid } from "../components";
|
||||||
|
|
||||||
const collisionMap: Record<string, Set<string>> = {
|
const collisionMap: Record<string, Set<string>> = {
|
||||||
[EntityNames.Key]: new Set([EntityNames.LockedDoor]),
|
[EntityNames.Key]: new Set([EntityNames.LockedDoor]),
|
||||||
@ -70,34 +70,14 @@ export class Collision extends System {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const keyDoorPair = [EntityNames.Key, EntityNames.LockedDoor].map((x) =>
|
[entity, otherEntity].forEach((e) => {
|
||||||
[entity, otherEntity].find((y) => y.name === x),
|
if (!e.hasComponent(ComponentNames.Colliding)) {
|
||||||
);
|
return;
|
||||||
const [key, door] = keyDoorPair;
|
}
|
||||||
if (key && door) {
|
const colliding = e.getComponent<Colliding>(ComponentNames.Colliding);
|
||||||
this.handleKeyDoorCollision(key, door, game);
|
if (colliding?.onCollision) {
|
||||||
}
|
colliding.onCollision(game, e === entity ? otherEntity : entity);
|
||||||
|
}
|
||||||
const curryPlayerPair = [EntityNames.Curry, EntityNames.Player].map((x) =>
|
});
|
||||||
[entity, otherEntity].find((y) => y.name === x),
|
|
||||||
);
|
|
||||||
const [curry, player] = curryPlayerPair;
|
|
||||||
if (curry && player) {
|
|
||||||
this.handleCurryPlayerCollision(curry, player, game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleKeyDoorCollision(key: Entity, door: Entity, game: Game) {
|
|
||||||
game.removeEntity(key.id);
|
|
||||||
game.removeEntity(door.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleCurryPlayerCollision(
|
|
||||||
curry: Entity,
|
|
||||||
_player: Entity,
|
|
||||||
game: Game,
|
|
||||||
) {
|
|
||||||
game.removeEntity(curry.id);
|
|
||||||
game.stop();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -309,7 +309,7 @@ export class Grid extends System {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private gridToScreenPosition(gridPosition: Coord2D) {
|
public gridToScreenPosition(gridPosition: Coord2D) {
|
||||||
const { width, height } = this.dimension;
|
const { width, height } = this.dimension;
|
||||||
return {
|
return {
|
||||||
x: gridPosition.x * width + width / 2,
|
x: gridPosition.x * width + width / 2,
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { SystemNames, System } from ".";
|
import { Grid as GridSystem, SystemNames, System } from ".";
|
||||||
import { Game } from "..";
|
import { Game } from "..";
|
||||||
import { ComponentNames, Grid, Interactable } from "../components";
|
import { ComponentNames, Grid, Interactable } from "../components";
|
||||||
import { Control } from "../components/Control";
|
import { Control } from "../components/Control";
|
||||||
import { Action, KeyConstants } from "../config";
|
import { Action, KeyConstants } from "../config";
|
||||||
import { Entity } from "../entities";
|
import { Entity, Particles } from "../entities";
|
||||||
import { Coord2D, Direction } from "../interfaces";
|
import { Coord2D, Direction } from "../interfaces";
|
||||||
|
import { colors } from "../utils";
|
||||||
|
|
||||||
export class Input extends System {
|
export class Input extends System {
|
||||||
private keys: Set<string>;
|
private keys: Set<string>;
|
||||||
@ -31,7 +32,7 @@ export class Input extends System {
|
|||||||
|
|
||||||
public update(_dt: number, game: Game) {
|
public update(_dt: number, game: Game) {
|
||||||
game.forEachEntityWithComponent(ComponentNames.Control, (entity) =>
|
game.forEachEntityWithComponent(ComponentNames.Control, (entity) =>
|
||||||
this.handleMovement(entity),
|
this.handleMovement(entity, game),
|
||||||
);
|
);
|
||||||
game.forEachEntityWithComponent(ComponentNames.Interactable, (entity) =>
|
game.forEachEntityWithComponent(ComponentNames.Interactable, (entity) =>
|
||||||
this.handleInteraction(entity),
|
this.handleInteraction(entity),
|
||||||
@ -57,7 +58,7 @@ export class Input extends System {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public handleMovement(entity: Entity) {
|
public handleMovement(entity: Entity, game: Game) {
|
||||||
const controlComponent = entity.getComponent<Control>(
|
const controlComponent = entity.getComponent<Control>(
|
||||||
ComponentNames.Control,
|
ComponentNames.Control,
|
||||||
);
|
);
|
||||||
@ -103,6 +104,25 @@ export class Input extends System {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (moveUp || moveLeft || moveRight || moveDown) {
|
||||||
|
const gridPosition = gridComponent.gridPosition;
|
||||||
|
const gridSystem = game.getSystem<GridSystem>(SystemNames.Grid);
|
||||||
|
const particles = new Particles({
|
||||||
|
center: gridSystem.gridToScreenPosition(gridPosition),
|
||||||
|
particleCount: 5,
|
||||||
|
particleShape: "circle",
|
||||||
|
particleMeanSpeed: 0.05,
|
||||||
|
particleSpeedVariance: 0.005,
|
||||||
|
particleMeanLife: 120,
|
||||||
|
particleMeanSize: 5,
|
||||||
|
particleSizeVariance: 2,
|
||||||
|
particleLifeVariance: 30,
|
||||||
|
particleColors: [colors.gray, colors.darkGray],
|
||||||
|
});
|
||||||
|
|
||||||
|
game.addEntity(particles);
|
||||||
|
}
|
||||||
|
|
||||||
entity.addComponent(gridComponent);
|
entity.addComponent(gridComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
18
src/engine/systems/Life.ts
Normal file
18
src/engine/systems/Life.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { System, SystemNames } from ".";
|
||||||
|
import { Game } from "..";
|
||||||
|
import { Life as LifeComponent, ComponentNames } from "../components";
|
||||||
|
|
||||||
|
export class Life extends System {
|
||||||
|
constructor() {
|
||||||
|
super(SystemNames.Life);
|
||||||
|
}
|
||||||
|
|
||||||
|
public update(_dt: number, game: Game) {
|
||||||
|
game.forEachEntityWithComponent(ComponentNames.Life, (entity) => {
|
||||||
|
const life = entity.getComponent<LifeComponent>(ComponentNames.Life);
|
||||||
|
if (!life.alive) {
|
||||||
|
game.removeEntity(entity.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,7 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
ComponentNames,
|
ComponentNames,
|
||||||
Highlight,
|
Highlight,
|
||||||
Sprite,
|
Renderable,
|
||||||
} from "../components";
|
} from "../components";
|
||||||
import { Game } from "..";
|
import { Game } from "..";
|
||||||
import { clamp } from "../utils";
|
import { clamp } from "../utils";
|
||||||
@ -22,7 +22,7 @@ export class Render extends System {
|
|||||||
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
|
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
|
||||||
|
|
||||||
game.forEachEntityWithComponent(ComponentNames.Sprite, (entity) => {
|
game.forEachEntityWithComponent(ComponentNames.Sprite, (entity) => {
|
||||||
const sprite = entity.getComponent<Sprite>(ComponentNames.Sprite);
|
const sprite = entity.getComponent<Renderable>(ComponentNames.Sprite);
|
||||||
sprite.update(dt);
|
sprite.update(dt);
|
||||||
|
|
||||||
const boundingBox = entity.getComponent<BoundingBox>(
|
const boundingBox = entity.getComponent<BoundingBox>(
|
||||||
|
@ -5,4 +5,5 @@ export namespace SystemNames {
|
|||||||
export const Grid = "Grid";
|
export const Grid = "Grid";
|
||||||
export const GridSpawner = "GridSpawner";
|
export const GridSpawner = "GridSpawner";
|
||||||
export const Collision = "Collision";
|
export const Collision = "Collision";
|
||||||
|
export const Life = "Life";
|
||||||
}
|
}
|
||||||
|
@ -6,3 +6,4 @@ export * from "./FacingDirection";
|
|||||||
export * from "./Grid";
|
export * from "./Grid";
|
||||||
export * from "./GridSpawner";
|
export * from "./GridSpawner";
|
||||||
export * from "./Collision";
|
export * from "./Collision";
|
||||||
|
export * from "./Life";
|
||||||
|
22
src/engine/utils/colors.ts
Normal file
22
src/engine/utils/colors.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// gruvbox dark
|
||||||
|
export const colors = {
|
||||||
|
bg: "#282828",
|
||||||
|
fg: "#ebdbb2",
|
||||||
|
red: "#cc241d",
|
||||||
|
green: "#98971a",
|
||||||
|
yellow: "#d79921",
|
||||||
|
blue: "#458588",
|
||||||
|
purple: "#b16286",
|
||||||
|
aqua: "#689d6a",
|
||||||
|
orange: "#d65d0e",
|
||||||
|
gray: "#a89984",
|
||||||
|
lightGray: "#928374",
|
||||||
|
darkGray: "#3c3836",
|
||||||
|
lightRed: "#fb4934",
|
||||||
|
lightGreen: "#b8bb26",
|
||||||
|
lightYellow: "#fabd2f",
|
||||||
|
lightBlue: "#83a598",
|
||||||
|
lightPurple: "#d3869b",
|
||||||
|
lightAqua: "#8ec07c",
|
||||||
|
lightOrange: "#fe8019",
|
||||||
|
};
|
@ -2,3 +2,5 @@ export * from "./clamp";
|
|||||||
export * from "./dotProduct";
|
export * from "./dotProduct";
|
||||||
export * from "./rotateVector";
|
export * from "./rotateVector";
|
||||||
export * from "./modal";
|
export * from "./modal";
|
||||||
|
export * from "./colors";
|
||||||
|
export * from "./random";
|
||||||
|
9
src/engine/utils/random.ts
Normal file
9
src/engine/utils/random.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export const normalRandom = (mean: number, stdDev: number, maxStdDevs = 2) => {
|
||||||
|
const [u, v] = [0, 0].map(() => Math.random() + 1e-12);
|
||||||
|
const normal =
|
||||||
|
mean + stdDev * Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);
|
||||||
|
return Math.min(
|
||||||
|
mean + maxStdDevs * stdDev,
|
||||||
|
Math.max(mean - maxStdDevs * stdDev, normal),
|
||||||
|
);
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user