24 de Agosto, 2014

»DUAL Adventure · No he podido acabar lo que quería hacer (que la verdad era demasiado ambicioso), pero he subido DUAL Adventure como mi entrada para LD30. Lo de las 48h sigue sin encajarme, especialmente cuando se lía el fin de semana y no puedo dedicar todo el tiempo que me gustaría. Además es mi juego de Agosto para mi reto de un juego al mes, aunque no está completo (he aprendido muchas cosas sobre cómo funcionan los juegos de plataformas, con lo que lo considero un éxito pese a todo).

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

20 de Agosto, 2014

»Resultados de gbjam 3 · The Legend of Traxtor, mi juego de Julio, ha quedado en puesto #21 de un total de 248 juegos. Se pueden ver los resultados de la votación en la página de la jam. Nada mal para un juego hecho en un par de días ;).

Hay 1 comentario, anotación clasificada en: 1gam, programming.

18 de Agosto, 2014

»¡Demasiadas Jams! · La verdad es que las game jams tienen mucnas cosas positivas. A mi me han ayudado a acabar juegos, que de otra forma nunca hubiera acabado. Además he aprendido muchas cosas y he mejorado mis habilidades en poco tiempo. Pero es que ahora están en todas partes, ¡hay demasiadas! Por ejemplo, las que lista CompoHub. Hoy esa web registra 6 en marcha, y eso que no están todas las que hay :o.

Hay 0 comentarios

12 de Agosto, 2014

»The matasano crypto challenges · Hice este reto vía email hace un tiempo, y es muy interesante. Me quedé atascado (perdí el interés) en el tercer bloque, pero puedo asegurar que es una de esas cosas que cuando las haces te da super-poderes ;). Yo utilicé Python y creo que fue buena idea (por el batteries included), pero en teoría cualquier lenguaje de programación puede valer.

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

10 de Agosto, 2014

Reproduciendo música en bucle en SFML

En SFML 2.1 tenemos una clase específica para reproducir música de fondo: sf::Music.

Su principal característica es que permite reproducir grandes ficheros haciendo streaming desde disco, con lo que no es necesario tener todo el fichero en memoria. Esto contrasta con sf::Sound, que se utiliza para sonidos en los que no puede haber latencia (se reproducen inmediatamente), y por lo tanto se almacenan en memoria.

Esta distinción está prensente en la mayoría de las librerías que he usado, aunque no siempre se utliza un interfaz diferente (por ejemplo, en pyglet es solo un parámetro en las funciones que cargan los ficheros de audio).

