fix(render): keep build-cost chip a fixed screen size

Chip used a world-space scale and y-offset, so it shrank as the camera
zoomed out and slid under the cursor. Now both the scale and the
downward offset are divided by zoom each frame, mirroring the existing
attack-troop-label pattern. Tunable via ghostCost in render-settings.
This commit is contained in:
evanpelle
2026-05-28 13:19:22 -07:00
parent 20bc311caf
commit 4cee61c7d1
3 changed files with 16 additions and 7 deletions
+4
View File
@@ -229,6 +229,10 @@ export interface RenderSettings {
minScreenScale: number; // minimum world-scale when zoomed out (prevents vanishing)
cullZoom: number; // popups hidden below this zoom level
};
ghostCost: {
screenScale: number; // screen-relative em scale; divided by zoom each frame for fixed on-screen size
screenYOffset: number; // screen-relative downward offset from icon center; divided by zoom each frame for fixed on-screen gap
};
spawnOverlay: {
highlightRadius: number; // tile highlight radius (squared internally)
highlightAlpha: number; // tile highlight opacity (01)
+8 -7
View File
@@ -38,10 +38,6 @@ const CONQUEST_Y_OFFSET = 8;
/** World-space font size for conquest popups. */
const CONQUEST_SCALE = 6;
const CONQUEST_OUTLINE_WIDTH = 2.0;
/** Tiles below the ghost icon center for the cost label. */
const GHOST_COST_Y_OFFSET = 3;
/** World-space font size — smaller than popups so it sits unobtrusively under the icon. */
const GHOST_COST_SCALE = 4;
/** Matches player-name outline width for a consistent UI look. */
const GHOST_COST_OUTLINE_WIDTH = 1.4;
/**
@@ -351,9 +347,10 @@ export class WorldTextPass {
}
// The vertex shader adds +0.5 to (x, y) for tile-center alignment, so we
// pass raw tile coords here — same convention as the other popup entries.
// Y offset is applied in rebuildInstances (zoom-relative).
this.ghostCostLabel = {
x: label.tileX,
y: label.tileY + GHOST_COST_Y_OFFSET,
y: label.tileY,
text: renderNumber(label.cost),
colorR: r,
colorG: g,
@@ -474,8 +471,12 @@ export class WorldTextPass {
// Ghost cost label — persistent, no fade or rise. layoutString already
// centers cursors around 0, so passing the tile coord places the text
// centered on the tile (vertex shader adds the +0.5 tile-center offset).
// Scale is divided by zoom so the chip keeps a constant on-screen size.
const label = this.ghostCostLabel;
if (label) {
const invZoom = 1 / Math.max(zoom, 0.0001);
const ghostScale = this.settings.ghostCost.screenScale * invZoom;
const ghostY = label.y + this.settings.ghostCost.screenYOffset * invZoom;
layoutString(
label.text,
this.glyph,
@@ -490,14 +491,14 @@ export class WorldTextPass {
const off = count * FLOATS_PER_INSTANCE;
this.instanceData[off + 0] = label.x;
this.instanceData[off + 1] = label.y;
this.instanceData[off + 1] = ghostY;
this.instanceData[off + 2] = this.cursors[i];
this.instanceData[off + 3] = this.charCodes[i];
this.instanceData[off + 4] = 1;
this.instanceData[off + 5] = label.colorR;
this.instanceData[off + 6] = label.colorG;
this.instanceData[off + 7] = label.colorB;
this.instanceData[off + 8] = GHOST_COST_SCALE;
this.instanceData[off + 8] = ghostScale;
this.instanceData[off + 9] = GHOST_COST_OUTLINE_WIDTH;
count++;
}
@@ -244,6 +244,10 @@
"minScreenScale": 0.15,
"cullZoom": 0.3
},
"ghostCost": {
"screenScale": 30,
"screenYOffset": 50
},
"spawnOverlay": {
"highlightRadius": 9,
"highlightAlpha": 1.0,