Saltar al contenido principal

Mandos y guardado en Memory Card

· 6 min de lectura
Desarrollador de Unity y creador de Polygon Engine

Al trabajar en las interacciones del juego, necesitaba que la entrada del mando funcionara de forma consistente en los scripts y la interfaz. También hacía falta guardar el progreso y recuperarlo en otra sesión. Son dos sistemas que se utilizan constantemente, pero que requieren considerar más situaciones que la configuración habitual de mi computadora de desarrollo.

Un jugador puede iniciar sin el mando conectado o usar uno con capacidades distintas. La Memory Card puede estar llena, faltar o contener datos dañados. Al integrar estos sistemas en Polygon Engine, tuve que tener en cuenta esos casos y ofrecer información suficiente para que cada juego pueda responder a ellos.

Detectar cuándo se presiona y se suelta un botón​

Para implementar la entrada, necesitaba distinguir entre mantener un botón presionado y presionarlo por primera vez. El primer caso se obtiene del estado actual; el segundo requiere compararlo con el estado anterior. Esa comparación debe dar el mismo resultado para todos los sistemas que consulten la entrada durante una actualización.

Por ejemplo, si un script consulta el botón de confirmar y después lo consulta la interfaz, ambos necesitan recibir información de la misma lectura del mando. Si cada consulta también avanza el estado anterior, el resultado puede cambiar según el orden en que se ejecuten los sistemas.

Por eso la lectura del dispositivo debe realizarse en un momento definido de la actualización, conservando el estado anterior para las comparaciones. Con esa información, los scripts pueden distinguir una pulsación, un botón mantenido y una liberación sin modificar la lectura que recibirán los demás.

La comunicación con el mando también tiene detalles que la API necesita resolver, como identificar el dispositivo e interpretar los bits activos en bajo, donde un cero indica que el botón está presionado. La referencia de mandos y tarjetas de memoria explica el protocolo. Entenderlo me permite ofrecer consultas sencillas desde Lua sin trasladar esos detalles a cada script.

Mandos digitales y analógicos​

Un mando digital y uno analógico no entregan la misma información. Además, conectar un gamepad a la computadora mediante el sistema operativo utiliza una vía distinta a la comunicación del mando con PS1. Para interpretar los valores correctamente, hay que conocer el tipo de dispositivo y el modo en que está funcionando.

En los sticks también hay que definir una zona muerta. Si es demasiado pequeña, un stick ligeramente fuera del centro puede mover al personaje o la cámara sin intención del jugador. Si es demasiado grande, cuesta realizar movimientos pequeños. Ajustarla y normalizar los valores facilita usar la entrada desde el juego.

El teclado facilita las pruebas rápidas en el editor, pero las interacciones con mando necesitan su propia revisión. Una tecla no reproduce el rango de un stick, y una prueba en computadora no comprueba la comunicación del mando con PS1. Por eso conviene probar la misma acción con los dispositivos y entornos a los que se destina el juego.

Coordinar la entrada del juego y los menús​

La consistencia de entrada no decide por sí sola quién debería reaccionar. Si un menú está abierto, la misma pulsación no debería confirmar por accidente una opción y activar una puerta detrás de él.

Para evitarlo, el juego necesita definir cuándo acepta movimiento, interacción o navegación del menú. El engine proporciona la entrada y los mecanismos de UI, mientras que los scripts coordinan su uso según el estado actual de la partida.

Un proyecto pequeño que combina movimiento, interacción e interfaz permite revisar esa coordinación. Al probarlos juntos, se puede detectar si una acción sigue llegando al personaje cuando debería atenderla el menú, algo que no aparece al probar únicamente un script de movimiento.

Comprobar el resultado del guardado​

Con el guardado, la primera distinción es entre actualizar los datos de la sesión y escribirlos en la tarjeta. En Polygon Engine, asignar un valor de PlayerPrefs modifica los datos en memoria. Guardarlos requiere una operación explícita y comprobar su resultado.

Esa diferencia también debe reflejarse en la interfaz. Abrir una puerta puede actualizar el progreso de la partida, pero el mensaje de guardado solo debería mostrarse cuando termine correctamente la escritura. Si aparece antes, el jugador podría cerrar el juego creyendo que ya se conservó su progreso.

Prefiero un flujo que conserve el progreso actual si falla la escritura, explique el problema de manera útil y permita reintentar. La carga también necesita comprobar su resultado antes de aplicar el estado guardado. De lo contrario, una lectura fallida puede convertirse en un problema aparentemente distinto, como mover al jugador al punto de control equivocado.

La guía de persistencia describe las APIs. El principio detrás de ellas es sencillo: lo que comunica el juego debe corresponder con la operación que realmente terminó bien.

Elegir qué progreso guardar​

Una Memory Card estándar de PS1 tiene 128 KiB organizados en dieciséis bloques de 8 KiB, con uno reservado para estructuras del sistema. El almacenamiento actual de Polygon Engine utiliza un bloque del juego y un conjunto limitado de entradas. Dentro de ese espacio, necesito elegir los datos que permitan reconstruir el progreso al cargar la partida.

Para una interacción sencilla, puede bastar con guardar el identificador del punto de control y el estado de una puerta. Así se evita copiar todas las propiedades de cada objeto, muchas de las cuales ya se recuperan al cargar la escena. Además de ocupar menos espacio, esos datos resultan más fáciles de mantener cuando cambia el nivel.

También hay que cuidar la identidad del archivo de guardado para que dos proyectos no utilicen la misma por accidente. Los valores deben respetar la codificación y los límites de longitud, y cualquier cambio en su estructura requiere decidir cómo leer las partidas anteriores. El tamaño reducido del archivo no elimina esas necesidades.

Probar tarjetas ausentes, llenas y dañadas​

Save Data Simulator permite probar desde el editor qué ocurre cuando falta la tarjeta, está llena o tiene datos dañados. Me resulta útil para revisar el mensaje que recibirá el jugador, la posibilidad de reintentar y la conservación del progreso de la sesión mientras se resuelve el problema.

Save Data Simulator con selector de simulación de fallas, límites de entradas y controles de persistencia

Save Data Simulator en la versión documentada del editor. Ilustra el flujo de pruebas de fallas, no una prueba con una tarjeta física.

Abre la imagen para verla a tamaño completo.

Estas pruebas ayudan a revisar la respuesta del juego, pero la simulación no reproduce todos los fallos de una tarjeta física ni las interrupciones eléctricas. Para evaluar esos casos hacen falta pruebas adicionales en emulador y hardware. Cada entorno permite comprobar una parte del comportamiento.

Trabajar en la entrada y el guardado me ha llevado a prestar más atención a lo que ocurre entre una acción y su confirmación. Necesito que el juego responda una vez a cada pulsación cuando corresponde, que los menús no activen otras interacciones y que el mensaje de guardado coincida con el resultado. Son detalles que conviene probar como parte de la partida completa.