Saltar al contenido principal

Módulo Input

Input reparte la entrada del jugador en acciones: «saltar», «disparar», «izquierda». Cada acción es un número del 0 al 15, y con map() la asocias a una tecla (en Windows) o al pin GPIO de un botón (en las placas). El juego pregunta después por la acción con pressed(), sin saber qué hay detrás. Con set(), otra fuente (un mando I2C, el táctil) puede mover las mismas acciones.

Captura del módulo Input de JARU para mapear acciones de teclado, botones y controles de juego

use Input

Plataformas​

WindowsESP32 / ESP32-S3ESP32-P4ESP32-S31
Qué asocia map()Una tecla, por su nombreUn pin GPIO, de 0 a 63Un pin GPIO, de 0 a 63Un pin GPIO, de 0 a 63, o una tecla de un teclado USB
Cuándo cambia lo que leeLas teclas, solo dentro de Display.update()Al consultarloAl consultarloAl consultarlo

Cada plataforma acepta un solo tipo: en Windows, un número de pin lanza ERR_TYPE_MISMATCH, y en las placas lo lanza un nombre de tecla (salvo en el ESP32-S31). Para que el mismo programa funcione en todas, elige el mapeo con platform():

use Input

const SALTAR = 0

if (platform() == HW_WIN32) then
Input.map(SALTAR, "SPACE") // en Windows, la barra espaciadora
else
Input.map(SALTAR, 15) // en la placa, un botón entre el pin 15 y GND
end
  • Qué pin lleva cada botón de tu placa, el mando I2C y el teclado USB del ESP32-S31: Botones, pines y mando.
  • En un juego con salas, los controles de la pestaña Game los mapea SceneInputMap(): ver La sala y el juego.

Funciones​

FunciónQué haceDevuelve
map(acción, tecla o pin)Asocia la acción a una tecla o a un pin.true
pressed(acción)Dice si la acción está pulsada.true o false
justPressed(acción)Dice si la acción se acaba de pulsar.true o false
released(acción)Dice si la acción se acaba de soltar.true o false
set(acción, estado)Pone el estado de la acción desde tu programa.true
update()Vuelve a leer las entradas.true

Todas las funciones lanzan sus errores, así que se capturan con try:

  • un número de argumentos que no es el suyo: ERR_INVALID_ARG_NUMBER;
  • una acción que no es un entero: ERR_TYPE_MISMATCH;
  • una acción fuera de 0 a 15: ERR_INVALID_VALUE.

map​

map(acción, tecla o pin) asocia la acción a una tecla o a un pin.

use Input

// Un juego con el teclado
Input.map(0, "W") // arriba
Input.map(1, "S") // abajo
Input.map(2, "A") // izquierda
Input.map(3, "D") // derecha
Input.map(4, "SPACE") // saltar
Input.map(5, "CTRL") // disparar
ParámetroTipoQué es
acciónenteroLa acción, de 0 a 15.
tecla o pincadena o enteroEn Windows, el nombre de la tecla. En las placas, el número de pin, de 0 a 63. En el ESP32-S31, cualquiera de los dos.

Devuelve true. Además de los errores de todas, lanza:

  • con un tipo que la plataforma no admite (un número en Windows, un nombre en las placas): ERR_TYPE_MISMATCH;
  • con un nombre de tecla que no existe, o un pin fuera de 0 a 63: ERR_INVALID_VALUE.

Volver a mapear una acción le cambia la tecla o el pin, borra lo que hubiera puesto set() y la deja suelta.

En las placas, el pin queda como entrada con la resistencia de pull-up interna: conecta el botón entre el pin y GND. La acción está pulsada mientras el pin está a nivel bajo. La VM no comprueba que el pin exista en tu placa: solo que esté entre 0 y 63.

Los pines 34 a 39 del ESP32

En el ESP32 (no en el S3), los pines 34 a 39 no tienen resistencia de pull-up interna. Un botón en ellos, sin una resistencia externa entre el pin y 3,3 V, deja el pin al aire y la acción no lee bien el botón. Usa otro pin o pon la resistencia. Los pines que no valen para un botón en cada chip están en Botones, pines y mando.

Las teclas​

