Jugable
Apuntarme

Guardar el récord sin base de datos

  • Nivel: ya programo
  • UI y estado
  • 2 min de lectura

Guardar un número en localStorage es una línea. Lo que hace este ejercicio útil es todo lo que pasa alrededor de esa línea, que es donde viven los bugs que llegan hasta producción.

Qué tienes que construir

Un juego mínimo —recoger cosas contra reloj sirve— con tres escenas:

  • Menú, con el récord actual y un botón de jugar.
  • Juego, 30 segundos y un marcador.
  • Fin, con tu puntaje, el récord, y un aviso distinto si lo rompiste.

El récord tiene que sobrevivir a recargar la página y a cerrar el navegador.

Criterios de aceptación

  1. Hacer 15 puntos, recargar la página: el menú muestra 15.
  2. Hacer 10 puntos después: el récord sigue siendo 15, y la pantalla de fin no dice que rompiste el récord.
  3. Un botón de "borrar récord" lo regresa a 0 y eso también persiste.
  4. Con localStorage bloqueado —abre una ventana en modo incógnito con las cookies apagadas— el juego se juega igual, solo que sin guardar. No hay pantalla en blanco ni error en consola.
  5. Si metes basura a mano en localStorage desde la consola del navegador (localStorage.setItem('record', 'perro')), el juego arranca con récord 0 en lugar de romperse.
  6. Salir de la escena de juego a media partida y volver a entrar empieza limpio: 30 segundos, marcador en 0.

Los criterios 4 y 5 son el ejercicio. Los otros cuatro son el pretexto.

Pistas

Encapsula el guardado en un módulo, aunque sean quince líneas. Que ninguna escena hable con localStorage directamente:

export const guardado = {
  leerRecord() {
    try {
      const n = parseInt(localStorage.getItem('record'), 10)
      return Number.isFinite(n) && n >= 0 ? n : 0
    } catch {
      return 0
    }
  },
  guardarRecord(n) {
    try { localStorage.setItem('record', String(n)) } catch { /* sin guardado */ }
  },
}

Ese try/catch es el criterio 4: en modo incógnito estricto, localStorage existe pero lanza excepción al escribir, cosa que sorprende a mucha gente. Y el Number.isFinite es el criterio 5: parseInt('perro') te da NaN, y NaN comparado con cualquier cosa es false, así que tu récord se vuelve imposible de romper y no hay error en ningún lado. Ese bug es silencioso y por eso es peligroso.

El criterio 6 te va a morder si guardas el estado de la partida en propiedades de la escena y las inicializas en create(). Phaser reutiliza la instancia de la escena: lo que declares fuera de create() sobrevive entre partidas. Todo lo que sea estado de una partida se inicializa en create(), sin excepciones.

Cuando ya te salió

Guarda las cinco mejores puntuaciones con la fecha de cada una, y muéstralas en el menú.

Ahora ya no guardas un número sino una estructura, y necesitas versionarla: { v: 1, tabla: [...] }. El día que cambies el formato, la gente que ya jugó tiene el viejo en el navegador. Decide desde ahora si lo migras o lo tiras — es la misma decisión que toma cualquier juego con partidas guardadas, a otra escala.

Sigue con estos

Ver el catálogo completo

Empiezo

El cronómetro que no miente

Un contador en pantalla que marque exactamente los mismos segundos en un monitor de 60 Hz y en uno de 144 Hz. Suena trivial hasta que lo mides.

  • Game loop
Leer el ejercicio Sin repo todavía
Empiezo

Un sprite que camina y se detiene

Un personaje que corre cuando lo mueves y se queda quieto cuando lo sueltas, sin que la animación se reinicie sola ni se quede pegada en medio del paso.

  • Sprites
  • Input
Leer el ejercicio Sin repo todavía
Empiezo

Monedas que no se cuentan dos veces

Veinte monedas, un marcador y un bug que aparece solo cuando el jugador es lento: la moneda que suma tres veces porque tardaste tres frames en salir de ella.

  • Colisiones
  • UI y estado
Leer el ejercicio Sin repo todavía

El próximo grupo

Un día
y un juego tuyo

Presencial en Tijuana

Cupo limitado por grupo

apartar lugar