render build ghost at cursor with sub-tile precision

Bypass the snap-to-tile in TransformHandler by adding
screenToWorldCoordinatesFloat. Each render frame, BuildPreviewController
re-emits the ghost preview at the cursor's exact world position
(adjusted by -0.5 to cancel the shader's tile-center offset). Buildable
validation still runs on the snapped tile at the 50ms throttle, but the
icon now follows the cursor 1:1 instead of stepping tile-to-tile.
This commit is contained in:
evanpelle
2026-05-17 19:13:29 -07:00
parent b8d72d3a4e
commit 4dc4810bcc
2 changed files with 57 additions and 12 deletions
+18 -10
View File
@@ -115,17 +115,25 @@ export class TransformHandler {
}
screenToWorldCoordinates(screenX: number, screenY: number): Cell {
const f = this.screenToWorldCoordinatesFloat(screenX, screenY);
return new Cell(Math.floor(f.x), Math.floor(f.y));
}
/** Like screenToWorldCoordinates but returns sub-tile precision. */
screenToWorldCoordinatesFloat(
screenX: number,
screenY: number,
): { x: number; y: number } {
const canvasCoords = this.screenToCanvasCoordinates(screenX, screenY);
const centerX =
(canvasCoords.x - this.game.width() / 2) / this.scale + this.offsetX;
const centerY =
(canvasCoords.y - this.game.height() / 2) / this.scale + this.offsetY;
const gameX = centerX + this.game.width() / 2;
const gameY = centerY + this.game.height() / 2;
return new Cell(Math.floor(gameX), Math.floor(gameY));
const gameX =
(canvasCoords.x - this.game.width() / 2) / this.scale +
this.offsetX +
this.game.width() / 2;
const gameY =
(canvasCoords.y - this.game.height() / 2) / this.scale +
this.offsetY +
this.game.height() / 2;
return { x: gameX, y: gameY };
}
canvasToScreenCoordinates(