Input
Read input inside `update()` - button presses, key presses, and typed text are one-frame events that already reset by the time `render()` runs, so checking...
Written for blit386 1.8.0. The rest of blit386.dev documents the newest engine, so an API you find there may be missing from a game pinned to 1.8.0.
Read input inside update() - button presses, key presses, and typed text are one-frame events that already reset by
the time render() runs, so checking them there can miss a tap. BLIT386 gives every player simple, named buttons that
work the same whether the player uses a keyboard or a gamepad.
Face buttons and the D-pad
The button names are constants on BT:
- Directions:
BT.BTN_LEFT,BT.BTN_RIGHT,BT.BTN_UP,BT.BTN_DOWN - Action buttons:
BT.BTN_A,BT.BTN_B,BT.BTN_X,BT.BTN_Y - Gamepad extras:
BT.BTN_L/BT.BTN_R(shoulders),BT.BTN_START,BT.BTN_SELECT, and the triggers as buttons,BT.BTN_L2/BT.BTN_R2(engine 1.8.0+, on at half pull; gamepad only, no keyboard key). Theread-gamepadskill covers sticks and analog triggers.
For one-player games, the player number is 0.
Held vs. just pressed
There are three questions you can ask, and the difference matters:
update() {
// isDown: true every frame the button is held. Use for movement.
if (BT.isDown(BT.BTN_RIGHT, 0)) {
this.x += 2;
}
// isPressed: true only on the one frame the button goes down. Use for jumping, firing, menu choices.
if (BT.isPressed(BT.BTN_A, 0)) {
this.jump();
}
// isReleased: true only on the one frame the button comes back up.
if (BT.isReleased(BT.BTN_A, 0)) {
this.stopChargingJump();
}
}On a keyboard, player 0's D-pad is W, A, S, D and Space maps to BTN_A by default, so these work with no extra setup.
The arrow keys do nothing for player 0 by default. Out of the box the engine assumes two keyboard players: W, A, S, D steer player 0 and the arrow keys steer player 1. A one-player game that wants both sets of keys should say so in
configure():
configure() {
return {
keyboardLayout: 'single', // WASD and the arrow keys both steer player 0 (engine 1.8.0+)
};
}With 'single', player 1 moves to I, J, K, L, and the arrow keys stop scrolling the web page. The default is 'versus'
(WASD for player 0, arrows for player 1). To change only one button instead, remap it:
BT.inputMap(0, BT.BTN_LEFT, 'KeyA', 'ArrowLeft') - this replaces that button's keys, so list WASD too if you want
both.
Arrow keys and Space also scroll the web page by default (unless you use keyboardLayout: 'single'). When your game
uses them, opt in so the page does not move while you play:
configure() {
return {
isCapturingKeyboardScroll: true,
};
}Raw keyboard keys
If you want a specific key by name, use its code (the same names the browser uses, like 'KeyW', 'Space',
'ArrowUp', 'Enter'):
if (BT.isKeyDown('KeyW')) {
this.y -= 2;
}
if (BT.isKeyPressed('Enter')) {
this.start();
}isKeyDown is "held", isKeyPressed and isKeyReleased are the one-frame edges, exactly like the buttons above.
For a "press any key to start" screen, you do not need to name a key (engine 1.8.0+):
if (BT.isAnyKeyPressed()) {
this.start(); // any key went down this frame
}BT.isAnyKeyDown(), BT.isAnyKeyPressed(repeat?), and BT.isAnyKeyReleased() cover the whole keyboard. "Released"
here means every key is up now, not just one of them. BT.isAnyButtonDown(player?),
BT.isAnyButtonPressed(player?, repeat?), and BT.isAnyButtonReleased(player?) do the same for one player's gamepad
buttons (not sticks or triggers). Read the pressed and released ones in update().
If you read raw arrow keys or Space (not only face buttons), set isCapturingKeyboardScroll: true in configure() the
same way.
Mouse, touch, and pen
These share one set of calls (a "pointer"). Slot 0 is the main one:
if (BT.isPointerActive(0)) {
const p = BT.pointerPos(0); // a Vector2i in screen pixels
// ...use p.x and p.y...
}Mouse, touch, and pen positions are already in display pixels. If you listen to a browser event yourself (for example a
file dropped on the canvas), BT.nativeScreenToDisplayPos(event.clientX, event.clientY) converts its coordinates the
same way, or returns null when there is no valid position.
Clicks and taps use the same three questions: BT.isPressed(BT.BTN_POINTER_A, 0) is the frame the button or finger went
down, BT.isDown while it is held. Read them in update(); a click that lands between two steps on a fast display
waits for the next update() (engine 1.8.0+), so none is lost.
Mouse wheel
The mouse wheel also scrolls the web page by default. If your game reads BT.pointerScrollDelta (for example to zoom
the camera), opt in so the page does not move while you play:
configure() {
return {
isCapturingPointerScroll: true,
};
}update() {
const wheel = BT.pointerScrollDelta; // a getter; vertical wheel movement this frame, in pixels
this.zoom += wheel;
}The same flag also controls touch scrolling past the canvas. Left at the default false, phones can tap-hold-scroll the
host page (touch-action: pan-y). Set it to true when your game owns vertical gestures (zoom, camera scroll) so the
page does not compete.
Next: docs/drawing.md to draw what the player controls.
Hot reload: keep playing while you edit
When you change a line and save, your game should keep running - score, position, and colors still where you left them. That is hot reload. New games from...
Colors and the palette
BLIT386 is "palette-first". Instead of drawing with a color directly, you fill a small set of numbered slots with colors once, then draw using the slot...