La clase sf::Music es bastante útil, pero le falta una funcionalidad que tampoco está disponible en muchas librerías, pero que en SFML me ha costado bastante implementar :(.

Un requerimiento frecuente cuando se reproduce música de fondo es que el fichero de audio tenga dos partes: la introducción y el cuerpo del bucle que se repite una vez la introducción ha terminado.

Hay distintas formas de implementar esto. A veces es más sencillo partir la música en dos ficheros y reproducir la segunda parte en bucle una vez la primera ha terminado, otras veces es mejor especificar el punto desde el cual el audio debe repetirse.

Para el primer caso normalmente es necesario que la librería nos permita usar un callback cuando un fichero de audio termina de reproducirse (este sistema lo uso en Javascript y Python), el segundo se puede implementar de diferentes formas, pero básicamente se trata de empezar la reproducción en un punto x en lugar de 0.

SFML utiliza un thread para reproducir el audio de forma que nuestro programa nunca se bloquee esperando operaciones con el audio. Esto en general es una buena idea, pero el sf::Music no proporciona opciones para comunicar los dos hilos de ejecución: el que corre nuestra aplicación y el que reproduce el audio. Lo mejor que tenemos en sf::Music es onGetData y onSeek; ambos ejecutados en el hilo que reproduce el audio.

La solución que he implementado en The Legend of Traxtor es la siguiente:

#include <SFML/System/Time.hpp>
#include <SFML/System/Mutex.hpp>
#include <SFML/System/Lock.hpp>
#include <SFML/Audio/Music.hpp>

class ExtMusic : public sf::Music
{
public:
    ExtMusic() : m_ext_loop(false) { }

    void setLoopOffset(float offs) {
        sf::Lock lock(m_ext_mutex);

        m_offs = sf::seconds(offs);
        m_ext_loop = true;
    }

protected:
    bool onGetData(SoundStream::Chunk &data) {
        sf::Lock lock(m_ext_mutex);

        bool result = sf::Music::onGetData(data);
        if (!result && m_ext_loop)
        {
            onSeek(m_offs);
            if (!data.sampleCount)
                sf::Music::onGetData(data);
            return true;
        }

        return result;
    }

private:
    sf::Mutex m_ext_mutex;
    sf::Time m_offs;
    bool m_ext_loop;
};

La idea de la clase ExtMusic (que extiende la clase sf::Music) es indicar desde qué segundo queremos que se repita el audio, y la reprodución saltará a ese punto en el fichero una vez nos quedemos sin datos para reproducir.

Además hay que usar un Mutex para garantizar el acceso exclusivo a los datos porque setLoopOffset se llamará desde un hilo que es distinto al que llamará a onGetData.

Se supone que SFML proporciona sf::SoudSource para que implementemos nuestras propias historias para reproducir audio, pero en mi caso era mucho más sencillo aumentar la funcionalidad de sf::Music que liarme con un interfaz de más bajo nivel como el que proporciona sf::SoundSource.

Al final el resultado es algo hacky (e incompatible con los bucles normales), pero bastante simple. La buena noticia es que alguien ha contribuido una implementación de bucles con punto de inicio, con lo que futuras versiones de SFML ya no tendrán esta limitación ;).

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

6 de Agosto, 2014

Sobre distribución

Ya he sobrepasado la mitad del reto de programar un juego al mes (durante el 2014), con 7 juegos. Si añadimos los que había hecho antes del empezar el reto, tengo una buena muestra para analizar.

He trabajado con diferentes lenguajes de programación y diferentes frameworks o librerías, y he probado distintos modelos de distribución, así que voy a comentar aquí algunas conclusiones.

Lenguaje Framework Linux Windows Mac Otros
C SDL 1 Fuente Binario Fuente SDL es muy portable
Python PyGame Fuente/Paquete Binario1 Fuente n/d
Python pyglet Fuente/Paquete Binario1 Fuente/Paquete2 n/d
Javascript Canvas 2D + SoundJS Navegador Navegador Navegador Necesita aceleración hardware
C++ SFML Paquete Binario3 n/d4 Soporte para Android en desarrollo

Notas:

  1. py2exe requiere una máquina con Windows para empaquetar.
  2. Empaquetado como un egg, distribución binaria de AVBin y usa la versión de python 2 instalada en el sistema.
  3. Cross-compiling en Linux, probado con Wine. No hace falta una máquina Windows.
  4. Por ahora un Mac es necesario.

Esta tabla antiende únicamente a la distribución, y en ese aspecto Javascript es la mejor opción, aunque no es cualquer navegador porque la aceleración hardware no está disponible en todas partes. No tiene en cuenta lo fácil o difícil que es el desarrollo o mis preferencias como programador (importante para mi: encuentro más satisfactorio programar en python que en javascript :P).

Además está la estabilidad de la plataforma. Por ejemplo, el resultado con Javascript es bastante variable (depende de la versión del navegador y de si tenemos aceleración en canvas), y en general es más difícil conseguir buen rendimiento. pyglet utiliza OpenGL y aceleración por hardware, y es más autocontenido que PyGame, pero este último es más estable y en general está mejor soportado (gracias a que usa SDL).

Distribuir el código puede ser la mejor opción si el juego acaba incluído en una distribución, pero entre ciclos de desarrollo y procedimientos pueden pasar muchos meses antes de que se obtengan resultados (y es posible que el juego nunca llegue a ninguna distribución).

