Funciones que no deberían faltar en ningún juego

Started by Anyeos, April 23, 2013, 10:37:15 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Anyeos

He crado para usar en mis juegos, y quisiera compartir con ustedes, una serie de funciones útiles organizadas en dos ficheros (diferenciados por su tarea):

       
  • siscomun.prg
  • snd.prg
Siscomun tiene las funciones que son las que siempre se necesitan para arrancar con un juego, por ejemplo la carga de las imágenes.
Snd su nombre lo dice, son las funciones de sonido. Pero incluyen una "mejorita".

No son demasiadas funciones, justamente son prácticas y simples.
La ventaja que tiene la de carga de imágenes, contra no usar esta utilidad, es que te permite organizar tus archivos todos en png. Pero, genera automáticamente un fpg a partir de toda esa información. Es una especie de "cacheador" de pngs a fpg.

Esto te permite desarrollar tus propios sprites en el formato estándar png sin tener que recurrir a las buenas pero a veces cansadoras herramientas de edición de FPG. Y luego cuando quieres distribuir tus juegos no es necesario copiar los png, simplemente lo distribuyes con los fpg que se autogeneraron.


Snd tiene un par de  funciones interesantes para manejar mejor los sonidos. Además de que también tiene una para cargar los sonidos, aunque a diferencia de la anterior sólo utiliza los propios archivos .wav porque nada más hace una llamada a "load_wav".

Pero, tanto la anterior como esta, te permiten organizar tus recursos en directorios. Así que podrías poner todos tus archivos de sonidos en el directorio "sonidos" y todos tus gráficos en el directorio "graficos". Para cambiar eso deberás modificar un poquito las funciones y reemplazar donde corresponda. Te vas a dar cuenta fácil ya que son pequeñas y simples.


Algo que debes saber a la hora de darles uso es que las imágenes deberás colocarlas en otro directorio además del "graficos". Ese otro directorio es el que va a adquirir luego el nombre del fichero fpg. Por ejemplo dentro de "graficos" puedes tener "naves", luego se generará un fichero "naves.fpg" dentro de "graficos".
Otra cosa importante es que los nombres de los archivos png deben comenzar con número. Por ejemplo: "06 - Misil.png". Ya que es de esa forma como podrá determinar el índice que le tocará dentro del FPG. Así a la hora de programar tu juego ya sabes que el graph=6 corresponde al misil.
¿Qué te parece?

Ojo, que cada llamada a la función debe ir acompañada de la cantidad de dígitos que deseas manejar, y también deberás respetar eso a la hora de nombrar los archivos. Por ejemplo si le pides 2 dígitos tienes que utilizar numeración tipo "00", "01", "02", y así sucesivamente, o sea, los ceros a la izquierda acá cuentan (si le pides 3 digitos sería "000", "001", etc). Si omites eso simplemente ese PNG no será considerado como válido y no será cargado. Pero todos los demás que estén bien sí se cargarán. Así que no se corre mucho riesgo si haces eso, simplemente te va a faltar esa imagen. El resto de los índices no se verán afectados pero ese si.
Esto también puede ser de utilidad, porque puedes colocar imágenes con nombres no reconocibles por la función para usarlas como referencia solo para ti (cuando no estás seguro de si usar en tu juego esa imagen o no). Y no afectarán al juego en lo absoluto.

Ojo2: Cada vez que modifiques un PNG, deberás borrar el .fpg asociado, de lo contrario nunca notarás el cambio ya que la función busca primero un .fpg, y si lo encuentra lo carga. Entonces para generar otra vez el .fpg a partir de tus png deberás borrar el .fpg que se generó antes. Esto es así para no perder el tiempo cargando los png si ya existe un fpg, además es para que puedas distribuir tu juego sólo con los fpg sin necesidad de incluir los png. Obvio, verdad? :P

Hay dos funciones más en ese archivo. Y sirven para convertir un word a signo negativo. O sea, almacenas un "int" que no supere la mitad de la capacidad de "word" pero están permitidos valores negativos. Obviamente la otra función realiza la tarea inversa. Esto sirve para almacenar valores negativos en un archivo ocupando menos espacio que si lo almacenaras en int.
Pero estás limitado a valores desde -32767 hasta 32768.


Ahora las de sonido
He creado una función que lo que hace es reproducir un sonido en un lugar determinado, sí, algo más o menos como sonido 3D, pero no es sonido realmente 3D, es sonido en estéreo posicionado (o sea, 2D).
Otra característica que tiene es que puede reproducir en 3 formas diferentes:

       
  • Normal
  • Loopeado
  • Indetenible
