29 de Abril, 2014

»Submarine (treasure hunting) · El fin de semana pasado probé suerte de nuevo con la Ludum Dare, aunque tenía varios compromisos para el sábado y solo pude dedicarle parte del domingo. Como estoy con un proyecto ambicioso, pensé que podría acabar un mini-juego y tener algo para Abril válido para el reto one game a month. Al final no pudo ser :(, pero sí he tenido tiempo para acabarlo para la jam (que las reglas son menos estrictas); el resultado es mi juego de este mes Submarine (treasure hunting) (y también mi entrada en Ludum Dare).

Hay 0 comentarios, anotación clasificada en: programming, javascript, 1gam.

20 de Abril, 2014

Trabajando en un RPG

Ya hablé hace tiempo de lo que me gustaba leer Maniacos del Calabozo (en Micromanía), y que había descubierto un par de blogs que me habían vuelto a enganchar (CRPG Revisited; algo menos activo, y el genial CRPG Addict).

Ya hace cosa de un año estuve pensando cómo podría ser el RPG mínimo, porque se trata de un tipo de juego que es muy complejo, pero creo que se podría acotar de forma que sea un proyecto viable para un solo programador (además a ratos).

Dejé el tema de lado porque ya no es solo el motor del juego, sino los gráficos que pueden llegar a ser muy exigentes, y además hace falta una historia. Guardé mis notas por si acaso, y las he retomado ahora: ya que de todas formas estoy haciendo un juego al mes, ¿qué tal si hago un RPG con un motor mínimo y lo voy perfeccionando en los meses siguientes?

Witchlord alpha
La idea va tomando forma

Además cooincide que a finales de Mayo tenemos la PyWeek 18 (que será mi juego para ese mes), con lo que puedo estirar un poco más el tiempo de Abril y tener unos días extra para trabajar la idea.

Por ahora las cosas parece que van saliendo, aunque va a ser más parecido a juegos tipo Rogue, porque es el sistema más sencillo de implementar (especialmente en cuanto a aspecto gráfico, aunque estoy implementando ideas que pueden dar mucho juego a la implementación de puzzles). Siempre tendré la posibilidad de mejorar el motor y darle más profundidad en el futuro (entregando un nuevo juego, claro está).

Aunque estamos en la recta final del mes, es todavía pronto para saber si podré terminarlo (juas). El reto one game a month es lo que tiene, que cada juego entregado solo significa un mes menos; y cada nuevo juego es empezar otra vez y poder fallar ;).

Hay 0 comentarios, anotación clasificada en: rpg, 1gam.

11 de Abril, 2014

»PyWeek 18 · El registro para la PyWeek 18 ya está abierto, y la competición se llevará a cabo del 11 al 18 de Mayo. Ya sabes de qué va: programar un juego desde cero en una semana, usando Python. Mi última entrada fue una aventura en 3D (o algo así) con la que quedé tercero en el ranking general (aunque lo importante es ciertamente participar y ¡entregar un juego al final de la semana!).

Hay 0 comentarios, anotación clasificada en: python, programming, pyweek.

28 de Marzo, 2014

»Game programming patterns · Me he encontrado este game programming patterns, un libro sobre diferentes patrones útiles que podemos utilizar programando juegos. Por ejemplo: game loop (al hilo de mi anotación sobre animaciones fluidas, donde implemento variable time step recortando por arriba a 60 FPS). Lectura muy recomendable.

Hay 0 comentarios, anotación clasificada en: programming.

23 de Marzo, 2014

Flax para Android

He publicado mi juego para Marzo del reto one game a month, esta vez un port de Flax para móviles corriendo Android (4.0 o posterior).

Mi experiencia con Android hasta el momento había sido un par de intentos (inclyendo compra de libros) para programar aplicaciones usando el SDK oficial, pero sin mucho interés (ni resultados :D), así que me he tenido que inventar soluciones a los problemas que me he ido encontrando.

Flax en Android

En primer lugar Flax es un juego HTML para jugar en un navegador, pero en el escritorio, así que tuve que adaptarlo a la plataforma móvil.

Para empezar tuve que cambiar cómo se dubuja el juego para poder soportar diferentes tamaños de pantalla y diferentes proporciones. En un escritorio es aceptable que no se utilice toda la pantalla (especialmente en un juego como Flax, donde la acción es vertical), pero en un dispositivo móvil donde la pantalla puede ser bastante pequeña hay que cambiar de estrategia. Además hay multitud de tamaños de pantalla y dispositivos ejecutando Android así que tuve que buscar una forma de que más o menos el juego se viera igual en todas ellas.

Al final no es tan complicado jugando un poco con CSS y detectando la orientación del dispositivo (el juego no funcionará en modo apaisado).