En Windows y en el teclado USB del ESP32-S31, las teclas se nombran así, en mayúsculas ("space" da ERR_INVALID_VALUE):

GrupoTeclas
LetrasA a Z
Números0 a 9
FlechasUP, DOWN, LEFT, RIGHT
EspecialesSPACE, ENTER, ESC, TAB
ModificadoresSHIFT, CTRL, ALT (las de la izquierda)

pressed​

pressed(acción) devuelve true mientras la acción está pulsada: por su tecla o su pin, o por set(). Una acción sin mapear devuelve false.

use Input

const IZQUIERDA = 2
const DERECHA = 3
var x = 160
var velocidad = 2

Input.map(IZQUIERDA, "A")
Input.map(DERECHA, "D")

// En cada fotograma: movimiento continuo mientras se mantiene pulsada
if (Input.pressed(IZQUIERDA)) then
x = x - velocidad
end
if (Input.pressed(DERECHA)) then
x = x + velocidad
end

Es la consulta segura: si la lees una vez por fotograma, ves todas las pulsaciones que duran al menos un fotograma (ver Cuándo se leen las entradas).

justPressed​

justPressed(acción) devuelve true si la acción ha pasado de suelta a pulsada desde la lectura anterior de las entradas. Sirve para lo que pasa una vez por pulsación: saltar, disparar.

use Input

const SALTAR = 4
Input.map(SALTAR, "SPACE")

if (Input.justPressed(SALTAR)) then
println("¡Salto!")
end
justPressed puede perder pulsaciones en la 0.9.7

La VM vuelve a leer las entradas cada vez que consultas una acción en un milisegundo distinto del de la lectura anterior, y cada lectura pasa el estado de ahora a «anterior» (ver Cuándo se leen las entradas). El flanco de justPressed() y released() solo dura hasta la siguiente lectura. Si en un mismo fotograma consultas varias veces las entradas (la misma acción u otras) y entre una consulta y otra cambia el milisegundo, la segunda lectura borra el flanco y la pulsación se pierde. Es un problema conocido de la 0.9.7.

Lee pressed() una sola vez por fotograma para cada acción y calcula los flancos a mano:

use Input

const SALTAR = 4
Input.map(SALTAR, "SPACE")

// Antes del bucle principal
var saltarAntes = false

// En cada fotograma
var saltar = Input.pressed(SALTAR)
var saltarPulsado = saltar and not saltarAntes // en lugar de Input.justPressed(SALTAR)
var saltarSoltado = not saltar and saltarAntes // en lugar de Input.released(SALTAR)
saltarAntes = saltar

released​

released(acción) devuelve true si la acción ha pasado de pulsada a suelta desde la lectura anterior de las entradas. Pierde flancos igual que justPressed(): usa el mismo rodeo.

use Input

const SALTAR = 4
Input.map(SALTAR, "SPACE")

if (Input.released(SALTAR)) then
println("Has soltado el salto")
end

set​

set(acción, estado) pone el estado de una acción desde tu programa. Es la fuente externa de la acción: un driver de mando I2C, un expansor de puertos o el táctil pueden alimentar las mismas acciones que el teclado o los GPIO, y el juego sigue leyendo pressed(), justPressed() y released() sin saber qué hay detrás.

use Input

const SALTAR = 4
var botonA = false // lo que lee tu driver del mando

// En cada fotograma, el driver del mando pone el estado del botón
Input.set(SALTAR, botonA)

if (Input.justPressed(SALTAR)) then
println("¡Salto!")
end
ParámetroTipoQué es
acciónenteroLa acción, de 0 a 15.
estadobooleanotrue si la acción está pulsada. También vale un entero: 0 es suelta. Otro tipo lanza ERR_TYPE_MISMATCH.

Devuelve true.

  • Si la acción tiene además una tecla o un pin, las dos fuentes se suman: la acción está pulsada si lo está cualquiera de las dos. El flanco se calcula como siempre, al releer las entradas.
  • Si la acción no tiene tecla ni pin, set() la deja mapeada solo con esta fuente, y el flanco de justPressed() o released() se calcula en el propio set(): dura hasta la siguiente llamada, por muchas veces que consultes en medio. Llama a set() una vez por fotograma.
  • map() sobre esa acción borra la fuente externa.