El normal es normal, o sea que si llamas a la función de vuelta sin que el sonido haya terminado de reproducir simplemente lo recomienza a reproducir nuevamente. Esto da un efecto como sonido tipo ametralladora, por ejemplo si llamas varias veces seguidas a la función.

El loopeado el nombre te lo dice, se reproduce en un lazo infinito (o sea, nunca termina, apenas llega al final vuelve a comenzar desde el principio), pero la llamada sucesiva a esta función va cambiando su posición en el espacio.

Luego indetenible es un sonido que no se detiene y se reproduce hasta llegar al final. Y aunque hagas varias llamadas sucesivas a la función, el sonido no volverá a comenzar a reproducirse desde el principio como lo haría en normal (esa es la única diferencia que tiene), pero le cambiará la ubicación en el espacio.

Esa función está hecha así a propósito para poder llamarla repetidas veces sin que se interrumpa la reproducción del sonido pero sí permitiendo cambiar la posición en el espacio. Así que la utilización recomendada es siempre haciéndole una llamada por cada "frame".


Dónde conviene usar los tipos de reproducción
La reproducción tipo normal sirve más para sonidos tipo explosiones o efectos especiales de impacto ya que suelen ocurrir espontáneamente y se necesita que se reproduzcan siempre desde el comienzo (por ejemplo una sucesión de explosiones quedarían bien de esa manera, en cambio si las reproducimos en tipo "indetenibles" no se alcanzarían a distinguir que fueron varias explosiones ya que sólo oiríamos la primera porque las demás pasarían más rápido de lo que termina en reproducirse el sonido).

La "indetenible" es útil para efectos más largos como el sonido de electrocución ya que requiere que el sonido se escuche completo sin que se interrumpa. Si se interrumpe, generaría la sensación de que estamos escuchando un golpe (o algo peor, un sonido repetitivo horrible) si usáramos "normal", por eso existe este tipo de reproducción.

Y la "loopeado" sirve para reproducir sonidos tipo como un motor que requiere que se repita siempre (en lazo infinito). Podríamos usar "indetenible" para esto pero en algún momento que se demore en llamar a la función se generaría una interrupción en el sonido (un silencio muy corto) y "quedaría mal" ya que se esperaba que se reproduzca con continuidad y sin cortes. Para eso sirve el tipo de reproducción loopeado.
Para detener el sonido de este tipo, se deberá llamar a la otra función que se encarga de ese trabajo: DetenerSND.


No te olvides que de todos modos tienes que editar un poco los archivos y ajustarlos a tu necesidad por ejemplo cambiando el directorio donde estarán los recursos de tu juego.

¿Y cuáles son las funciones? Acá te las digo, pero primero quería explicarte para qué fueron hechas (o sea, su propósito de vida, pobres funciones, démosles una oportunidad).
Las funciones más comunes son:
fpgdir_loadcache(string directorio, int digitos);
int2sigword( int i );
sigword2int( word w );

IniciarSND();
load_snd( string archivo );
EmitirSND( int waveid, int canal, int x2, int y2, int tipo );
DetenerSND( int waveid, int canal );
DetenerSNDTodos();


Es probable que luego haga una nueva versión de "load_snd" que sea equivalente a "fpgdir_loadcache" en el tema de utilizar directorios. En lugar de especificar uno por uno los archivos con su ruta, lo haría con un parámetro adicional. Mi idea es hacer de estas funciones multiplataforma, así no tenemos la necesidad de especificar directorios a la hora de cargar los recursos del juego.


Ah, otra cosa que nos ahorra siscomun.prg es la necesidad de hacer "import" a varios modulos que normalmente se utilizan. Así que en caso de que no quieras cargar ciertos módulos vas a tener que quitar su llamada editando ese fichero.




Te voy a dar un ejemplo de utilización:

include "siscomun.prg"
include "snd.prg"
...
    set_title("Naves2000");
    graph_mode = MODE_16BITS;
    restore_type = COMPLETE_RESTORE;
    g_modes = MODE_DOUBLEBUFFER |  MODE_WAITVSYNC;
    set_mode(640, 480, 16, g_modes );
    set_fps(30, 0);

    IniciarSND();
   
    g_region1 = region_define(1, 5, 5, 629, 445);
    fpg_main = fpgdir_loadcache("main", 1);
    fpg_naves = fpgdir_loadcache("naves", 2);
    snd_avionjet = load_snd("avionjet.wav");

    Start_scroll(0, fpg_main, 0, 1, 1, 15);
    scroll.flags1 = B_TRANSLUCENT;
    scroll.flags2 = B_TRANSLUCENT;
    scroll.ratio = 200;