En el caso de SDL con Dodgin Diamond 2 se puede comprobar que publicar el código fuente permite que el juego sea portado a plataformas que de otra manera sería imposible soportar. En general los ports respetan la licencia, aunque en este caso no lo considero demasiado importante.

Por ahora estoy centrándome en resultados inmediatos, sobretodo cuando se trata de una game jam, que se traduce en: el juego debe funcionar en Windows, luego Linux (porque puedo y porque me resulta fácil, no por el número de usuarios), y si es posible y con muy baja prioridad... Mac.

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

4 de Agosto, 2014

The Legend of Traxtor

Ayer publiqué mi juego para Julio dentro del reto one game a month: The Legend of Traxtor.

Se trata de un juego tipo puzzle match-3 en el que controlas una nave (legendaria, claro que sí :D) que puede mover bloques buscando hacer coincidir al menos 3 iguales antes de que los bloques llegen a su linea de defensa al final del tablero.

The Legend of Traxtor (gameplay)

Aunque el planteamiento es bastante diferente, tiene muchos puntos en común con mi juego de Junio (Zooooop! es otro match-3), y creo que Traxtor es más divertido.

Además he aprovechado para participar en gbjam 3 (aquí está mi entrada), que es una game jam en la que hay que recrear (mas o menos) la apariencia de los juegos de Game Boy, esto es: resolución de 160x144 y solo 4 colores.

Sorprendentemente la limitación de los colores la he llevado más o menos bien (no es que sea un artista, así que no he echado de menos tener más colores :P), pero el tamaño de pantalla ha sido un problema a veces (¡no cabe casi nada!).

