En esta entrega le añadiremos al programa un menú inicial, donde se pueda iniciar una partida, cargar partida, o salir del juego.
Objetivo
Crear el menu inicial del juego, y utilizar las funciones se guardar y cargar partida. Se guardará la partida de forma automática cuando salgamos pulsando la tecla ESC del juego.
Programación
Para el menu se crea un archivo llamado menu.h, donde se pondrá todo lo referente al menu.
El en archivo menu.h se tendrá lo siguiente:
/* Name: menu.h Author: KODAYGAMES Web: http://devcpp-allegro.blogspot.com/ */ #define NUMOP 3 class MIMENU { private: int op; int max_op; bool salidamenu; bool pulsa; public: MIMENU(); void pinta(); int teclado(); bool salir(); }; MIMENU::MIMENU(){ op = 1; max_op = NUMOP; salidamenu = false; pulsa = false; }; void MIMENU::pinta() { if ( op == 1 ) blit(menufondo2, buffer, 0, 220, 0, 220, PANTALLA_ANCHO, 70); if ( op == 2 ) blit(menufondo2, buffer, 0, 310, 0, 310, PANTALLA_ANCHO, 70); if ( op == 3 ) blit(menufondo2, buffer, 0, 400, 0, 400, PANTALLA_ANCHO, 70); }; int MIMENU::teclado(){ if (key[KEY_DOWN] && !pulsa ){ op++; pulsa = true; sonido_boton(); } if (key[KEY_UP] && !pulsa) {op--; pulsa=true; sonido_boton(); } if ( pulsa && !key[KEY_DOWN] && !key[KEY_UP] ) pulsa=false; if (op > max_op) op=1; if (op < 1) op=max_op; // comprueba si esta en la opcion de salir if ((key[KEY_ENTER] || key[KEY_ENTER_PAD]) && op == max_op ) salidamenu = true; if (key[KEY_ESC]) op=max_op; return op; }; bool MIMENU::salir(){ return salidamenu; };
La variable NUMOP contiene el numero de opciones que tiene el menu, en este caso 3.
Se crea una clase llamada MIMENU, que se encarga de mostrar el menu, y controlar si se pulsa las teclas del cursor para cambiar la opcion seleccionada.
op: contiene la opcion que esta actualmente seleccionada.
max_op: contiene el máximo de opciones que tiene el menu.
salidamenu: indica si se ha salido o no del menu. En este caso se sale cuando se pulsa la tecla ENTER o INTRO.
pulsa: indica si se ha pulsado algunas de las teclas que controla el menu, la tecla del cursor arriba y la tecla abajo.
La función MIMENU::MIMENU(), inicializa las variables.
La función MIMENU::pinta(), se encarga de pintar según el valor de op una de las opciones.
La función MIMENU::teclado(), se encarga de controlar las teclas del cursor, para que cambien el valor de op. Si se pulsa la tecla abajo aumenta en 1 el valor de op, y si se pulsa arriba disminuye en 1, cuando el valor de op es menor que 1 se cambia a valer el max_op que es 3. Y cuando el valor de op supera max_op, op vale 1. De esta forma solo tendras tres posibles valores 1,2 y 3.
Si se pulsa la tecla ESC, el valor de op es 3. Lo que indica que se selecciona la ultima opción que en este caso es SALIR.
Si se pulsa alguna de las teclas ENTER o INTRO y op es igual a 3, entonces pone a true salidamenu, para indicar que se a pulsado SALIR.
La función MIMENU::salir(), se utiliza para obtener el valor de la variable salidamenu, que es la que nos indica si se ha pulsado SALIR.
Haz clic aqui para descargar en RAR el archivo menu.h
En el archivo global.h, se declaran nuevas imagenes.
BITMAP *cbuffer; BITMAP *menufondo; BITMAP *menufondo2;
La imagen cbuffer, contendrá el mapa de choque actual que se está mostrando por pantalla.
La imagen menufondo, y menufondo2, estas dos variables BITMAP tendrán las imágenes que se utilizan para el menú inicio del juego.
En el archivo players.h, se añade una nueva función llamada pon_choque(), que se encargará de añadir un rectángulo de choque en la imagen de choque cbuffer.
void player::pon_choque() { rectfill( cbuffer, x+4, y+17, x+28, y+30, 0xffffff); }
La funcion player::choca(), cambia la forma de controlar el choque, se realiza mediante la imagen cbuffer, y por tanto se simplifica, ya que no es necesario calcular la posición con respecto al mapa, sino que se realiza con respecto a la pantalla.
bool player::choca() { bool resp=false; for (int i=2; i < 30; i++ ) { for (int j=16; j < 32; j++) { // color rojo if ( getpixel( cbuffer, x+i, y+j) == 0xff0000 ) resp=true; // color verde if ( getpixel( cbuffer, x+i, y+j) == 0x00ff00 ) { cambio = 1; resp=true; } // color azul if ( getpixel( cbuffer, x+i, y+j) == 0x0000ff ) { cambio = 2; resp=true; } // color amarillo if ( getpixel( cbuffer, x+i, y+j) == 0xffff00 ) { cambio = 3; resp=true; } } } return resp; }
En la función player::teclado(), se ha quitado el control del mapa de choque.
void player::teclado() { int ax = x; int ay = y; if ( !hablar && !swtienda ) { // 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 ( key[KEY_SPACE] && ataca == 0 ) { ataca = 1; } if ( !key[KEY_SPACE] && ataca == 1 ) { ataca = 2; sonido_espada_aire(); } } if ( ax != x || ay != y ) { if ( choca() ) { x =ax; y =ay; }else{ int num = FRAME_RATE / 12; if ( tiempo_total % num == 0 ) { sonido_pasos(); // entra si a cambiado alguna de las variables x,y animacion++; if ( animacion > 2 ) animacion = 0; } } } if ( ataca > (FRAME_RATE / 4) ) ataca = 0; }
La función player::cambia_escenario(), se elimina ya que no es necesario.
Haz clic aqui para descargar RAR del archivo players.h
En el archivo npc.h, se ha cambiado la clase npc.
class npc { protected: // posicion int x,y; int ax,ay; int direccion; int animacion; int escena; int estado; int img; BITMAP* imagen; public: void crea( int i, int _x, int _y, int dir, int _estado, int _lugar ); void pinta(); void actualiza(); bool chocanpc(); bool posicion_cerca(int num=0); void cambia_estado(int _estado){ estado = _estado; }; bool frente(); bool alineado_vertical(); const int getimg() { return img; }; const int getx() { return x; }; const int gety() { return y; }; const int getdir() { return direccion; }; const int getestado(){ return estado; }; const int getlugar() { return escena; }; };Se ha eliminado las variables mifondo y primer. Se ha cambiado la función crear y se ha añadido algunas funciones para poder obtener los datos del npc para guardarlos.
void npc::crea( int i, int _x, int _y, int dir, int _estado, int _lugar) { x = _x; y = _y; direccion = dir; animacion = 0; escena = _lugar; img = i; BITMAP *_img = (BITMAP *)datosjuego[i].dat; imagen = create_bitmap(_img->w, _img->h); blit( _img, imagen, 0,0, 0,0, _img->w, _img->h); estado = _estado; }
Antes la función npc::crea(), recibía una imagen bitmap. Ahora recibe un índice, mediante el cual accede a la imagen utilizando (BITMAP *)datosjuego[i].dat;
La función npc::pinta() cambia por completo.
void npc::pinta() { if ( lugar == escena ) { actualiza(); int vx = x - desplazamiento_map_x; int vy = y - desplazamiento_map_y; rectfill( cbuffer, vx+2, vy+1, vx+30, vy+31, 0xff0000); masked_blit(imagen, buffer, animacion*32, direccion*32, vx, vy, 32,32); } }
La función npc::chocanpc(), tambien cambia ya que se utiliza la imagen de choque cbuffer.
bool npc::chocanpc() { int ninix,niniy; int nfinx,nfiny; if ( direccion == 0 ) { // abajo ninix = 0; niniy = 32 - desplazamiento; nfinx = 32; nfiny = 32; } if ( direccion == 1 ) { // izquierda ninix = 0; niniy = 0; nfinx = desplazamiento; nfiny = 32; } if ( direccion == 2 ) { // derecha ninix = 32 - desplazamiento; niniy = 0; nfinx = 32; nfiny = 32; } if ( direccion == 3 ) { // arriba ninix = 0; niniy = 0; nfinx = 32; nfiny = desplazamiento; } int vx; int vy; // comprobar si colisiona con el mapa for ( int ci=ninix; ci < nfinx; ci++) { for (int cj=niniy; cj < nfiny; cj++) { vx = x - desplazamiento_map_x; vy = y - desplazamiento_map_y; // color rojo if ( getpixel( cbuffer, vx+ci, vy+cj) == 0xff0000 || getpixel( choque, x+ci, y+cj) == 0xff0000 ){ return true; } // color blanco prota if ( getpixel( cbuffer, vx+ci, vy+cj) == 0xffffff ){ return true; } } } return false; }
La función npc::actualiza() cambia, ahora no tiene que controlar el manejo de la imagen de choque.
void npc::actualiza() { // para indicar que se ejecuta dos veces int num = FRAME_RATE / 6; if ( tiempo_total % num == 0 ) { if ( estado != 0 ) { animacion++; if ( animacion > 2 ) animacion = 0; } switch ( estado ) { case 1: // camina horizontal ax = x; ay = y; if ( direccion == 1 ) { // camina izquierda x-=desplazamiento; if ( chocanpc() ) { // posicion no valida x = ax; direccion = 2; } } if ( direccion == 2 ) { // camina derecha x+=desplazamiento; if ( chocanpc() ) { // posicion no valida x = ax; direccion = 1; } } break; case 2: // camina vertical ax = x; ay = y; if ( direccion == 0 ) { // camina abajo y+=desplazamiento; if ( chocanpc() ) { // posicion no valida y = ay; direccion = 3; } } if ( direccion == 3 ) { // camina arriba y-=desplazamiento; if ( chocanpc() ) { // posicion no valida y = ay; direccion = 0; } } break; case 3: // camina giro derecha ax = x; ay = y; if ( direccion == 0 ) { // camina abajo y+=desplazamiento; if ( chocanpc() ) { // posicion no valida y = ay; direccion = 1; } } if ( direccion == 1 ) { // camina izquierda x-=desplazamiento; if ( chocanpc() ) { // posicion no valida x = ax; direccion = 3; } } if ( direccion == 2 ) { // camina derecha x+=desplazamiento; if ( chocanpc() ) { // posicion no valida x = ax; direccion = 0; } } if ( direccion == 3 ) { // camina arriba y-=desplazamiento; if ( chocanpc() ) { // posicion no valida y = ay; direccion = 2; } } break; case 4: // camina giro izquierda ax = x; ay = y; if ( direccion == 0 ) { // camina abajo y+=desplazamiento; if ( chocanpc() ) { // posicion no valida y = ay; direccion = 2; } } if ( direccion == 1 ) { // camina izquierda x-=desplazamiento; if ( chocanpc() ) { // posicion no valida x = ax; direccion = 0; } } if ( direccion == 2 ) { // camina derecha x+=desplazamiento; if ( chocanpc() ) { // posicion no valida x = ax; direccion = 3; } } if ( direccion == 3 ) { // camina arriba y-=desplazamiento; if ( chocanpc() ) { // posicion no valida y = ay; direccion = 1; } } break; case 5: // camina libre if ( tiempo_total % 200 == 0 ) { direccion = rand()%4; } ax = x; ay = y; if ( direccion == 0 ) { // camina abajo y+=desplazamiento; if ( chocanpc() ) { // posicion no valida y = ay; direccion = rand()%4; } } if ( direccion == 1 ) { // camina izquierda x-=desplazamiento; if ( chocanpc() ) { // posicion no valida x = ax; direccion = rand()%4; } } if ( direccion == 2 ) { // camina derecha x+=desplazamiento; if ( chocanpc() ) { // posicion no valida x = ax; direccion = rand()%4; } } if ( direccion == 3 ) { // camina arriba y-=desplazamiento; if ( chocanpc() ) { // posicion no valida y = ay; direccion = rand()%4; } } break; default: // parado if ( tiempo_total % 300 == 0 ) { direccion = rand()%4; } break; } } }
Al igual que en la clase npc, la clase enemigo se cambia a la hora de crear. No requiere una imagen, sino un id de la imagen. Y se cambia el manejo del choque, se utiliza cbuffer.
class enemigo : public npc { int vida; int v_actual; bool muerto; int golpeado; int exp; public: void crea( int i, int _x, int _y, int dir, int _estado, int _lugar, int v ); void herida( int d ); void pinta(); bool ha_muerto() { return muerto; }; void movimiento(); void daexp(int e){ exp = e; }; const int getvida() { return vida; }; }; void enemigo::crea( int i, int _x, int _y, int dir, int _estado, int _lugar, int v ) { x = _x; y = _y; direccion = dir; animacion = 0; escena = _lugar; img=i; BITMAP *_img = (BITMAP *)datosjuego[i].dat; imagen = create_bitmap(_img->w, _img->h); blit( _img, imagen, 0,0, 0,0, _img->w, _img->h); estado = _estado; vida = v; v_actual = vida; muerto = false; golpeado=0; exp = 50; }; void enemigo::herida( int d ) { if ( !muerto ) { int num = FRAME_RATE / 2; v_actual-=d; if ( v_actual <= 0 ) { muerto = true; int vx = x - desplazamiento_map_x; int vy = y - desplazamiento_map_y; rectfill( cbuffer, vx+2, vy+1, vx+30, vy+31, 0x000000); sonido_muere(); jugador.sube_experiencia(exp); // siempre obtiene un objeto aleatorio int q2 = lobj.size(); int q = lobj[rand()%q2].id; jugador.obtiene_objeto( q ); }else{ // daño defensivo del enemigo if ( tiempo_total % num == 0 ) { if ( rand()%2 == 1 ) { sonido_herido(); jugador.herido(5+rand()%5); } } } } }; void enemigo::pinta() { if ( lugar == escena && !muerto ) { if ( v_actual == vida ) { actualiza(); }else{ movimiento(); } int vx = x - desplazamiento_map_x; int vy = y - desplazamiento_map_y; rectfill( cbuffer, vx+2, vy+1, vx+30, vy+31, 0xff0000); masked_blit(imagen, buffer, animacion*32, direccion*32, vx, vy, 32,32); if ( golpeado == 1 ) { int xn = 2 + rand()%2; jugador.no_ataca(); if ( rand()%10 != 1 ) { sonido_espada_da(); herida(xn); }else{ sonido_espada_choca(); } golpeado = 0; } if ( golpeado == 0 && jugador.atacando() && posicion_cerca() && frente() ) { golpeado = 1; } if ( !muerto ) { int nm = (v_actual * 30 ) / vida; rectfill( buffer, vx+1, vy, vx+nm, vy+5, 0x00ff00); rect( buffer, vx, vy, vx+31, vy+5, 0x000000); } } if ( lugar != escena && v_actual < vida ) { int num = FRAME_RATE / 5; if ( tiempo_total % num == 0 ) { v_actual++; } } } void enemigo::movimiento() { ax = x; ay = y; int jx = jugador.getx() + desplazamiento_map_x; int jy = jugador.gety() + desplazamiento_map_y; // para indicar que se ejecuta dos veces int num = FRAME_RATE / 6; int esta = 0; // mira hacia donde este el jugador if ( jx > x ) { direccion = 2; }else{ direccion = 1; } if ( jy > y ) { if ( abs ( jx - x ) < desplazamiento*3 ) { direccion = 0; } }else{ if ( abs ( jx - x ) < desplazamiento*3 ) { direccion = 3; } } // enemigo te persigue if ( tiempo_total % num == 0 ) { if ( x+32 < jx ) { x+=desplazamiento; esta = 1; } if ( x > jx+32 ) { x-=desplazamiento; esta = 1; } if ( y+32 < jy ) { y+=desplazamiento; esta = 1; } if ( y > jy+32 ) { y-=desplazamiento; esta = 1; } } if ( ax != x || ay != y ) { // se ha movido en una de las direcciones if ( chocanpc() ) { x = ax; y = ay; esta = 0; } if ( esta != 0 ) { animacion++; if ( animacion > 2 ) animacion = 0; } } if ( posicion_cerca() ) { int num = FRAME_RATE / 3; if ( tiempo_total % num == 0 ) { if ( rand()%3 == 1 ) { sonido_herido(); jugador.herido(2+rand()%2); animacion++; if ( animacion > 2 ) animacion = 0; } } } }Ya no se utiliza directamente la imagen choque, para poner el choque del npc y el enemigo. Esto se controla con la nueva imagen cbuffer, que almacena el mapa de choque de lo que se ve por pantalla.
Haz clic aqui para descargar en RAR el archivo npc.h
En el archivo main.cpp, se añade dos archivos nuevos menu.h y partidas.h.
#include < iostream > #include < allegro.h > #include "log.h" #include "datosjuego.h" #include "botones.h" #include "global.h" #include "audio.h" #include "menu.h" #include "misobjetos.h" #include "dialogos.h" #include "players.h" #include "tiendas.h" #include "mijuego.h" #include "partidas.h"
En la función inicia_allegro(), se añade la inicialización de la nueva variable cbuffer, justo despues de inicializar buffer.
cbuffer = create_bitmap(PANTALLA_ANCHO, PANTALLA_ALTO);
El main() cambia por completo, para añadir el menu.
int main() { inicia_allegro(); carga_inicio(); musica_menu(); int op; MIMENU menu; do{ blit(menufondo, buffer, 0, 0, 0, 0, PANTALLA_ANCHO, PANTALLA_ALTO); menu.pinta(); op = menu.teclado(); // se a seleccionado una opcion if (key[KEY_ENTER] || key[KEY_ENTER_PAD]) { if ( op == 2 ) { // carga partida carga_partida(); sonido_sube_nivel(); } if ( op == 1 ) { jugador.inicia(); lugar = 1; desplazamiento_map_x=-160; desplazamiento_map_y=-160; sonido_sube_nivel(); npersonaje = 10; personajes[0].crea( diper001, 1300,700, 1,1,3); personajes[1].crea( diper005, 280, 450, 0,2,3); personajes[2].crea( diper005, 230, 280, 3,2,3); personajes[3].crea( diper003, 960, 310, 2,3,3); personajes[4].crea( diper005, 1120, 450, 0,4,3); personajes[5].crea( diper004, 900, 650, 1,5,3); personajes[6].crea( diper006, 850, 800, 0,0,3); personajes[7].crea( diper001, 530, 280, 1,5,3); personajes[8].crea( diper007, 334, 170, 0,0,4); personajes[9].crea( diper008, 142, 170, 0,0,4); nmalos = 3; malos[0].crea( diene001, 380, 280, 3,5,2,100); malos[1].crea( diene001, 400, 720, 0,5,2,100); malos[2].crea( diene001, 380, 240, 0,5,2,100); malos[3].crea( diper005, 440, 720, 3,5,2,100); } if ( op == 1 || op == 2 ) { carga_juego(); // juego salir = false; while ( !salir ) { // tecla de salida if ( key[KEY_ESC] ) salir = true; 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); } } if ( lugar == 2 ) para_sonido_ambiente(); // fin juego guarda_partida(); musica_menu(); op=2; } } pintar_pantalla(); }while ( !menu.salir() ); destroy_bitmap(fondo); destroy_bitmap(choque); destroy_bitmap(cielo); destroy_bitmap(menufondo); destroy_bitmap(menufondo2); destroy_bitmap(cbuffer); destroy_bitmap(buffer); return 0; } END_OF_MAIN();
Se hace que cuando salgas de la partida, se guarde automáticamente.
Haz clic aqui para descargar en RAR el archivo main.cpp
Y hasta aquí llega este curso, aun falta un próximo curso en el que detalla los cambios realizados en el archivo mijuego.h. Por tanto, aun no se puede compilar pues no se tiene todo.
No hay comentarios:
Publicar un comentario