Si quieres ver alguna de las anteriores entregas entra en el Contenido del Blog.
Hasta el momento solo se tiene un personaje andando, cuya animación de movimiento es muy variable según el valor que se le haya dado a la función rest(). Algunos que tengan ordenadores mas nuevos y potentes le habrán tenido que poner un número mas alto, y los que tengan ordenadores antiguos y lentos le habrán puesto un valor mas pequeño. Esto no es mas que un apaño, es decir, que solo nos vale a quienes lo estamos programando y probando, ya que se ha adaptado dicho valor a nuestro ordenador. Se debe programar algo mas genérico que pueda funcionar en todos por igual.
Para ello, se debe de controlar los Fotogramas por Segundo (FPS).
Que son los FPS ?
Las imágenes por segundo (fotogramas por segundo o cuadros por segundo, en inglés frames per second o FPS) es la medida de la frecuencia a la cual se muestran distintos fotogramas (frames). En informática estos fotogramas están constituidos por un número determinado de píxeles que se distribuyen a lo largo de una red de texturas. La frecuencia de los fotogramas es proporcional al número de píxeles que deben generarse, incidiendo en el rendimiento del ordenador que los reproduce. El número de FPS que el ojo humano necesita para ver una imagen con fluidez es de 24.
Aparte del control de los FPS, también se añade el primer escenario. Esto conlleva a el control de colisiones, y superposiciones de objetos.
Programación
Se realizarán los siguientes cambios:
En global.h se añade
En global.h se añade
// controla el bucle principal bool salir; // Variable usada para la velocidad volatile unsigned int contador_tiempo_juego = 0; // Indica los FPS const int FRAME_RATE = 30; // Función para controlar la velocidad void inc_contador_tiempo_juego() { contador_tiempo_juego++; } END_OF_FUNCTION(inc_contador_tiempo_juego)
En el programa principal vuelve a cambiar, quedando de la siguiente forma:
/* Name: RPG Author: Yadok - KODAYGAMES Date: 27/08/15 Web: http://devcpp-allegro.blogspot.com/ Description: Creacion de un juego al estilo RPG mas informacion en la web Version: curso 4 */ #include < allegro .h > #include "global.h" #include "players.h" #include "mijuego.h" void inicia_allegro() { allegro_init(); install_keyboard(); set_color_depth(32); set_gfx_mode(GFX_AUTODETECT_WINDOWED, PANTALLA_ANCHO, PANTALLA_ALTO, 0, 0); buffer = create_bitmap(PANTALLA_ANCHO, PANTALLA_ALTO); LOCK_VARIABLE(contador_tiempo_juego); LOCK_FUNCTION(inc_contador_tiempo_juego); // Iniciamos el limitador de FPS install_int_ex(inc_contador_tiempo_juego, BPS_TO_TIMER( FRAME_RATE )); } // programa principal int main() { inicia_allegro(); carga_juego(); salir = false; while ( !salir ) { if ( contador_tiempo_juego ) { while ( contador_tiempo_juego ) { actualiza_juego(); contador_tiempo_juego--; } clear_to_color(buffer, 0x00000); pinta_juego(); pintar_pantalla(); }else{ rest(1); } // tecla de salida if ( key[KEY_ESC] ) salir = true; } destroy_bitmap(buffer); return 0; } END_OF_MAIN();
En el curso anterior se comentó de poner las funciones del jugador en un archivo aparte, aqui se muestra el contenido de ese archivo que he llamado players.h
// players.h // Esta clase se encarga del manejo del jugador class player { BITMAP *prota; int x,y; int direccion; int animacion; public: void inicia(); void pinta(); void teclado(); int getx(){ return x; }; int gety(){ return y; }; void posiciona( int _x, int _y); }; void player::inicia() { prota = load_bmp("personaje.bmp",NULL); // inicializar vbles direccion = 0; animacion = 0; x = 540; y = 280; } void player::pinta() { masked_blit(prota, buffer, animacion*32, direccion*32, x, y, 32,32); } void player::teclado() { int ax = x; int ay = y; // teclas control usuario if ( key[KEY_UP] ) { y-=desplazamiento; direccion = 3; } if ( key[KEY_DOWN] ) { y+=desplazamiento; direccion = 0; } if ( key[KEY_LEFT] ) { x-=desplazamiento; direccion = 1; } if ( key[KEY_RIGHT] ) { x+=desplazamiento; direccion = 2; } if ( ax != x || ay != y ) { // entra si a cambiado alguna de las variables x,y animacion++; if ( animacion > 2 ) animacion = 0; } // limites globales if ( x < 0 ) x = 0; if ( x > PANTALLA_ANCHO ) x = PANTALLA_ANCHO; if ( y < 0 ) y = 0; if ( y > PANTALLA_ALTO ) y = PANTALLA_ALTO; } void player::posiciona( int _x, int _y) { x=_x; y=_y; }
Y tambien se a añadido otro archivo en el que se pondrá lo que es la base de nuestro juego. A este archivo lo he llamado mijuego.h
/* mijuego.h */ BITMAP *fondo; BITMAP *choque; BITMAP *alto; player jugador; // carga todo lo necesario antes de empezar el juego void carga_juego() { jugador.inicia(); // cargamos imagenes del primer escenario fondo = load_bmp("casa.bmp",NULL); choque = load_bmp("casa-choque.bmp",NULL); alto = load_bmp("casa-sup.bmp",NULL); } // actualiza el estado del juego void actualiza_juego() { int ax,ay; ax = jugador.getx(); ay = jugador.gety(); jugador.teclado(); // comprobar si colisiona con el mapa bool choca = false; int px = jugador.getx()-160; int py = jugador.gety()-160+16; for ( int ci=0; ci < 32; ci++) { for (int cj=0; cj < 16; cj++) { if ( getpixel( choque, px+ci, py+cj) == 0xff0000 ){ choca = true; ci = 32; cj = 16; } if ( getpixel( choque, px+ci, py+cj) == 0x00ff00 ) salir = true; } } if ( choca ){ // vuelve al estado anterior jugador.posiciona( ax,ay ); } } // Se encarga de pintar todo sobre el buffer void pinta_juego() { blit( fondo, buffer, 0,0, 160, 160, 480,325); jugador.pinta(); masked_blit( alto, buffer, 0,0, 160, 160, 480,325); }
Paso a Paso
Para el control de los FPS se ha creado una función llamada inc_contador_tiempo_juego, esta función solo se encarga de incrementar el valor de la variable contador_tiempo_juego un determinado numero de veces por segundo.
install_int_ex(inc_contador_tiempo_juego, BPS_TO_TIMER( FRAME_RATE ));
Esta es la función que se encarga de que se llame según la variable FRAME_RATE que en el ejemplo tiene un valor de 30.
El bucle principal tiene una primera condición que se cumplirá siempre que contador_tiempo_juego tenga un valor distinto a 0. Dentro de esta condición tiene un bucle que se estará repitiendo hasta que el valor de contador_tiempo_juego sea 0, mientras se estará actualizando nuestro juego. En el momento en el que llega al valor 0 sale del bucle y continua pintando por pantalla. De este modo como el contador_tiempo_juego se incrementará 30 veces en un segundo, en teoría, deberá pintarlo 30, ya que mientras que el valor de la variable sea 0 no hará nada.
En player.h se han añadido algunas nuevas funciones que hacia falta, como son las que permite obtener la posición actual getx(), gety() y la función que sitúa al personaje en una posición indicada ( posiciona ).
En mijuego.h se tiene lo siguiente:
- carga_juego: se encarga de carga todos los datos y archivos que se necesiten antes de iniciar el juego.
- actualiza_juego: se encarga del manejo interno del juego, aquí es donde se controla todo, ya sea el movimiento del jugador, como la colisión con los objetos.
- pinta_juego: se encarga de volcar todas las imagenes sobre el buffer, para montar la imagen que se mostrará por pantalla.
Esta es la imagen de choque, en esta imagen se marca con el color rojo por donde no queremos que pueda pasar el personaje, y el color verde para indicar la salida.
La función utilizada para controlar la colisión del personaje con el escenario se encarga de comparar la mitad del espacio que ocupa el personaje, con la imagen choque. Con el comando getpixel va comprobando pixel a pixel si donde se va a situar la imagen del personaje existe algún pixel de color rojo, en este caso existe colisión y por tanto vuelve a su posición anterior. Y en el caso de que detecte algún pixel de color verde este provocará la finalización del programa.
bool choca = false; int px = jugador.getx()-160; int py = jugador.gety()-160+16; for ( int ci=0; ci < 32; ci++) { for (int cj=0; cj < 16; cj++) { if ( getpixel( choque, px+ci, py+cj) == 0xff0000 ){ choca = true; ci = 32; cj = 16; } if ( getpixel( choque, px+ci, py+cj) == 0x00ff00 ) salir = true; } }
El primer bucle recorre la imagen a lo ancho (32 pixel) y la segunda a lo alto (16 pixel). Las variables px,py contiene la posición del jugador dentro de la imagen choque, como se ha mostrado la imagen choque en la posición (160,160), por eso se le resta 160 a la posición del jugador. Y en la altura se le suma 16 para que tenga en cuenta la parte de abajo.
El comando getpixel obtiene el pixel que se encuentra en la posicion px+ci, py+cj de la imagen choque, y devuelve el valor del color de dicho pixel.
El comando getpixel obtiene el pixel que se encuentra en la posicion px+ci, py+cj de la imagen choque, y devuelve el valor del color de dicho pixel.
En la función de pinta_juego(), se pinta primero el fondo, luego al personaje y finalmente la imagen con zonas transparente.
Llegado a este punto, si todo esta correctamente copiado donde debe, solo hará falta tener las imagenes, para evitar posibles fallos las he comprimido las imagenes en un archivo RAR .
Si alguno quiere descargarse el codigo fuente del proyecto en Dev-c++
¿En donde puedo generar mis propios bmp?
ResponderEliminarPuedes utilizar cualquier editor gráfico que te permita guardar en formato BMP, por ejemplo, si tienes el Sistema Operativo Windows, este trae un editor bastante sencillo y simple llamado Paint.
EliminarDisculpa, pero no me detecta el color verde al salir del escenario, copie todo el codigo de la pagina mijuego.h solo para verificar si estaba haciendo algo mal, pero no, todo esta igual en el codigo, sabes a que se deba?
ResponderEliminarcomprueba que en la imagen el color sea exactamente el verde 0x00ff00
ResponderEliminarGracias, cambie el tono del verde y ahora el programa funciona a la perfeccion, buen blog amigo :D
EliminarMe gustaria saber para que sirve la foto que tiene el rosa que finalidad tiene y que criterios hay que usar para poner el rosa no lo entiendo muy bien muchas gracias
ResponderEliminarEl comando masked_blit utiliza el color rosa 0xff00ff como color transparente (no pinta). Y se utiliza en el ejemplo para poner la imagen casa-sup.bmp por encima de las otras, de manera que el personaje puede quedar tapado segun esta imagen. ( en el ejemplo el prota se puede ocultar detras de las macetas y la bañera)
Eliminarhola amigo... estoy siguiendo tus tutoriales desde hace unos dias, y siento que este es el mejor y mo entiendo por que lo descontinuaste para iniciar otro en otro blog... ese esta muy desordenado si lo comparamos con este...
ResponderEliminarSe creó el otro blog ya que este utiliza la librería de Allegro versión 4, y en el otro se utiliza la librería versión 5.
Eliminar