...
    file = fpg_naves;
    graph = 2;
...
            EmitirSND(snd_avionjet, CANAL_JET, x, y, SNDT_LOOPED);
...
        FRAME;
...
            DetenerSND(snd_avionjet, CANAL_JET);


En el ejemplo tenemos un directorio llamado "graficos" con otros dos directorios: "main" y "naves".
En esos directorios tenemos:
En "main" los siguientes archivos: "0 - Fondo espacial.png" y "1 Fondo galaxia.png".
En "naves" los siguientes archivos: "00 - Nave jugador.png", "01 - Disparo laser.png", "02_Nave Enemigo.png", "03 Disparo enemigo.png".

Como te habrás dado cuenta, el nombre puede contener cualquier caracter después de los dígitos obligatorios. He utilizado un guión "-", un espacio " ", y un guión bajo "_" para separar el número del nombre. Puedes usar cualquier símbolo o ninguno en absoluto eso es solamente a fines estéticos o como a ti más cómodo te resulte para leer tus archivos (yo utilizo la forma con el guión). Lo importante es que lo primero debe ser un número con la cantidad de dígitos igual a lo especificado y luego la extensión y el formato debe ser .png.

El otro directorio del ejemplo es "sonidos". Y ahí simplemente tenemos un solo archivo llamado: "avionjet.wav".

Fijate que se utilizó un scroll. Por supuesto que el resto del código no está presente, queda a cargo de tu imaginación ;)
Pero lo que quiero destacar es que EmitirSND se llamará tantas veces como FRAME, ya que si no lo hacemos el sonido no irá "moviéndose" con la nave, sino que quedará reproduciéndose en el sitio donde se inició por primera vez.
No es necesario detener todos los tipos de sonido (llamando a DetenerSND), depende el efecto que se busque, pero, salvo el SNDT_LOOPED, el resto se reproduce máximo hasta que termina el sonido y se detiene automáticamente.

Como también puedes observar resulta sencillo cargar todos los PNG como si siempre se tratara de un FPG. Inclusive si cambias el bit depth (profundidad de color) del modo de video, sólo bastará con borrar el FPG que se ha autogenerado y luego automáticamente tu juego volverá a cargar todos los PNG y generará un nuevo FPG siempre compatible con el modo de video actual.
La verdad que esta función me ha ahorrado un enorme trabajo y me permite trabajar llanamente con ficheros PNG (que son fácilmente editables con cualquier programa) evitando la complicación de tener que estar añadiéndolos manualmente con un editor de FPG.




QuoteSi los nombres de las funciones te parecen mejor al revés, por ejemplo en lugar de IniciarSND sería SNDIniciar, me gustaría que me dieras tu opinión de por qué, por favor. Ya que obviamente tu tienes derecho a hacer lo que gustes con estos archivos, si quieres los puedes modificar, no hay problema con eso. El problema en realidad es que no me decido y por ahora prefiero usar la forma como IniciarSND ya que en español se lee mejor (y creo que así queda bien).




Bueno, espero que sean útiles para ti ya que para mi lo han sido.
No te preocupes en hacerles todas las modificaciones que necesites para adaptarlo a tu juego (o solamente si quieres utilizar una parte está bien). Pero tienen un copyright porque si vas a distribuir ese código fuente modificado, quiero dejar en claro que tienes todo eso permitido. Pero si le has hecho tus propias modificaciones y quieres distribuirlo así, pues, deja en claro también que esas son tus modificaciones y que yo no tengo nada que ver con esas modificaciones.



Que tengas un buen día.

Yawin

Yo tengo mi propia plantilla que incluye en un archivo las funciones de configuración, en otro las funciones de carga, la splash screen, etc... igual algún día la subo.
Sigue el desarrollo de mi motor RPG: https://www.youtube.com/watch?v=TbsDq3RHU7g

process main()
       begin
           loop
               pedo();
               frame;
            end
       end

Anyeos

Sería bueno que lo subas, comparte si te apetece, date el gusto, jaja.

También podríamos hacer funciones estándares para tener un menú. Por ahora me están faltando esas.
El menú principal que normalmente uno configura el audio, el video, da inicio a las partidas y todo eso. Eso de configurar el audio el video e inclusive los controles para el jugador, tampoco deberían faltar en ningún juego.