La siguiente diferencia evidente son los controles. El juego original se puede controlar con el teclado o, en caso de ser un navegador Chrome, con un gamepad (tengo uno USB de esos baratos y funciona genial), pero ninguno de estos controles está disponible en un dispositivo Android (no estoy seguro en la parte del gamepad, así que he dejado el soporte por si acaso).

En este caso me empapé de la especificación de los eventos touch y programé un joystick virtual que intenta simular un control analógico: touch-joystick-js (lo he publicado como Open Source).

La idea es que cuando el usuario pulse la pantalla, la nave disparará, y dibujaremos una referencia de la posición del joystick para dar información al usuario (aparte del hecho de que la nave se moverá, claro). Como el dedo del usuario puede molestar para ver la acción, en cualquier momento se puede levantar el dedo para pulsar en otra parte de la pantalla y tener el joystick de nuevo.

No sé si es la mejor forma de implementar este tipo de controles, pero funciona y el juego es bastante jugable.

De todas formas he tenido que ajustar la dificultad porque los controles táctiles son menos precisos que el teclado o que el gamepad, así que algunos enemigos resistirán menos impactos o dispararán con menos frecuencia (esquivar algunos de esos proyectiles puede ser complicado en la pantalla reducida de un móvil).

Mi idea original era utilizar Apache Cordova (aka Phonegap), pero finalmente no pudo ser por un fallo conocido en Kit Kat que desactiva la aceleración por hardware en los componentes que usa Cordova para ejecutar las aplicaciones web (bug 315111).

Estuve a punto de desistir porque, debido a ese problema, no obtenía más de 1 ó 2 frames por segundo, mientras que el juego iba más rápido que en mi portátil si usaba Chrome para Android (en serio :o).

Finalmente la solución vino de la mano de otro proyecto: Crosswalk; que proporciona la misma funcionalidad que Cordova, pero incluye su propio runtime basado en Chromium en lugar de usar un componente WebView del sistema. Por ahora está un poco verde y tiene el inconveniente del tamaño de las aplicaciones, junto al hecho de que tienes que distribuir un componente nativo (el runtime), pero la velocidad y la funcionalidad del entorno es el esperado :).

Así que aquí está: Flax para Android, mi juego de Marzo ;).

Hay 2 comentarios, anotación clasificada en: programming, javascript, 1gam.

21 de Marzo, 2014

»Hasta luego Firefox OS · Porque es posible que vuelva a probar, pero ahora mismo... no merece la pena :(. Hace unos 6 meses desde que comprara un ZTE Open con Firefox OS; y lo he intentado usar a diario (mucho, incluso me compilé la versión 1.2), pero no está listo. Además la batería del terminal cada vez dura menos, imagino que en algún sitio tenía que notarse el precio. En fin, que vuelvo a Android, y damos por concluido el experimento.

Hay 0 comentarios, anotación clasificada en: firefox.

10 de Marzo, 2014

Alien Gamma

Ayer publiqué Alien Gamma, mi segundo juego dentro del reto one game a month, que corresponde con el mes de Febrero (aunque con unos días de retraso).

Hay diferentes motivos para el retraso: es un juego muy ambicioso para este tipo de reto (en un mes), empecé tarde porque no sabía qué hacer hasta mitad de mes y porque... ¡esto es una actividad a tiempo parcial!

Se trata de un shooter-platformer o, lo que es lo mismo, un juego de salta y dispara, influenciado por juegos clasicos como Alien Syndrome o Contra. Todo empezó porque buscando inspiración me puse a ver vídeos de juegos viejos a los que jugué hace años, y por algún motivo acabé en un vídeo de la versión arcade de Rastan Saga.

La verdad es que Rastan no es un juego genial (siempre me ha hecho gracia que el protagonista sea un guerrero que si toca el agua, muere :P), pero tiene muchos elementos del género bastante bien implementados para la época, y la música me encanta. El caso es que empecé a analizar cómo funciona el juego, del que se puede aprender mucho, y pensé: algunas de esas cosas no son tan difíciles de hacer, ¿no?

De ahí a ver algunos vídeos de Contra, Midnight Resistance (inspirado en el primero), Green Beret y Megaman varios; y me puse a dibujar sprites para el juego. Es curioso como tener un nombre ayuda a arrancar (lo mismo hice con Flax: empecé con la pantalla del título).

Jugando a Alien Gamma

Desde luego que es un juego demasiado ambicioso para un mes -eso estaba claro-, y además empecé tarde, así que decidí hacer algo, y si resultaba divertido siempre podré hacer una secuela más amplia en el futuro (empezando el primer día del mes, mucho mejor). Por este motivo he tenido que simplificar muchas cosas (por eso más que Contra, es digamos muy Megaman, por tener que limitar los movimientos; ¡no se puede disparar en 8 direcciones!).

Aún con los recortes obligatorios, me lié a implementar algunas cosas que son bastante complicadas (como las plataformas móviles en vertical, mucho más difícil que las horizontales), pero que al final han resultado bastante bien (aún con algún glitch de vez en cuando). Sin esas plataformas los niveles hubieran quedado más limitados en estructura y en cuanto a posibilidades de platforming (saltar de aquí para allá).

La premisa es sencilla: la base espacial Gamma pide ayuda después de que unas criaturas hostiles aterrizan accidentalmente cerca de la base. Nuestro personaje tiene que recorrer los diferentes niveles liberando a los colonos atrapados en una especie de capullo alienígena, con la inestimable ayuda de su rifle de plasma ;).

