Separates game loop from doodler draw loop. Each state is in charge of registering and unregistering layers

This commit is contained in:
2025-02-13 06:07:42 -07:00
parent 43a5268ed5
commit 3befb69f51
10 changed files with 421 additions and 130 deletions

View File

@@ -1,3 +1,7 @@
import { getContext } from "../lib/context.ts";
import { TrackSystem } from "../track/system.ts";
import { Train } from "../train.old.ts";
export class StateMachine<T> {
private _states: Map<T, State<T>> = new Map();
private currentState?: State<T>;
@@ -6,6 +10,13 @@ export class StateMachine<T> {
this.currentState?.update(dt, ctx);
}
optimizePerformance(percent: number) {
const ctx = getContext() as { trains: Train[]; track: TrackSystem };
if (percent < 0.5) {
ctx.track.optimize(percent);
}
}
get current() {
return this.currentState;
}

View File

@@ -33,6 +33,8 @@ export class EditTrackState extends State<States> {
private ghostRotated = false;
private closestEnd?: End;
layers: number[] = [];
override update(dt: number): void {
const inputManager = getContextItem<InputManager>("inputManager");
const track = getContextItem<TrackSystem>("track");
@@ -138,22 +140,6 @@ export class EditTrackState extends State<States> {
this.ghostSegment = undefined;
this.ghostRotated = false;
}
this.selectedSegment?.draw();
if (this.ghostSegment) {
doodler.drawWithAlpha(0.5, () => {
if (!this.ghostSegment) return;
this.ghostSegment.draw();
if (getContextItemOrDefault("debug", false)) {
const colors = getContextItem<string[]>("colors");
for (
const [i, point] of this.ghostSegment.points.entries() ?? []
) {
doodler.fillCircle(point, 4, { color: colors[i + 3] });
}
}
});
}
}
// manipulate only end of segment while maintaining length
@@ -263,13 +249,34 @@ export class EditTrackState extends State<States> {
track.translate(translation);
}
track.draw(true);
// TODO
// Draw ui
// Draw track points
// Draw track tangents
}
override start(): void {
const doodler = getContextItem<Doodler>("doodler");
this.layers.push(
doodler.createLayer(() => {
this.selectedSegment?.draw();
if (this.ghostSegment) {
doodler.drawWithAlpha(0.5, () => {
if (!this.ghostSegment) return;
this.ghostSegment.draw();
if (getContextItemOrDefault("debug", false)) {
const colors = getContextItem<string[]>("colors");
for (
const [i, point] of this.ghostSegment.points.entries() ?? []
) {
doodler.fillCircle(point, 4, { color: colors[i + 3] });
}
}
});
}
track.draw(true);
}),
);
setContextItem("trackSegments", [
undefined,
new StraightTrack(),
@@ -376,9 +383,19 @@ export class EditTrackState extends State<States> {
// TODO
// Cache trains and save
// const trackCount = 2000;
// for (let i = 0; i < trackCount; i++) {
// const seg = new StraightTrack();
// track.registerSegment(seg);
// }
}
redoBuffer: TrackSegment[] = [];
override stop(): void {
for (const layer of this.layers) {
getContextItem<Doodler>("doodler").deleteLayer(layer);
}
const inputManager = getContextItem<InputManager>("inputManager");
inputManager.offKey("e");
inputManager.offKey("w");

View File

@@ -1,5 +1,6 @@
import { Doodler, Vector } from "@bearmetal/doodler";
import { bootstrapInputs } from "../../inputs.ts";
import { setContextItem } from "../../lib/context.ts";
import { getContextItem, setContextItem } from "../../lib/context.ts";
import { InputManager } from "../../lib/input.ts";
import { ResourceManager } from "../../lib/resources.ts";
import { StraightTrack } from "../../track/shapes.ts";
@@ -13,6 +14,8 @@ export class LoadState extends State<States> {
States.RUNNING,
]);
layers: number[] = [];
override update(): void {
// noop
}
@@ -37,6 +40,14 @@ export class LoadState extends State<States> {
resources.ready().then(() => {
this.stateMachine.transitionTo(States.RUNNING);
});
const doodler = getContextItem<Doodler>("doodler");
this.layers.push(doodler.createLayer((_, __, dTime) => {
doodler.clearRect(new Vector(0, 0), doodler.width, doodler.height);
doodler.fillRect(new Vector(0, 0), doodler.width, doodler.height, {
color: "#302040",
});
}));
}
override stop(): void {
// noop

View File

@@ -1,3 +1,4 @@
import { Doodler } from "@bearmetal/doodler";
import { getContext, getContextItem } from "../../lib/context.ts";
import { InputManager } from "../../lib/input.ts";
import { TrackSystem } from "../../track/system.ts";
@@ -14,26 +15,40 @@ export class RunningState extends State<States> {
States.PAUSED,
States.EDIT_TRACK,
]);
layers: number[] = [];
override update(dt: number): void {
const ctx = getContext() as { trains: Train[]; track: TrackSystem };
// const ctx = getContext() as { trains: DotFollower[]; track: TrackSystem };
const input = getContextItem<InputManager>("inputManager");
// TODO
// Update trains
// Update world
// Handle input
// Draw (maybe via a layer system that syncs with doodler)
ctx.track.draw();
for (const train of ctx.trains) {
// if (input.getKeyState("ArrowUp")) {
// train.acceleration.x += 10;
// }
train.move(dt);
train.draw();
}
// Monitor world events
for (const train of ctx.trains) {
train.move(dt);
}
}
override start(): void {
const doodler = getContextItem<Doodler>("doodler");
this.layers.push(
doodler.createLayer(() => {
const track = getContextItem<TrackSystem>("track");
track.draw();
}),
doodler.createLayer(() => {
const trains = getContextItem<Train[]>("trains");
for (const train of trains) {
// if (input.getKeyState("ArrowUp")) {
// train.acceleration.x += 10;
// }
train.draw();
}
}),
);
// noop
const inputManager = getContextItem<InputManager>("inputManager");
const track = getContextItem<TrackSystem>("track");
@@ -46,7 +61,7 @@ export class RunningState extends State<States> {
const train = new Train(track.path, [new RedEngine(), new Tender()]);
ctx.trains.push(train);
});
// const trainCount = 1000;
// const trainCount = 2000;
// for (let i = 0; i < trainCount; i++) {
// const train = new Train(track.path, [new RedEngine(), new Tender()]);
// ctx.trains.push(train);
@@ -66,6 +81,8 @@ export class RunningState extends State<States> {
});
}
override stop(): void {
// noop
for (const layer of this.layers) {
getContextItem<Doodler>("doodler").deleteLayer(layer);
}
}
}