web/keys
Keyboard state for browser games.
import web.keys
In a game you usually ask once per frame which keys are down, rather than taking an event for every press. This package listens to the keydown and keyup events of the window for you and keeps the state of up to 32 keys. Call Init once, then ask Down or Pressed inside the EveryFrame function from dom, and draw with web/canvas. For a form or a text field, where you want each key as it comes, use dom.OnEventE with dom.EventKey instead.
Keys are named by the browser's key value: ArrowLeft, Enter, " " for the space bar, a for the A key. Things to know:
- Only tracked keys are seen.
Inittracks the four arrows, Enter and the space bar; add others withTrack. Asking about any other key returns false. - Letters change with Shift and Caps Lock:
abecomesA. Track and ask for both. - Tracked keys stop doing their normal job on the page, so they cannot be typed into a text box while the game runs.
- A key can stay "down" after the window loses focus. When the player switches windows while holding a key, the browser sends no
keyup, andDownstays true until that key is pressed and released again.
Holding a key and pressing it once
The two questions are different. Down is true for as long as the key is held, which suits walking. Pressed is true once per press, the next time you ask after the key went down, which suits jumping, shooting or opening a menu: holding the space bar does not make the player jump again and again.
import dom
import web.canvas
import web.keys
mut x := 40.0
mut y := 160.0
mut vy := 0.0
mut c := 0 as i64
func main:
el := dom.Create("canvas")
dom.SetAttr(el, "id", "level")
dom.SetAttr(el, "width", "400")
dom.SetAttr(el, "height", "200")
dom.Append(dom.Body(), el)
c = canvas.Get("level")
keys.Init()
floor := 160.0
dom.EveryFrame((ms: f64):
_ := ms
if keys.Down("ArrowLeft"):
x -= 3
end
if keys.Down("ArrowRight"):
x += 3
end
jump := keys.Pressed(" ")
if jump and y >= floor:
vy = -9
end
vy += 0.5
y += vy
if y > floor:
y = floor
vy = 0
end
canvas.ClearRect(c, 0, 0, 400, 200)
canvas.FillStyle(c, "#444")
canvas.FillRect(c, 0, floor + 20, 400, 20)
canvas.FillStyle(c, "#e11d48")
canvas.FillRect(c, x, y, 20, 20)
end)
endNotice that the program asks Pressed on every frame and only then checks whether the player stands on the floor. If it wrote y >= floor and keys.Pressed(" "), a press in mid air would not be asked about, would stay waiting, and the player would jump by itself on landing.
Your own keys: pause with P
Init tracks the arrow keys, Enter and the space bar. Add any other key with Track, using the browser's key name: a letter, Escape, Shift and so on. The names are exact text, so with Shift or Caps Lock on, the P key arrives as P, not p; track both when it matters.
import dom
import web.canvas
import web.keys
mut paused := false
mut angle := 0.0
mut c := 0 as i64
func main:
el := dom.Create("canvas")
dom.SetAttr(el, "id", "game")
dom.SetAttr(el, "width", "200")
dom.SetAttr(el, "height", "200")
dom.Append(dom.Body(), el)
c = canvas.Get("game")
keys.Init()
keys.Track("p")
keys.Track("P")
keys.Track("Escape")
dom.EveryFrame((now: f64):
_ := now
if keys.Pressed("p") or keys.Pressed("P") or keys.Pressed("Escape"):
paused = not paused
end
if not paused:
angle += 0.05
end
canvas.ClearRect(c, 0, 0, 200, 200)
canvas.Save(c)
canvas.Translate(c, 100, 100)
canvas.Rotate(c, angle)
canvas.FillStyle(c, "#22c55e")
canvas.FillRect(c, -30, -30, 60, 60)
canvas.Restore(c)
if paused:
canvas.FillStyle(c, "#000")
canvas.Font(c, "18px sans-serif")
canvas.TextAlign(c, "center")
canvas.FillText(c, "Paused, press P", 100, 190)
end
end)
endA tracked key no longer does its usual job in the page, because the package calls PreventDefault for it. That is what stops the arrows and the space bar from scrolling during a game, but it also means a tracked letter cannot be typed into a text box on the same page.
Like the dom examples, these run in the browser and are only checked here.
Smooth movement with arrows and WASD
Most games accept both the arrow keys and W, A, S and D. The helper held checks an arrow, a letter and its capital, so Caps Lock does not stop the player. Each axis becomes -1, 0 or 1; when both are set the program divides by the length, so moving diagonally is not faster than moving straight. The speed is in pixels per millisecond and is multiplied by ms, the time since the last frame that EveryFrame passes in, so the dot moves at the same speed on a 60 Hz and a 144 Hz screen.
import dom
import math
import str
import web.canvas
import web.keys
mut x := 200.0
mut y := 150.0
mut c := 0 as i64
func held(arrow: str, letter: str): bool:
ret keys.Down(arrow) or keys.Down(letter) or keys.Down(str.Upper(letter))
end
func axis(minus_arrow: str, minus_letter: str, plus_arrow: str, plus_letter: str): f64:
mut v := 0.0
if held(minus_arrow, minus_letter): v -= 1.0 end
if held(plus_arrow, plus_letter): v += 1.0 end
ret v
end
func main:
el := dom.Create("canvas")
dom.SetAttr(el, "id", "field")
dom.SetAttr(el, "width", "400")
dom.SetAttr(el, "height", "300")
dom.Append(dom.Body(), el)
c = canvas.Get("field")
keys.Init()
each k in ["w", "a", "s", "d", "W", "A", "S", "D"]:
keys.Track(k)
end
speed := 0.2
dom.EveryFrame((ms: f64):
mut dx := axis("ArrowLeft", "a", "ArrowRight", "d")
mut dy := axis("ArrowUp", "w", "ArrowDown", "s")
length := math.Sqrt(dx * dx + dy * dy)
if length > 0.0:
dx = dx / length
dy = dy / length
end
x = math.ClampF(x + dx * speed * ms, 10.0, 390.0)
y = math.ClampF(y + dy * speed * ms, 10.0, 290.0)
canvas.FillStyle(c, "#0f172a")
canvas.FillRect(c, 0, 0, 400, 300)
canvas.FillStyle(c, "#22c55e")
canvas.BeginPath(c)
canvas.Arc(c, x, y, 10, 0, 6.2832)
canvas.Fill(c)
end)
endFunctions
Track
func Track(k: str)key value, such as a. Up to 32 keys; more are ignored without a word. Works before or after Init.import dom
import web.canvas
import web.keys
mut x := 160.0
mut score := 0
func main:
el := dom.Create("canvas")
dom.SetAttr(el, "id", "game")
dom.Append(dom.Body(), el)
c := canvas.Get("game")
keys.Init()
keys.Track("a")
keys.Track("d")
dom.EveryFrame((now: f64):
if keys.Down("ArrowLeft") or keys.Down("a"):
x -= 4
end
if keys.Down("ArrowRight") or keys.Down("d"):
x += 4
end
if keys.Pressed(" "):
score += 1
end
canvas.ClearRect(c, 0, 0, canvas.Width(c), canvas.Height(c))
canvas.FillRect(c, x - 10, 100, 20, 20)
canvas.FillText(c, "score: " + score, 10, 20)
end)
endInit
func Init()The example for this item is shown with the ones above: web-keys-Game.salam
Down
func Down(k: str): boolThe example for this item is shown with the ones above: web-keys-Game.salam
Pressed
func Pressed(k: str): boolThe example for this item is shown with the ones above: web-keys-Game.salam