Animación compleja

Started by DCelso, September 28, 2009, 11:14:08 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

DCelso

Hola a todos,
Tengo en mente un ejercicio de una animación en la que el fichero de animación es el siguiente:

[Nombre de estado1]
id1 de la imagen, milisegundos que dura
id2 de la imagen, milisegundos que dura
..
idn de la imagen, milisegundos que dura

[Nombre de estado2]
id1 de la imagen, milisegundos que dura
id2 de la imagen, milisegundos que dura
..
idn de la imagen, milisegundos que dura

..
..
[Nombre de estadon]
id1 de la imagen, milisegundos que dura
id2 de la imagen, milisegundos que dura
..
idn de la imagen, milisegundos que dura

el proceso debe tener almenos una variable llamada estado, y un vector de estructuras (llamada st_animacion) de longitud igual al número de estados que contiene el siguiente campo: un vector de estrcutrudas (llamada st_postura) con los siguientes campos: id_imagen, mili segundos que dura.

Además el proceso debe cambiar al siguiente estado al pulsar la tecla "e" y empezar de nuevo la animación en este nuevo estado.

A ver si alguien que le gusten los retos personales, y tenga tiempo libre :D, es capaz de poner, al menos, una aproximación inicial a lo pedido , para poder tenerlo todos como ejemplo y empezar a buscarle defectos y/o mejoras. :D
Monstruos Diabólicos

"A PAck of classic GAMEs For BennuGD" en desarrollo
http://code.google.com/p/apagame4be/

SplinterGU

muy parecido a mi crap de zombies
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

Windgate

