Setting to stop the camera drifting toward the pointer (t7)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 01:15:36 -04:00
co-authored by Claude Opus 5.5
parent 1a3a848bad
commit 40d9647b73
7 changed files with 47 additions and 6 deletions
+1 -1
View File
@@ -379,7 +379,7 @@ Saves in IndexedDB, world files (export / import) and the blueprint library.
Starting, loading and quitting a game; modal dialogs; the pause menu.
- functions: showLoading, enterGame, startNewWorld, startFromSave, quitToTitle, refreshContinue, anyModal, openModal, closeModal, closeAllModals, topModal, dialog, openPause, renderWorlds
### src/ui/settings.js (122 lines)
### src/ui/settings.js (123 lines)
The settings screen.
- functions: openSettings, renderSettings, applySettings, toggleFullscreen, confirmWipe
- variables: SET_TABS, setTab
+9
View File
@@ -4,6 +4,15 @@ What agents have done, newest first. Add an entry for every piece of work: the d
note or request it was for, what changed, the files touched, the tests run, and anything left
open or not verified. Keep each entry short; the git history has the details.
## 2026-10-24: t7 Option to turn off smooth pan following the mouse (Claude Code, Codex)
- New setting `cameraLead` (Display: "Camera looks toward the pointer", on by default):
`src/ui/state.js`, `src/ui/settings.js`; `src/ui/loop.js` gives the camera no look-ahead when
it is off (mouse and gamepad).
- `test/play.py`: the toggle saves; with it off the camera stays on the bot with the mouse in a
corner; with it on the camera moves toward the corner.
- Tests: headless suites and `play.py` pass; `t_smooth` half-resolution flake as before.
## 2026-10-24: t9 Script to run the local server for testing (Claude Code, Codex)
- New `tools/serve.py`: builds, serves `dist/` on 127.0.0.1 (port 8000, or the next free one up
+2 -2
View File
@@ -45,8 +45,8 @@ function frame(now) {
} else acc = 0;
drainEvents();
let lookX = 0, lookY = 0;
if (GAME.usingPad) { lookX = GAME.padAim.x * 0.5; lookY = GAME.padAim.y * 0.5; }
else if (GAME.mouse.in && toolId() !== 'build' && !GAME.paste && !GAME.selDrag && !selModeHeld()) { const dpr = R.canvas.width / window.innerWidth; lookX = (GAME.mouse.x - window.innerWidth / 2) * dpr / R.zp; lookY = (GAME.mouse.y - window.innerHeight / 2) * dpr / R.zp; }
if (SET.cameraLead && GAME.usingPad) { lookX = GAME.padAim.x * 0.5; lookY = GAME.padAim.y * 0.5; }
else if (SET.cameraLead && GAME.mouse.in && toolId() !== 'build' && !GAME.paste && !GAME.selDrag && !selModeHeld()) { const dpr = R.canvas.width / window.innerWidth; lookX = (GAME.mouse.x - window.innerWidth / 2) * dpr / R.zp; lookY = (GAME.mouse.y - window.innerHeight / 2) * dpr / R.zp; }
// hold the camera's look-ahead still while dragging out a line, box or blueprint
// While dragging, the camera stays put unless the bot itself is flying, so the
// grid under the cursor does not slide around mid-line.
+1
View File
@@ -11,6 +11,7 @@ const SET_TABS = {
{ k: 'cursorLabel', label: 'Name next to the cursor', d: 'Shows what you are pointing at right beside the cursor.', type: 'toggle' },
{ k: 'showPerf', label: 'Performance stats', d: 'Frame time, simulation time and active areas.', type: 'toggle' },
{ k: 'cameraSmooth', label: 'Camera smoothing', type: 'range', min: 0, max: 0.95, step: 0.05, fmt: v => Math.round(v * 100) + '%' },
{ k: 'cameraLead', label: 'Camera looks toward the pointer', d: 'The view drifts toward the mouse or right stick. Turn off to keep the bot centred.', type: 'toggle' },
{ k: 'shake', label: 'Screen shake', type: 'toggle' },
],
Graphics: [
+1 -1
View File
@@ -3,7 +3,7 @@
// ---------- Settings ----------
const SET_DEFAULT = {
zoom: 12, integerScale: true, renderScale: 1, uiScale: 1, fpsCap: 0, showPerf: false, shake: true, cameraSmooth: 0.7,
zoom: 12, integerScale: true, renderScale: 1, uiScale: 1, fpsCap: 0, showPerf: false, shake: true, cameraSmooth: 0.7, cameraLead: true,
look: 'smooth', lighting: 'full', lightEvery: 1, ambient: 0.12, daylight: 1, glow: 'low', grain: 1, incandescence: true, particles: true,
thermalPalette: 'inferno', simSpeed: 1, maxSteps: 3,
volMaster: 0.8, volMusic: 0.5, volSfx: 0.8, muted: false, muteUnfocused: true,
+8 -2
View File
@@ -1,9 +1,15 @@
---
id: t7
title: Option to turn off smooth pan following the mouse
status: Todo
status: Review
created: 2026-10-24
updated: 2026-10-24T04:41:45Z
updated: 2026-10-24T05:15:28Z
order: 1
report: |
(agent) New setting: Settings > Display > "Camera looks toward the pointer". Turn it off and
the camera stays centred on the bot instead of drifting toward the mouse (or right stick).
It is on by default for new players; your choice is saved in the browser.
To check: turn it off, move the mouse to a screen corner while flying: the view shouldn't move.
---
## Context
+25
View File
@@ -121,6 +121,31 @@ with sync_playwright() as p:
ev("SET.zoom = 4; resizeRender(); R.snapNext = true;") # the rest of this play-through was laid out at zoom 4
ev(f"SET.look = '{look}'"); pg.wait_for_timeout(200)
check(f'the {look} look is drawing', ev('R.smoothOn') == (look == 'smooth'), ev('[SET.look, R.smoothOn, SM.ok]'))
# Camera lead changes the view, not the bot or its aim.
mouse = ev('[GAME.mouse.x, GAME.mouse.y]')
ev("openSettings('Display')")
pg.screenshot(path=str(out / '00-camera-settings.png'))
pg.click('#set-cameraLead')
check('camera lead toggle saves its value', ev("!SET.cameraLead && JSON.parse(localStorage.getItem('deepgrain.settings')).cameraLead === false"))
pg.keyboard.press('Escape')
paused = ev('GAME.paused')
ev("GAME.paused = true; SET.cameraLead = false; $('cards').replaceChildren()") # cards cover the corner
pg.mouse.move(1240, 740); pg.wait_for_timeout(1000)
check('camera test pointer reaches the world', ev('GAME.mouse.in && GAME.mouse.x === 1240 && GAME.mouse.y === 740'))
camera_offset = """(() => {
const v = viewCells(), x = bot.x + bot.w / 2 - v.w / 2, y = bot.y + bot.h / 2 - v.h / 2;
const tx = v.w >= W ? (W - v.w) / 2 : Math.max(0, Math.min(W - v.w, x));
const ty = v.h >= H ? (H - v.h) / 2 : Math.max(0, Math.min(H - v.h, y));
return [R.camX - tx, R.camY - ty];
})()"""
centred = ev(camera_offset)
check('camera stays centred with pointer lead off', all(abs(d) < 2 for d in centred), centred)
ev('SET.cameraLead = true')
pg.wait_for_timeout(1000)
led = ev(camera_offset)
check('camera leads toward the pointer when enabled', all(b - a > 2 for a, b in zip(centred, led)), led)
pg.mouse.move(*mouse)
ev(f'GAME.paused = {json.dumps(paused)}; saveSettings()')
def cell_to_screen(x, y):
return ev(f'(() => {{ const dpr = R.canvas.width / innerWidth; const s = worldToScreenPx({x}+0.5, {y}+0.5); return [s.x/dpr, s.y/dpr]; }})()')
def blk(bx, by): return cell_to_screen(bx * 4 + 2, by * 4 + 2)