update​

update() vuelve a leer las entradas, como haría la siguiente consulta, y devuelve true. Si cae en el mismo milisegundo que la lectura anterior, no hace nada. Casi nunca hace falta: pressed(), justPressed() y released() ya leen solas.

Cuándo se leen las entradas​

La VM no lee las entradas a un ritmo fijo:

  • Las lee al consultarlas. Cada vez que llamas a pressed(), justPressed(), released() o update() en un milisegundo distinto del de la lectura anterior, la VM vuelve a leer todas las acciones que tienen tecla o pin. Las consultas que caen en el mismo milisegundo comparten la lectura.
  • Cada lectura pasa el estado de ahora a «anterior». justPressed() y released() comparan los dos: por eso su flanco solo dura hasta la siguiente lectura.
  • En Windows, el teclado solo cambia dentro de Display.update(), que es donde la ventana recibe las pulsaciones. Sin Display.update() en el bucle, las teclas no cambian nunca. En las placas, el pin y el teclado USB se leen en el momento.
  • Una acción que solo mueve set() no se relee: su estado cambia en cada set().

Lo seguro es leer cada acción una vez por fotograma con pressed(), guardar el valor en una variable y calcular a mano los flancos que necesites, como en el Ejemplo completo.

Ejemplo completo​

Un cuadrado que se mueve con las flechas, cuenta los saltos y se cierra con ESC. Lee cada acción una vez por fotograma y calcula el flanco del salto a mano.

use Display, Input

const IZQUIERDA = 0
const DERECHA = 1
const SALTAR = 2
const SALIR = 3

if (platform() == HW_WIN32) then
Input.map(IZQUIERDA, "LEFT")
Input.map(DERECHA, "RIGHT")
Input.map(SALTAR, "SPACE")
Input.map(SALIR, "ESC")
else
// Pines de ejemplo: pon los de los botones de tu placa
Input.map(IZQUIERDA, 32)
Input.map(DERECHA, 33)
Input.map(SALTAR, 25)
Input.map(SALIR, 26)
end

Display.open(320, 240)
Display.mode(2)
Display.fps = 30
var draw = Display.draw

var x = 160
var saltos = 0
var saltarAntes = false
var salir = false

while (not salir)
// Cada acción, una vez por fotograma
var izquierda = Input.pressed(IZQUIERDA)
var derecha = Input.pressed(DERECHA)
var saltar = Input.pressed(SALTAR)
salir = Input.pressed(SALIR)

if (izquierda) then
x = x - 3
end
if (derecha) then
x = x + 3
end
if (saltar and not saltarAntes) then // el flanco, a mano
saltos = saltos + 1
end
saltarAntes = saltar

draw.cls(0x000000)
draw.color = 0x00FF00
draw.rect(x - 8, 112, 16, 16, true)
draw.text(8, 8, "Saltos: " + saltos.toString(), 0xFFFFFF)
Display.update() // en Windows, aquí cambian las teclas
end
Display.close()

Trampas y problemas conocidos​

  • justPressed() y released() pueden perder pulsaciones si consultas las entradas varias veces en el mismo fotograma: ver justPressed.
  • En Windows, sin Display.update() no cambia ninguna tecla. El teclado se lee en los mensajes de la ventana, que se atienden ahí.
  • Las teclas, en mayúsculas: "SPACE", no "space".
  • Como mucho 16 acciones, de 0 a 15. Si necesitas más, haz que la misma acción signifique cosas distintas según el momento del juego (en un menú, en la partida).
  • En el ESP32-S31, una acción tiene un pin o una tecla, no los dos: el último map() sustituye al anterior. Para leer los dos, usa dos acciones, o set().
  • Input no filtra los rebotes de un botón. Un botón mecánico puede abrirse y cerrarse varias veces al pulsarlo, y cada cambio que caiga en una lectura distinta es un flanco. Si te pasa, ignora los cambios durante unos milisegundos después de cada uno.
  • Al terminar el programa, las acciones vuelven a estar sin mapear, también cuando la placa vuelve a cargar el programa sin reiniciarse.