Así que los los componentes del juego son por una parte hacerse con el mapa y encontrar a todos los colonos (hay un contador en pantalla que nos indica cuántos faltan por rescatar), y por otra acceder a ellos saltando entre plataformas y ascensores, siempre evitando que los alienígenas nos maten (el componente shooter). Comparado con otros juegos del género, el desarrollo de las escenas no tiene porqué ser lineal (y no lo es, es posible incluso perderse buscando ese último colono que nos falta por liberar).

He repetido Javascript y canvas, que ha resultado muy conveniente en este caso para cargar los mapas de Tiled en formato JSON (aunque implementar un viewport y el scroll es tan lioso como siempre :D).

También he vuelto a usar SoundJS de CreateJS, que es una pasada y simplifica mucho la gestión del audio; porque esta vez sí hay música de fondo, que he creado con Schism Tracker (una reimplementación bastante buena de Impulse Tracker que soporta Linux). En general creo que he conseguido un par de melodías de fondo que ambientan muy bien el estilo de juego y dan ese feel de juego clásico.

Para el mes de Marzo (que ya lleva retraso), voy a probar a hacer un port de Flax a alguna plataforma móvil, ¡a ver qué sale!

Hay 4 comentarios, anotación clasificada en: programming, javascript, 1gam.

8 de Marzo, 2014

Animaciones fluidas con canvas y Javascript

La forma oficial de programar animaciones en Javascript es usando Window.requestAnimationFrame(), con el que indicar al navegador que queremos que una función se ejecute justo antes de redibujar la página (o un canvas en particular).

La teoría dice que de esta forma conseguimos que nuestra función encargada de dibujar (y cuando estamos programando un juego como Flax, el bucle principal del juego), se ejecute a unos 60 cuadros por segundo (según recomendación del W3C, porque además es habitualmente la tasa de refresco de la mayoría de las pantallas; ahora que los monitores CRT han pasado a mejor vida).

Además tenemos que tener en cuenta que requestAnimationFrame no llamará a nuestra función con la misma frecuencia, nuevamente en teoría, si la página no está en primer plano.

requestAnimationFrame está soportado en todos los navegadores modernos, y se puede simular fácilmente en los casos en los que no está disponible (aunque eso puede significar que la implementación de canvas tampoco nos vale).

Con estos datos podríamos asumir que 60 cuadros por segundo es genial y simplemente preparar nuestra función para esa velocidad y todos contentos, pero la realidad es bastante diferente.

