Add stats overlay with cursor position, TPS, and colony info

This commit is contained in:
Jared Miller 2026-03-11 17:37:33 -04:00
parent d91b5a9b5e
commit 9f958089c0
Signed by: shmup
GPG key ID: 22B5C6D66A38B06C
4 changed files with 102 additions and 2 deletions

View file

@ -45,7 +45,26 @@
width: 65px;
}
.reset-btn {
#stats {
font-family: monospace;
color: #777;
padding: 8px 12px;
font-size: 11px;
line-height: 1.8;
border-bottom: 1px solid #1a1a1a;
}
#stats .label {
display: inline-block;
width: 65px;
color: #555;
}
#stats .value {
color: #c43c3c;
}
.reset-btn {
width: calc(100% - 16px);
margin: 12px 8px;
padding: 6px;

View file

@ -1,6 +1,7 @@
import Config from "./Config";
import GUI from "./GUI";
import Renderer from "./Renderer";
import StatsOverlay from "./StatsOverlay";
import AntsComputeScene from "./scenes/AntsComputeScene";
import AntsDiscretizeScene from "./scenes/AntsDiscretizeScene";
import DrawScene from "./scenes/DrawScene";
@ -25,6 +26,7 @@ export default new (class App {
);
private scenes!: SceneCollection;
private gui: GUI = new GUI();
private statsOverlay: StatsOverlay = new StatsOverlay();
private renderLoop = (time: number): void => this.render(time);
private lastTime: number = 0;
private queuedSimSteps: number = 0;
@ -82,6 +84,7 @@ export default new (class App {
}
this.renderer.renderSimulation(this.scenes);
this.statsOverlay.recordTick();
}
private render(time: number) {
@ -105,6 +108,11 @@ export default new (class App {
this.renderer.renderToScreen(this.scenes);
this.statsOverlay.update(
this.scenes.screen.pointerPosition,
this.renderer.colonyStatsData,
);
this.lastTime = time;
}
})();

View file

@ -1,7 +1,7 @@
import type { WebGLRenderTarget } from "three";
import * as THREE from "three";
import type { SceneCollection } from "./App";
import ColonyStats from "./ColonyStats";
import ColonyStats, { type ColonyStatsData } from "./ColonyStats";
import Config from "./Config";
import {
MAT_AIR,
@ -406,6 +406,10 @@ export default class Renderer {
}
}
public get colonyStatsData(): ColonyStatsData {
return this.colonyStats.data;
}
static convertNumberToFloatString(n: number): string {
return n.toFixed(8);
}

69
src/StatsOverlay.ts Normal file
View file

@ -0,0 +1,69 @@
import type { ColonyStatsData } from "./ColonyStats";
import Config from "./Config";
export default class StatsOverlay {
private el: HTMLElement;
private cursorEl: HTMLElement;
private tpsEl: HTMLElement;
private antsEl: HTMLElement;
private carryingEl: HTMLElement;
private tickTimestamps: number[] = [];
constructor() {
this.el = document.createElement("div");
this.el.id = "stats";
// insert after #info
// biome-ignore lint/style/noNonNullAssertion: #info exists in index.html
const info = document.getElementById("info")!;
info.after(this.el);
this.cursorEl = this.addLine("cursor");
this.tpsEl = this.addLine("tps");
this.antsEl = this.addLine("ants");
this.carryingEl = this.addLine("carrying");
}
private addLine(label: string): HTMLElement {
const line = document.createElement("div");
line.innerHTML = `<span class="label">${label}</span><span class="value">—</span>`;
this.el.appendChild(line);
// biome-ignore lint/style/noNonNullAssertion: .value span created by innerHTML above
return line.querySelector(".value")!;
}
public recordTick(): void {
const now = performance.now();
this.tickTimestamps.push(now);
// keep last 60 timestamps for averaging
if (this.tickTimestamps.length > 60) {
this.tickTimestamps.shift();
}
}
public update(
pointerPosition: { x: number; y: number },
colonyStats: ColonyStatsData,
): void {
const gridX = Math.floor(pointerPosition.x * Config.worldSize);
const gridY = Math.floor(pointerPosition.y * Config.worldSize);
this.cursorEl.textContent = `${gridX}, ${gridY}`;
// tps from tick timestamps
if (this.tickTimestamps.length >= 2) {
const span =
this.tickTimestamps[this.tickTimestamps.length - 1] -
this.tickTimestamps[0];
const tps =
span > 0 ? ((this.tickTimestamps.length - 1) / span) * 1000 : 0;
this.tpsEl.textContent = `${Math.round(tps)}`;
}
this.antsEl.textContent = `${colonyStats.totalAnts}`;
const carrying = Math.round(
colonyStats.foragerRatio * colonyStats.totalAnts,
);
this.carryingEl.textContent = `${carrying}`;
}
}