DCelso, tengo un módulo llamado animaciones.prg que se encarga de todo eso que decís, estoy preparando el tema asociado, acabo de hacer el 22 (http://trinit.es/tutoriales/) en el que se introduce a la animación compleja y el 23 es el que suelta el módulo con gestión de lo que dices, incluída latencia en FRAMES para cambiar de gráfico y alguna cosa más :P

No obstante cualquier ejercicio o recurso adicional que sueltes bienvenido sea.
Iván García Subero. Programador, profesor de informática, monitor de actividades culturales y presidente de TRINIT Asociación de Informáticos de Zaragoza. http://trinit.es

DCelso

ya que sacas el tema,acabo de ver tu tutorial 22, y te comento varias cosillas:
en la última página en el pie de página pone: Tema 21: efectos de sonido
Creo que tal y como usas el proceso animar, sería mejor que fuera "function" ya que ésas, en teoría, son menos costosas ya que "usan el contexto del proceso que las llamó".
Por otro lado la llamada a animar, creo que debería ir justo antes de la palabra frame, en vez de al principio. Ya que queda un poco más lógico que primero selecciones los gráficos de la animación y luego intentes animar. Actualmente lo que hace es seleccionar los gráficos de la animación, esperar al siguiente frame y ya en el siguiente frame animas lo calculado en el frame anterior. Esto es algo conceptual porque en la práctica hacen casi lo mismo solo que va un frame desfasado.
Luego en el rigth y en el left, se te ha olvidado o bien poner el flag a 1 o a 0, o bien en uno de ellos ir recorriendo los gráficos en orden inverso(de mayor a menor) ya que sino andará hacia atrás como maikel jacson (a no ser que sea eso lo que quieras :D).

Monstruos Diabólicos

"A PAck of classic GAMEs For BennuGD" en desarrollo
http://code.google.com/p/apagame4be/

Windgate

Cierto todo DCelso, me he apresurado a subirlo para ayudar un poquillo al N44B.

He resubido temas 20 21 y 22 con el pie de página corregido y lo de la animación lo arreglo nuevo porque entro a currar ya!

Y he rectificado el juego de ejemplo del tema 20 para que salgan los 2 personajes amigos, aunque esto lo debería decir en el hilo del tutorial... Pero bueno.
Iván García Subero. Programador, profesor de informática, monitor de actividades culturales y presidente de TRINIT Asociación de Informáticos de Zaragoza. http://trinit.es

DCelso

Ya que nadie se anima, pongo el código inicial que tengo para intentar optimizarlo (para hacerlo más fácil de entender) con vuestra ayuda.

Program animacion;

import "mod_video"
import "mod_key"
import "mod_draw"
import "mod_proc"
import "mod_map"
import "mod_time"
import "mod_file"
import "mod_regex"

Global
profundidad = 32;
FPSteoriocos = 30;
ancho = 200;
alto = 150;
duracion_maxima = 80000;
tiempo_ini, tiempo_actual;
duracion_restante;
int ganar = 0;
type st_postura;
id_imagen;
m_segundos;
End
type st_animacion;
st_postura postura[100];
int num_posturas;
string nombre;
End
Begin
set_mode(ancho,alto,profundidad);
set_fps(FPSteoriocos,0);
    tiempo_ini = get_timer() + duracion_maxima;
    player(ancho/2,alto/2);
    while (!key(_esc))
        if (key(_f))
            full_screen = !full_screen;
            set_mode(ancho,alto,profundidad);
        end
        frame;
end
let_me_alone();
end

process player(x,y)
private
    int direccion = 1;
    int avance = 5;
    int anim_actual;
    st_animacion animaciones[4];
    int num_animaciones;
    int i;
    pose_actual=0;
    t_inical, t_transcurrido;   
begin
num_animaciones =read_ani_file("recursos/jimmy.ani",&animaciones);
    file = create_fpg_from_tileset("recursos/jimmy.png",12,12);
    anim_actual = 1;
    pose_actual =0;
    graph = animaciones[anim_actual].postura[pose_actual].id_imagen;
    t_inical = get_timer();
while (!key(_esc) )
    t_transcurrido = get_timer() - t_inical;
    if (t_transcurrido > animaciones[anim_actual].postura[pose_actual].m_segundos)
          t_inical = get_timer();
          pose_actual++;
      if (pose_actual >= animaciones[anim_actual].num_posturas);
           pose_actual = 0;
      end
        end
        if (key(_down))
            anim_actual =0;
        end
        if (key(_up))
            anim_actual =2;
        end
        if (key(_left))
            anim_actual =1;
            flags =1;
        end
        if (key(_right))
            anim_actual =1;
            flags =0;
        end
        if (key(_a))
            anim_actual =3;
        end
        graph = animaciones[anim_actual].postura[pose_actual].id_imagen;
        Frame(100);
end
unload_fpg(file);
end

function read_ani_file(string ani_filename, st_animacion pointer animaciones)
private
    num_animaciones;
    file1;
    i;
    string line;
    string commands[100];
    string subcommands[10];
begin
num_animaciones =-1;
    file1 = fopen("recursos/jimmy.ani",o_read);
    while (!feof(file1) )
   line = fgets(file1);
   i = split("]",line,&commands,100);
   if (i == 1)
       split(",",commands[0],&subcommands,100);
       animaciones[num_animaciones].postura[animaciones[num_animaciones].num_posturas].id_imagen=subcommands[0];
       animaciones[num_animaciones].postura[animaciones[num_animaciones].num_posturas].m_segundos=subcommands[1];
       animaciones[num_animaciones].num_posturas++;
       else
         num_animaciones++;
         animaciones[num_animaciones].nombre = commands[0];
       end
    end
    return num_animaciones;
end

function get_next_graph(st_animacion animacion_in,int index)
begin
    index ++;
    if (index > animacion_in.num_posturas)
        index = 1;
    end
    return index;
end

function create_fpg_from_tileset(string tilesetname,int num_columns,int num_rows)
private
    tileset;
    tile_width;
    tile_height;
    i,j,k;
    cur_tile;
begin
    tileset=load_png(tilesetname);
    tile_width=graphic_info(0,tileset,g_width)/num_columns;
    tile_height=graphic_info(0,tileset,g_height)/num_rows;
    file = fpg_new();
    cur_tile = new_map(tile_width,tile_height,profundidad);
    for (j=0;j<num_rows;j++)
        for (i=0;i<num_columns;i++)
         k= (j*num_rows+i)+1;
         map_clear(0,cur_tile,0);
         map_block_copy(0,cur_tile,0,0,tileset,i*tile_width,j*tile_height,tile_width,tile_height,0);
         fpg_add(file,k,0,cur_tile); 
        end
    end
    unload_map(0,cur_tile);
    unload_map(0,tileset);
    return file;
end

Aqui se ven muchos conceptos fundamentales a la hora de programar juegos además de la animación con estructuras, como son, control por tiempo, lectura de archivos de texto usando split, creación de fpgs a partir de una imagen "tileset", cambio a pantalla completa o ventana, control básico de teclado.
Monstruos Diabólicos

"A PAck of classic GAMEs For BennuGD" en desarrollo
http://code.google.com/p/apagame4be/

Windgate

Hay un parpadeo al pulsar arriba (O abajo no me acuerdo) supongo que será el número de gráfico que has puesto uno de menos o uno de más.

Entiendo el ejemplo y lo veo bien, se pasa al siguiente valor de gráfico del vector cuando transcurre el tiempo en milisegundos asociado a ese gráfico. No me atrevo a meter mano en tu código, pero se ve completo, diferencias que veo con mi idea:


  • Yo asumo que toda animación tiene números de gráfico consecutivos, y con almacenar la información de gráfico inicial y gráfico final me vale, luego sumo 1 y ya está.

  • También asumo que la latencia (frames de espera) para animar son los mismos para toda la animación, así que sólo guardo 1 valor asociado al gráfico inicial y final.
[/list]
Soluciones distintas pero válidas, yo haría un .prg separado que permita abstraerte un poco más. En mi caso basta con abrir el .prg y poner los gráficos iniciales y finales que corresponden al juego, luego una función animar() se ocupa de animar cualquier cosa.

Te dejo, aunque sea por simple curiosidad, un ejemplo de diagrama de estados para gestionar cambios de animación, que a partir de 3 animaciones distintas la cosa se complica y salen condiciones a puñados:

http://trinit.es/temario/Diagramas%20de%20Estados%20-%20Animaciones.pdf

También otro diagrama más complejo que gestiona la inicialización de un juego, carga de fpg, fnt y wav, inicialización de los valores de animación que te digo, etc. Teniendo en cuenta módulos en los que se encuentra cada cosa, éste diagrama mola más, es un DIN-A0 grande grande y tiene en cuenta la jerarquía de procesos de Bennu xD

http://trinit.es/temario/Diagramas%20III%20-%20Ejemplo%20Plataformas.pdf

Lo que más me ha sorprendido de tu ejemplo es el recorte automático del spritesheet, qué difícil es encontrar spritesheets bien organizados que permitan recortar así... Con los del Street Fighter por ejemplo hay que trabajar muchísimo y en su día hice un programa basado en map_block_copy que "copiaba" los recortes correspondientes de Blanka a partir de una estructura de datos con las coordenadas de corte, fue un maldito infierno...

Lo que me recuerda que tengo en mente hace tiempo hacer un programa que recorte automáticamente cualquier spritesheet basándome en recorrer pixel a pixel evitando filas/columnas con el color de fondo y copiando los sprites que resultan de ello... :P
Iván García Subero. Programador, profesor de informática, monitor de actividades culturales y presidente de TRINIT Asociación de Informáticos de Zaragoza. http://trinit.es

DCelso

:D, Gracias, sipo, lo ví justo después de postearlo. Es por culpa del orden, la captura de teclas hay que hacerla antes de el cambio de imagen de la animacion, osea, al principio del bucle principal. El código quedaría tal que así.

Program animacion;

import "mod_video"
import "mod_key"
import "mod_draw"
import "mod_proc"
import "mod_map"
import "mod_time"
import "mod_file"
import "mod_regex"

Global
profundidad = 32;
FPSteoriocos = 30;
ancho = 200;
alto = 150;
duracion_maxima = 80000;
tiempo_ini, tiempo_actual;
duracion_restante;
int ganar = 0;
type st_postura;
id_imagen;
m_segundos;
End
type st_animacion;
st_postura postura[100];
int num_posturas;
string nombre;
End
Begin
set_mode(ancho,alto,profundidad);
set_fps(FPSteoriocos,0);
    tiempo_ini = get_timer() + duracion_maxima;
    player(ancho/2,alto/2);
    while (!key(_esc))
        if (key(_f))
            full_screen = !full_screen;
            set_mode(ancho,alto,profundidad);
        end
        frame;
end
let_me_alone();
end

process player(x,y)
private
    int direccion = 1;
    int avance = 5;
    int anim_actual;
    st_animacion animaciones[4];
    int num_animaciones;
    int i;
    pose_actual=0;
    t_inical, t_transcurrido;   
begin
num_animaciones =read_ani_file("recursos/jimmy.ani",&animaciones);
    file = create_fpg_from_tileset("recursos/jimmy.png",12,12);
    anim_actual = 1;
    pose_actual =0;
    graph = animaciones[anim_actual].postura[pose_actual].id_imagen;
    t_inical = get_timer();
while (!key(_esc) )
        if (key(_down))
            anim_actual =0;
        end
        if (key(_up))
            anim_actual =2;
        end
        if (key(_left))
            anim_actual =1;
            flags =1;
        end
        if (key(_right))
            anim_actual =1;
            flags =0;
        end
        if (key(_a))
            anim_actual =3;
        end
    t_transcurrido = get_timer() - t_inical;
    if (t_transcurrido > animaciones[anim_actual].postura[pose_actual].m_segundos)
          t_inical = get_timer();
          pose_actual++;
      if (pose_actual >= animaciones[anim_actual].num_posturas);
           pose_actual = 0;
      end
        end
        graph = animaciones[anim_actual].postura[pose_actual].id_imagen;
        Frame(100);
end
unload_fpg(file);
end

function read_ani_file(string ani_filename, st_animacion pointer animaciones)
private
    num_animaciones;
    file1;
    i;
    string line;
    string commands[100];
    string subcommands[10];
begin
num_animaciones =-1;
    file1 = fopen("recursos/jimmy.ani",o_read);
    while (!feof(file1) )
   line = fgets(file1);
   i = split("]",line,&commands,100);
   if (i == 1)
       split(",",commands[0],&subcommands,100);
       animaciones[num_animaciones].postura[animaciones[num_animaciones].num_posturas].id_imagen=subcommands[0];
       animaciones[num_animaciones].postura[animaciones[num_animaciones].num_posturas].m_segundos=subcommands[1];
       animaciones[num_animaciones].num_posturas++;
       else
         num_animaciones++;
         animaciones[num_animaciones].nombre = commands[0];
       end
    end
    return num_animaciones;
end

function get_next_graph(st_animacion animacion_in,int index)
begin
    index ++;
    if (index > animacion_in.num_posturas)
        index = 1;
    end
    return index;
end

function create_fpg_from_tileset(string tilesetname,int num_columns,int num_rows)
private
    tileset;
    tile_width;
    tile_height;
    i,j,k;
    cur_tile;
begin
    tileset=load_png(tilesetname);
    tile_width=graphic_info(0,tileset,g_width)/num_columns;
    tile_height=graphic_info(0,tileset,g_height)/num_rows;
    file = fpg_new();
    cur_tile = new_map(tile_width,tile_height,profundidad);
    for (j=0;j<num_rows;j++)
        for (i=0;i<num_columns;i++)
         k= (j*num_rows+i)+1;
         map_clear(0,cur_tile,0);
         map_block_copy(0,cur_tile,0,0,tileset,i*tile_width,j*tile_height,tile_width,tile_height,0);
         fpg_add(file,k,0,cur_tile); 
        end
    end
    unload_map(0,cur_tile);
    unload_map(0,tileset);
    return file;
end


En cuanto a separarlo en procesos distintos, es lo suyo, pero no lo hice por no liar mas la perdiz e ir directamente más al grano de lo que quiero enseñar, tal vez en la siguiente versión :D. En cuanto a separar las cosas en .prgs distintos, para consensuar conceptos no es muy bueno ya que la gente se suele perder abriendo un archivo y otro constantemente, esta idea es genial cuando ya no quieres enseñar técnicas sino programar un juego sin tanto contenido didáctico, o con ánimo de lucro:D. Al menos eso pienso por mi experiencia. Un archivo mas o menos grande con todo es fácil de controlar, en cambio muchos archivos pequeños con poco contendio, la mayoría de veces complica más que beneficia. Hay siempre que llegar a un consenso e intentar separar pero con cabeza, solo cuando sea extrictamente necesario ya sea por no hacer muy grande y pesado un .prg o por separar un concepto que verdaderamente está muy alejado o no relaccionado con la tarea principal del .prg.
Monstruos Diabólicos

"A PAck of classic GAMEs For BennuGD" en desarrollo
http://code.google.com/p/apagame4be/