Update via to v0.4.0 and decouple tick rate from snake speed

Use via.OnKeyDownMap for snake keybindings, replacing the manual
dataExpr/rawDataAttr workaround. Window-scoped key handling removes
the need for tabindex/focus hacks, and WithPreventDefault on arrow
keys prevents page scrolling during gameplay.

Introduce a 60 FPS tick loop with a separate snake movement speed
(7 cells/s) so direction input is polled every frame but game state
only advances at the configured rate.
This commit is contained in:
Ryan Hamamura
2026-02-02 09:18:13 -10:00
parent 7e78664534
commit 038c4b3f22
4 changed files with 28 additions and 70 deletions

View File

@@ -5,7 +5,10 @@ import (
)
const (
tickInterval = 500 * time.Millisecond
targetFPS = 60
tickInterval = time.Second / targetFPS
snakeSpeed = 7 // cells per second
moveInterval = time.Second / snakeSpeed
countdownSeconds = 10
inactivityLimit = 60 * time.Second
)
@@ -91,6 +94,7 @@ func (si *SnakeGameInstance) gamePhase() {
defer ticker.Stop()
lastInput := time.Now()
var moveAccum time.Duration
for {
select {
@@ -104,7 +108,7 @@ func (si *SnakeGameInstance) gamePhase() {
return
}
// Apply pending directions (iterate all 8 slots)
// Apply pending directions every tick for responsive input
inputReceived := false
for i := 0; i < 8; i++ {
if si.pendingDir[i] != nil && i < len(si.game.State.Snakes) && si.game.State.Snakes[i] != nil {
@@ -128,6 +132,14 @@ func (si *SnakeGameInstance) gamePhase() {
return
}
// Only advance game state at snakeSpeed
moveAccum += tickInterval
if moveAccum < moveInterval {
si.gameMu.Unlock()
continue
}
moveAccum -= moveInterval
state := si.game.State
// Advance snakes