Vídeo de presentación del proyecto
En este vídeo se muestra el funcionamiento de la App.
Ideación de la App y selección de la API externa
Para continuar con la segunda práctica, como se pedía en el enunciado, debía integrar una API externa que aportara valor real a la aplicación desarrollada en la PR1. Desde el inicio tuve claro que no quería añadir la API como un simple “extra técnico”, sino que debía integrarse de forma coherente dentro del concepto del juego.
Mi primera idea, antes incluso de investigar APIs existentes, fue que los skins de los robots pudieran obtenerse externamente. Sin embargo, rápidamente descarté esta opción, ya que el diseño del juego requiere que los personajes tengan ruedas y no animaciones de caminar, lo que hacía muy compleja la integración de assets externos sin romper la coherencia visual y funcional del juego.
La segunda idea fue utilizar la API Astronomy Picture of the Day de la NASA para mostrar un fondo distinto cada día. Llegué incluso a obtener el token y hacer pruebas, pero finalmente descarté esta opción al darme cuenta de que trabajar con imágenes tan pesadas no era adecuado para un juego arcade sencillo, además de suponer una carga innecesaria de procesamiento en cada iteración del draw.
Finalmente opté por integrar JokeAPI, una API que ofrece chistes de forma ligera y rápida. Me pareció una opción especialmente adecuada para un MVP, ya que aporta personalidad y trasfondo a los personajes sin interferir en la jugabilidad. En un juego arcade tan simple, dotar de carácter a los robots suma valor a la experiencia del usuario. Además, esta idea deja abierta una clara evolución futura del proyecto, como la creación de un lore o guion más elaborado, o personajes con estilos de humor propios que se puedan desbloquear progresivamente.
Para reforzar esta idea, el usuario puede seleccionar desde el panel de ajustes la categoría de humor que desea, y será el robot el que le “cuente” el chiste, reforzando la sensación de personaje con identidad propia.
Consultas y pasos seguidos en la documentación de Capacitor
El primer uso de Capacitor en el proyecto fue Capacitor Motion, utilizado para leer la inclinación del dispositivo móvil y permitir el movimiento lateral del jugador. Esta funcionalidad se encapsuló en un módulo externo motion.js, que posteriormente se importaba en sketch.js. En concreto, se utilizó Motion.addListener('orientation',, obteniendo el valor
callback)gamma para controlar el movimiento izquierda/derecha.
En una iteración posterior del proyecto fue necesario mantener la pantalla encendida mientras el usuario jugaba, ya que en partidas largas la pantalla se apagaba automáticamente. Para solucionar esto se utilizó Capacitor Keep Awake, llamando a KeepAwake.keepAwake() desde setup(). Para asegurar la persistencia de los datos a largo plazo se decidió utilizar Capacitor Preferences. A diferencia del almacenamiento temporal, Preferences garantiza que los datos no se pierdan entre sesiones. Este plugin se utilizó para guardar información como la categoría de chistes seleccionada, opciones de audio, personajes desbloqueados, nombre de usuario y ranking de puntuaciones. La lógica se centralizó en el módulo storage.js, utilizando Preferences.get() y Preferences.set().
Durante el desarrollo surgió un problema con el audio: la música continuaba sonando aunque el usuario cambiara de aplicación o apagara la pantalla. Para gestionar correctamente estos estados se utilizó Capacitor App, empleando App.addListener('appStateChange', para pausar música y lógica del juego cuando la app pasaba a segundo plano.
callback)
Para mejorar el feedback durante la partida se integró Capacitor Haptics. Se creó un módulo específico haptics.js, desde el que se utilizó Haptics.impact() para acciones suaves como recoger monedas y Haptics.vibrate() con duración para el Game Over. En este punto fue importante adaptar la implementación a las limitaciones del dispositivo utilizado.
Finalmente, la integración de la API externa se realizó mediante fetch, gestionando todas las llamadas de forma asíncrona con async/await.
El flujo de desarrollo fue el habitual con Capacitor:
- En ordenador:
npm run dev - En dispositivo real (Xiaomi Redmi Note 12):
npm run,
buildnpx cap sync android,npx cap open
android
Pruebas con funcionalidades nativas del dispositivo
Para ajustar el control por inclinación fue necesario realizar múltiples pruebas, afinando la velocidad del movimiento del jugador hasta encontrar un equilibrio entre sensibilidad y control. Se limitaron los rangos de inclinación para evitar comportamientos erráticos.
En el caso de Haptics, el dispositivo Xiaomi utilizado no diferenciaba claramente entre los estilos light, medium y heavy. Por este motivo se optó por combinar Haptics.impact() para acciones leves y Haptics.vibrate() con duración para eventos importantes como la muerte del jugador, ofreciendo así un feedback más claro.
Integración de la API: dinamismo y control de errores
Cuando el jugador pierde la partida al colisionar con una bomba, el juego realiza una petición a la API de forma dinámica. Esta llamada depende directamente de la configuración del usuario, ya que la categoría de chistes seleccionada se inyecta como parámetro en la URL de la petición.
Dado que la conexión a internet puede fallar o no estar disponible, se implementó un sistema de fallback. Si la petición a la API falla o tarda demasiado, el juego utiliza una base de datos interna de chistes almacenados localmente, garantizando que la experiencia no se rompa.
Aunque la API permite seleccionar idioma, la cantidad de chistes disponibles en castellano era muy limitada, por lo que se decidió mantener los chistes en inglés, manteniendo el resto de la interfaz del juego en castellano.
Decisiones y aprendizajes
Inicialmente la idea era mostrar los chistes durante la partida cada vez que el jugador recogía una moneda. Sin embargo, tras probarlo, se comprobó que esta decisión rompía el ritmo del juego y perjudicaba la experiencia de usuario. Finalmente se optó por mostrar los chistes únicamente en la pantalla de Game Over, momento en el que el jugador puede leerlos con calma.
Fue necesario implementar un sistema claro de estados del juego: pantalla de inicio, juego activo, Game Over y ajustes. Esto permitió que el proyecto funcionara como un MVP estable y comprensible.
Durante el desarrollo también fue clave aprender a depurar directamente en Android mediante Logcat, ya que muchos errores no eran visibles desde las herramientas de desarrollo del navegador.
Problemas, soluciones y conclusiones
Uno de los problemas más complejos fue gestionar correctamente el audio y la persistencia de datos asíncronos. Al tener el código distribuido en varios módulos y trabajar de forma intermitente en el tiempo, fue necesario apoyarse en flags, pruebas controladas y herramientas de IA como Gemini y Copilot para identificar rápidamente los errores.
Como conclusión, este proyecto ha servido para consolidar el flujo completo de desarrollo de una app híbrida con Capacitor, desde la ideación hasta la distribución, y para entender la importancia de diseñar bien un MVP antes de escalar funcionalidades.
Bocetos, capturas e iteraciones
Primeros bocetos para la implementación de los chistes:

Resultados del estado de inicio de la app, estado de juego, Game Over y settings:

Fuentes y citaciones
- Capacitor Motion: https://capacitorjs.com/docs/apis/motion
- Capacitor keep-awake: https://github.com/capacitor-community/keep-awake).
- Capacitor Preferences: https://capacitorjs.com/docs/apis/preferences
- Capacitor App: https://capacitorjs.com/docs/apis/app
- Capacitor Haptics: https://capacitorjs.com/docs/apis/haptics
- Fetch: https://developer.mozilla.org/es/docs/Web/API/Fetch_API/Using_Fetch
- Funciones asíncronas: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
Vínculo al .apk
https://github.com/elecinas/skip-balls/releases/download/v1.0/app-debug.apk
















Aquest és un espai de treball personal d'un/a estudiant de la Universitat Oberta de Catalunya. Qualsevol contingut publicat en aquest espai és responsabilitat del seu autor/a.