He repetido con C++ y SFML, que le voy cogiendo el tranquillo. La verdad es que no era mi intención repetir género, pero me lie un poco con otro proyecto que no pude terminar en Julio así que este juego ha sido de emergencia, desarrollado en unos 3 días durante las 96 horas de gracia permitidas para acabar el juego de cada mes (con la suerte de que me ha permitido participar en la #gbjam que empezaba en Agosto).

Por ahora hay binarios para Windows en la página del juego, en los próximos días intentaré empaquetar el juego para Ubuntu Trusty al menos.

Actualización: he publicado paquetes para Ubuntu Trusty en la página del juego.

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

30 de Julio, 2014

»Desmovilizándome · He estado pensando seriamente en actualizar mi equipo de casa (un portátil Dell normalito), pero no sé si quiero comprar otro portátil. Ahora mismo tengo un escritorio, con lo que trabajo con una pantalla de 22", ratón y teclado (un KBT Pure Pro compacto, y mecánico), más un Chromebook de 11.6" para llevar de paseo y a conferencias. En estos casos el portátil ocupa mucho espacio en la mesa, y no lo muevo para nada.

Estoy pensando en un Gigabyte Brix (el GB-BXI3-4010, que trae un Intel Core i3 de cuarta generación con una tarjeta de vídeo Intel 4400 HD; con 120 GB SSD y 8 GB de memoria se queda en unos 420 EUR). Ocupa poco espacio, es muy silencioso, y tiene todo lo que necesito (hasta WLAN). No es la máquina más potente, pero tampoco es mi prioridad. ¿Alguien tiene experiencia con este tipo de barebones?

Hay 2 comentarios

27 de Julio, 2014

Compositing y rendimiento

Alex llevaba tiempo quejándose de que su portátil no movía Minecraft con suficiente suavidad y la verdad es que, calidad de drivers aparte, no es que la máquina fuera corta de recursos (la GPU no es una pasada, pero la máquina tiene 4GB de RAM y... ¡es solo Minecraft!).

Se me ocurrió que podría probar con Openbox en lugar de Unity, y hay una gran diferencia (ella trabaja con Ubuntu Precise, que ya va siendo viejo... es verdad). Había leído por ahí que correr un gestor de ventanas minimalista puede mejorar los FPS que se consiguen en Linux en juegos 3D más o menos exigentes.

En mis experimentos con pyglet he notado que hay un problema similar en GNOME, y está relacionado con gestores de ventanas que implementan compositing (en inglés).

La idea básica es que en lugar de dibujar en el display, el gestor de ventanas que hace composición (traducción libre) proporciona un buffer para cada ventana a donde se redirigen las operaciones de dibujado de una forma transparente para la aplicación.

Una vez se tiene ese buffer listo, el gestor de ventanas enviará la información al display, realizando muchas veces un post-proceso para proporcionar todos los effectos a los que nos han ido acostumbrando los escritorios modernos.

Esto ni que decir tiene que tienen un coste, especialmente alto con tarjetas gráficas poco potentes o en las que el driver para Linux no es especialmente bueno.

En el caso de Alex, el gestor de ventanas Openbox no realiza composición, con lo que Minecraft se ejecuta mucho más suave al eliminar el trabajo extra que realiza Unity y que cuando ella juega al juego es completamente innecesario.

Aunque esos efectos que esperamos en un escritorio moderno son importantes (si los Mac y Windows lo hacen, hay una presión importante en el mercado para que las distribuciones Linux siguan la tendencia), es frecuente que causen problemas si quremos jugar ocasionalmente en Linux sin invertir en el hardware específico que nos daría el rendimiento necesario.

Es bastante habitual desactivar la redirección en juegos que se ejecutan en pantalla completa, con varios hacks que no terminan de funcionar en todas partes, y con el nuevo y prometedor _NET_WM_BYPASS_COMPOSITOR, que nos permitirá informar al gestor de ventanas que no queremos su buffer ni la redirección.

Hay otros factores que pueden afectar al rendimiento de los juegos en Linux (drivers, la versión de Mesa 3D), pero el compositing es el que más dolores de cabeza me ha dado, y no podemos esperar que todos los usuarios de nuestros juegos utilizen un gestor de ventanas minimalista :(.

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

20 de Julio, 2014

El Makefile perfecto

Igual el título es un poco efectista, pero dado que me ha costado un poco dar con el Makefile para mis proyectos C++ con SFML, lo pongo por aquí por si le es útil a alguien más.

BIN=main
CXX=g++
# debug
CXXFLAGS=-fPIC -I. -c -Wall -ggdb -DDEBUG
LDFLAGS=
# release
#CXXFLAGS=-fPIC -O2 -I. -c -Wall
#LDFLAGS=-s
LIBS=-lsfml-system -lsfml-graphics -lsfml-window
 
SOURCES=$(wildcard *.cc)
OBJECTS=$(SOURCES:.cc=.o)
 
all: $(SOURCES) $(BIN)
 
clean:
    rm -f $(BIN) *.o *.orig Makefile.deps
 
Makefile.deps:
    $(CXX) $(CXXFLAGS) -MM $(SOURCES) > Makefile.deps
 
$(BIN): $(OBJECTS)
    $(CXX) $(LDFLAGS) $(OBJECTS) $(LIBS) -o $@
 
.cc.o:
    $(CXX) $(CXXFLAGS) $< -o $@
 
include Makefile.deps

Asumo GNU Make, aunque la verdad es que no sé si funcionaría con otras implementaciones.

Lo más destacable es que busca las dependencias automáticamente (usando el flag -MM del compilador), de forma que cuando modificamos un fichero solo recompilamos los objetos que se ven afectados. Además asume que todos los ficheros .cc del directortio forman parte del proyecto y que los includes locales están en el mismo directorio (es un proyecto prequeño).

La verdad es que probé CMake, que es lo que usa SFML para configurar y compilar; pero entre que no tengo necesidad real de configurar (por ahora no distribuyo fuentes) y al final CMake genera Makefiles, decidí investigar un poco más lo que ya sabía en lugar de dedicar tiempo a una herramienta nueva que por ahora no necesito.

Al final lo que más tiempo me llevó fue dar con el orden en el que hay que enlazar las librerías, por tema de dependencias entre ellas, y todo porque no leí correctamente la entrada en el FAQ :(.

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

Entradas antiguas