Estamos hablando de dibujar en canvas, y el rendimiento va a ser bastante variable en diferente hardware. Por ejemplo: hay gran diferencia entre disponer de una implementación en nuestro navegador que tiene aceleración por hardware para canvas y no tenerla. En Linux es bastante posible que no tengamos aceleración, así que dependiendo de lo que haga nuestro código... es posible que la promesa de ejecutar nuestra función 60 veces por segundo no pueda cumplirse :(.

Esto no es necesariamente un problema si implementamos nuestras animaciones usando una técnica de velocidad de variable. Por ejemplo, si estamos moviendo un objeto en el eje x, en lugar de incrementar en cada llamada a nuestra función su posición en n pixeles, podemos incrementar la posición usando pixeles por segundo. Podemos calcular cuánto tiempo ha transcurrido entre este cuadro y el anterior y multiplicar esa diferencia por el número de pixeles que deseamos si hubiera pasado un segundo (esto se suele expresar como dt*v, siendo dt la diferencia tiempo transcurrido).

Mediante esta técnica conseguimos que la animación sea fluida aunque la máquina sea algo más lenta de lo que esperamos, porque en lugar de incrementar nuestra animación en n, se incrementará en n + k para compensar los cuadros que se pierden en un hardware más lento (siempre con un límite, por supuesto; la calidad de la animación se degradará rápidamente).

Bien, ahora veamos porqué nada de esto es fácil y cómo he solucionado (más o menos) el problema.

En primer lugar la promesa de los 60 cuadros por segundo no se cumple, o no exáctamente. Supongamos el siguiente trozo de código como bucle básico para nuestro juego:

var then = 0;
var cnt = 0;
var loop = function(now) {
	var dt = now-then;

	if(cnt < 250) {
                console.log(cnt++ + ", " + dt);
	}
	// update(dt);
	// draw();

	then = now;
	requestAnimationFrame(loop);
}

loop();

requestAnimationFrame toma un parámetro: la función que queremos que se ejecute. Cuando nuestra función sea ejecutada, tendrá como parámetro el timestamp en el que el navegador llama a la función.

Guardando ese timestamp entre ejecuciones podemos calcular nuestro delta, y en este caso recogemos 250 puntos que pasamos a dibujar a continuación.

requestAnimationFrame, Chrome

requestAnimationFrame, Firefox

He eliminado algunos valores anómalos en los primeros cuadros, probablemente relacionado con el JIT del navegador entrando en escena. Nos encargaremos de eso cuando cubramos caso en el que el navegador puede no llamar a nuestra función si la página no está en primer plano.

El valor de dt está en milisegundos -cosas de Javascript :(-, así que 1000*(1/60.0) es el ~16.66 que deberíamos obtener en cada cuadro. Como podemos observar, no es así.

El caso de Firefox parece incluso peor que el de Chrome, pero para nuestro objetivo tiene poca importancia: no podemos confiar en dt sin más porque no va a ser constante :(.

La solución que he implementado es, en mi opinión, bastante sencilla: basar la animación solo parcialmente en el delta. Si en navegador llama a nuestra función más veces que los 60 cuadros por segundo que esperamos, usamos el dt para ralentizar la animación y mantener la tasa de pixeles por segundo que esperamos, y si el navegador es más lento; asumimos el dt perfecto de 1000/60.

Esto va a causar problemas en máquinas lentas, pero viendo la variabilidad de requestAnimationFrame, tampoco podemos hacer mucho más: el juego correrá lento.

Además con este ajuste evitamos procesar un delta muy grande porque, por ejemplo, la página ha perdido el primer plano por unos segundos. En ese caso obtendremos nuestra tasa máxima ajustada a los 60 cuadros por segundo ideales.

Finalmente yo prefiero trabajar en unidades por segundo, así que convertimos nuestro delta antes de pasarlo a la función que actualice el estado del juego. Esto además tiene la ventaja de suavizar un poco la variabilidad del delta gracias a perder precisión al dibujar.

var then = 0;
var cnt = 0;
var loop = function(now) {
    // dt en segundos
	var dt = Math.min(1000/60, now-then)/1000;

	if(cnt < 250) {
                console.log(cnt++ + ", " + dt);
	}
	// update(dt);
	// draw();

	then = now;
	requestAnimationFrame(loop);
}

loop();

Esta solución se podría mejorar aceptando el valor de delta cuando está por encima de el valor ideal, poniendo un límite máximo pasado el cual nuestra animación no funciona (porque nos saltamos demasiados cuadros), pero en mis pruebas he llegado a la conclusión de que no merece la pena. Si se llega al punto en el que una máquina con potencia razonable no puede mover el juego, es tiempo de optimizar o hacer las cosas de otra forma ;).

Por ejemplo, en Flax escalo el canvas en tiempo real, de forma que jugar a pantalla completa en un monitor grande es más costoso que hacerlo en un netbook; pero siempre es posible hacer la página más pequeña para dar un poco de respiro a la máquina y conseguir los 60 cuadros por segundo.

En otra anotación explicaré el gran problema de usar animaciones basadas en dt en Javascript y canvas en 2D (pista: sub-pixel rendering).

Hay 0 comentarios, anotación clasificada en: programming, javascript.

20 de Febrero, 2014

»Nueva web en python.org · Finalmente se ha publicado la nueva web de Python, mucho más moderna que la anterior (aunque menos familiar también). Y me han comentado que corre en Python 3, ¡estamos en el futuro! ;)

Hay 0 comentarios, anotación clasificada en: python.

13 de Febrero, 2014

»Obey the Testing Goat! · Una de las partes más interesantes del Django Weekend Cardiff del fin de semana pasado fue el tutorial de Harry Percival (Test-driven web development with Python). Aunque tengo experiencia con unit-testing en Django, me lo pasé muy bien y aprendí muchos trucos nuevos ;). Ya conocía su libro Test-Driven Web Development with Python (y al propio Harry), pero me parece buena idea escribir una nota por aquí por si alguien quiere echarle un vistazo. Muy recomendable.

Hay 0 comentarios, anotación clasificada en: programming, python.

Entradas antiguasEntradas nuevas