Você está na página 1de 301

Crea tu RPG en C++ y Allegro

Este es un curso realizado en el Dev-C++, esta programado en C++ y con la librera Allegro 4.4.

En el curso se va enseando poco a poco a montar un juego RPG, lo que se suele llamar como action RPG. De manera que se tenga
una base para luego poder hacer uno mismo su propio juego. Para poder realizar el curso se recomienda que se tenga conocimientos
bsicos en programacin y C++. Aunque se intentar explicar todos los comandos nuevos que se van utilizando.

Contenido del curso


1. Creando un personaje
2. Aadiendo animacin
3. Programacin modular
4. Control de FPS
5. Creando mi primer escenario
6. Control del Scroll
7. Aadiendo algunos efectos sonoros
8. Aadiendo msica
9. Creacion de un fichero DAT
10. Aadiendo un segundo escenario
11. Creando un NPC
12. Movimiento de un NPC
13. Una nueva clase para los dilogos
14. Aadiendo dilogos al juego
o Nueva fuente en los dilogos
15. Sistema de Lucha
16. Barra de vida
17. Sistema de Lucha II
18. Experiencia
19. Nivel de dificultad del juego
20. Inventario
21. Inventario II
22. Tiendas
23. Tiendas II
24. Tiendas III
25. Guardar Partida
26. Cargar Partida
27. Menu
28. Menu II
29. Control de errores

Debido a que cada vez que se aade una imagen, sonido, etc., se debe de modificar el fichero DAT, se hace algo pesado tener que
descargarlo en cada entrega la versin correspondiente. Como este archivo se va manteniendo para todo el curso y se va ampliando
segn va avanzando el curso el tamao de este archivo va aumentando mucho, he decidido facilitar el trabajo a todos aquellos que
estn haciendo el curso, poniendo aqu en esta pgina el ultimo fichero DAT. De esta manera aunque ests empezando los primeros
temas, ya no tendrs que ir actualizandolo, solo tendrs que descargarlo una vez, siempre que no salga un nuevo tema que incluya
algn fichero multimedia.

Recordad que este fichero DAT se empieza a utilizar a partir del tema 9, ya que antes no se utiliza. Este fichero DAT es acumulativo,
es decir, que el ultimo te vale para todo lo anterior.

Recursos del curso


Tema - Contenido - Link
1 - Primer ejemplo - Descargar
4 - Ejemplo completo - Descargar
5 - Cdigo programa - Descargar
5 - Imagenes - Descargar
7 - Archivo sonido Wav - Descargar
8 - Archivo msica midi - Descargar

Para todos los temas superior al 8.


Fichero DAT, versin vlida hasta el tema 28. - Descargar
Fichero DAT, versin vlida hasta el tema 24. - Descargar
Fichero DAT, versin vlida hasta el tema 21. - Descargar
Fichero DAT, versin vlida hasta el tema 20. - Descargar
Fichero DAT, versin vlida hasta el tema 18. - Descargar
Fichero DAT, versin valida hasta el tema 17. - Descargar
Fichero DAT (archivos multimedia), versin vlida hasta el tema 16. - Descargar

A partir del tema 21 se utiliza tambin los archivos objetos.


Fichero objetos DAT y TXT . valido para 21. - Descargar

Fichero DAT datostienda tema 24 - Descargar


Fichero DAT objetos tema 24 - Descargar

Del tema 28, los archivos de los objetos y la tienda, que incluye objetos.dat, objetos.txt, datostienda.dat, tiendas.txt, list01.txt y
list02.txt. Descargar
Crear juego RPG en C++ y Allegro 4

Aqui comienza el primer curso de crear tu juego, al estilo RPG maker, para ello se utiliza el lenguaje C++ y la librera Allegro
Version 4.4.

Cual es el estilo RPG maker, pues para que se entienda mejor mirar la siguiente imagen ...

En este primer tutorial, se har el manejo del personaje principal. Para ello tendremos en cuenta:

1. Se utilizar para la imagen del protagonista un character set del RPG Maker.
2. Se utilizar las teclas del cursor para mover al personaje.
Que es un character set del RPG Maker ?

Se trata de una imagen, donde viene dibujado al personaje segn las cuatro direcciones, abajo, izquierda, derecha y arriba. Y cada una
de ellas tiene tres variantes para hacer una pequea animacin.

Para nuestro tutorial utilizaremos la siguiente imagen, pero si no os gusta o queris utilizar otra podis buscar mas en google.

Estas imgenes suelen venir en formato png, pero como el Allegro no trabaja con png, se debe transformar la imagen a BMP. Adems
para luego no tener problemas el color blanco de fondo se debe sustituir por el color rosa que es el que utiliza Allegro para las
transparencia.

Esta imagen la guardaremos con el nombre de "personaje.bmp".


Programacin
A continuacin se explica paso a paso el programa utilizado.

allegro_init();
install_keyboard();

set_color_depth(32);
set_gfx_mode(GFX_AUTODETECT_WINDOWED, 800, 600, 0, 0);

Se empieza inizializando la librera y definiendo el tamao de la ventana que vamos a crear, en este ejemplo ser de 800x600.

BITMAP *buffer = create_bitmap(800, 600);


BITMAP *prota = load_bmp("personaje.bmp",NULL);

bool salir;
int x,y;

// inicializar vbles
x = 10;
y = 10;
salir = false;

Se inicializan las variables que se van a manejar en el programa. Dos imagenes BITMAP, una de ellas contendr la imagen del
personaje (prota) y la otra la utilizaremos para montarlo todo antes de volcarlo a la pantalla ( buffer ). Las variables x,y contienen la
posicin del personaje, que inicialmente se le da el valor de 10,10 respectivamente.

El bucle principal se encarga de pintar el fondo, comprobar si el usuario a pulsado alguna tecla para mover al personaje, pinta al
personaje y finalmente lo muestra por pantalla.

clear_to_color(buffer, 0xaaaaaa);

masked_blit(prota, buffer, 0,0, x, y, 32,32);


// teclas control usuario
if ( key[KEY_UP] )
{
y--;
}
if ( key[KEY_DOWN] )
{
y++;
}
if ( key[KEY_LEFT] )
{
x--;
}
if ( key[KEY_RIGHT] )
{
x++;
}

// limites
if ( x < 0 ) x = 0;
if ( x > 800 ) x = 800;
if ( y < 0 ) y = 0;
if ( y > 600 ) y = 600;

blit(buffer, screen, 0, 0, 0, 0, 800, 600);

rest(10);

// tecla de salida
if ( key[KEY_ESC] ) salir = true;

clear_to_color(buffer, 0xaaaaaa);
Borra el contenido de buffer, rellenandolo de un color gris (0xaaaaaa).
masked_blit(prota, buffer, 0,0, x, y, 32,32);
Pinta la imagen prota sobre la imagen buffer, en la posicin x,y dentro de la imagen buffer. Con un tamao de la imagen prota de
32x32.

El apartado de teclas control usuario, se encarga de las teclas definidas para mover el personaje, para ello se modifican los valores de
x,y. Siendo "x" para moverlo en horizontal y la "y" para moverlo en vertical, en este caso se utilizan las teclas del cursor.

El apartado limites, se encarga de establecer los limites de las variables x,y , que dependen del tamao de la ventana que en este caso
son 800,600. De esta forma se evita que se salga del campo de vision la imagen prota.

blit(buffer, screen, 0, 0, 0, 0, 800, 600);


Vuelca el contenido de la imagen buffer sobre screen, que es la pantalla, con un tamao que previamente sea definido, segn el
ejemplo es 800,600. Es justo en este momento en el que se muestra todo por pantalla.

rest(10);
Hace una pequea pausa de 10 milisegundos.

CODIGO COMPLETO DEL EJEMPLO


?
1 /*
2 Name: RPG
3 Author: Yadok - KODAYGAMES
4 Date: 27/08/15 14:49
5 Web: http://devcpp-allegro.blogspot.com/
6 Description:
7 Creacion de un juego al estilo RPG
8 mas informacion en la web
9 */
10
11
12#include <allegro.h>
13
14int main()
15{
16 allegro_init();
17 install_keyboard();
18
19 set_color_depth(32);
20 set_gfx_mode(GFX_AUTODETECT_WINDOWED, 800, 600, 0, 0);
21
22 BITMAP *buffer = create_bitmap(800, 600);
23 BITMAP *prota = load_bmp("personaje.bmp",NULL);
24
25 bool salir;
26 int x,y;
27
28 // inicializar vbles
29 x = 10;
30 y = 10;
31 salir = false;
32
33 while ( !salir )
34 {
35 clear_to_color(buffer, 0xaaaaaa);
36
37 masked_blit(prota, buffer, 0,0, x, y, 32,32);
38
39 // teclas control usuario
40 if ( key[KEY_UP] )
41 {
42 y--;
43 }
44 if ( key[KEY_DOWN] )
45 {
46 y++;
47 }
48 if ( key[KEY_LEFT] )
49 {
50 x--;
51 }
52 if ( key[KEY_RIGHT] )
53 {
54 x++;
55 }
56
57 // limites
58 if ( x < 0 ) x = 0;
59 if ( x > 800 ) x = 800;
60 if ( y < 0 ) y = 0;
61 if ( y > 600 ) y = 600;
62
63
64 blit(buffer, screen, 0, 0, 0, 0, 800, 600);
65
66 rest(10);
67
68 // tecla de salida
69 if ( key[KEY_ESC] ) salir = true;
70
71 }
72
73 destroy_bitmap(prota);
74 destroy_bitmap(buffer);
75
76 return 0;
77}
78END_OF_MAIN();

Si todo esta correcto, se tendr un programa que muestra un personaje por pantalla que se puede mover con las teclas del cursor.
Si deseas descargar todo lo que se utiliza en este ejemplo pulsa en el link

Crear juego RPG en C++ y Allegro 4 (2)

Segunda entrega del curso, continuando con lo anterior en esta entrega vamos a poner animacin a nuestro personaje, ya que aunque se
puede desplazar por la pantalla la imagen del personaje no cambia.
Como anteriormente vimos, la imagen personaje.bmp contiene la animacin y direcciones.

Segn vemos en la imagen, empezando de arriba a abajo se tiene las siguientes direcciones:

1. Abajo
2. Izquierda
3. Derecha
4. Arriba

El tamao de uno de los personajes si se quisiera cojer solo es de 32x32 pixel.


Para controlar la direccin y la animacin se crean dos variables.
La variable direccion contendr cuatro posibles valores 0 - 3.
La variable animacion contendr tres posibles valores 0 - 2.
Tambin se crea una copia de (x,y) llamados (ax,ay) para guardar el valor anterior, y as comprobar si ha cambiado o no.

Programacin
Empezamos con los cambios en nuestro programa.
Se declaran e inicializan las nuevas variables
int ax,ay;
int direccion;
int animacion;
int desplazamiento;
// inicializar vbles
ax = 10;
ay = 10;
direccion = 0;
animacion = 0;
desplazamiento = 4;

Dentro del bucle principal al inicio se debe de dar valor a las variables (ax,ay) con sus respectivos valores.

ax = x;
ay = y;

La parte de control del teclado de usuario cambia completamente por lo siguiente:

// 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;
}

Y lo ultimo que se debe de cambiar y mas importante es la parte que se encarga de mostrar al personaje.

masked_blit(prota, buffer, animacion*32, direccion*32, x, y, 32,32);

Con este comando estamos indicando de que pinte en buffer una imagen de 32x32, que sera recortada de la imagen prota segn las
variables animacion y direccion. Y sern situadas en la posicin x,y.

Quedando todo el bucle principal de la siguiente manera:

?
1 while ( !salir )
2 {
3 clear_to_color(buffer, 0xaaaaaa);
4
5 masked_blit(prota, buffer, animacion*32, direccion*32, x, y, 32,32);
6 ax = x;
7 ay = y;
8 // teclas control usuario
9 if ( key[KEY_UP] )
10 {
11 y-=desplazamiento;
12 direccion = 3;
13 }
14 if ( key[KEY_DOWN] )
15 {
16 y+=desplazamiento;
17 direccion = 0;
18 }
19 if ( key[KEY_LEFT] )
20 {
21 x-=desplazamiento;
22 direccion = 1;
23 }
24 if ( key[KEY_RIGHT] )
25 {
26 x+=desplazamiento;
27 direccion = 2;
28 }
29 if ( ax != x || ay != y )
30 {
31 // entra si a cambiado alguna de las variables x,y
32 animacion++;
33 if ( animacion > 2 ) animacion = 0;
34 }
35
36 // limites
37 if ( x < 0 ) x = 0;
38 if ( x > 800 ) x = 800;
39 if ( y < 0 ) y = 0;
40 if ( y > 600 ) y = 600;
41
42
43 blit(buffer, screen, 0, 0, 0, 0, 800, 600);
44
45 rest(60);
46
47 // tecla de salida
48 if ( key[KEY_ESC] ) salir = true;
49
50 }

Si se fijan, ahora el valor de rest() es mayor debido a que en el pc en el que se ha probado iba muy rpido, por eso se aument el valor
a 60, en el caso de ir algo lento pueden reducirlo, recuerda que el numero indica el tiempo de espera en milisegundos.

Llegado a este punto, si todo esta correcto tendremos lo mismo que se muestra en el siguiente video.

Crear juego RPG en C++ y Allegro 4 (3)

Una nueva entrega del tutorial para crear tu propio juego RPG en C++ y Allegro.

En este tutorial prepararemos el cdigo programado, en algo mas modular y organizado para facilitar futuras ampliaciones. Para ello
vamos a crear algunas funciones y alguna que otra clase.

Si observamos el cdigo que tenemos hasta el momento, vemos que la funcin main lo contiene todo, si siguiramos programando de
este modo llegara un punto en el que sera muy difcil de encontrar un fallo, o algo que queramos modificar. Para evitar que perdamos
el control debemos poner comentarios para facilitar la bsqueda de cada uno de los procesos que realiza el juego, adems de crear
funciones modulares que nos permita tener el cdigo de forma ordenada.

Ahora se aadir un nuevo archivo al proyecto. Para los que tenga el Dev-C++ se colocar sobre el nombre de nuestro proyecto, pulsa
con el boton derecho, y en el men desplegable selecciona la opcion "Nuevo codigo fuente", para aadir otro archivo a nuestro
proyecto el cual lo llamaremos "global.h"
En este archivo, se pondr todas las variables que se quieran que sean global.

Que es una variable Global ?


Una variable global puede ser modificada en cualquier parte del programa y cualquier parte del programa depende de ella. Es por ello
que una variable global tiene un potencial ilimitado para crear dependencias. Sin embargo, en algunas ocasiones, las variables globales
resultan muy tiles. Por ejemplo, se pueden usar para evitar tener que pasar variables usadas muy frecuentemente de forma continua
entre diferentes subrutinas o funciones

Por tanto, en global.h pondremos lo siguiente:


// GLOBAL.H

// Ancho y alto de la pantalla


const int PANTALLA_ANCHO = 800;
const int PANTALLA_ALTO = 600;
// En este BITMAP dibujaremos todo
BITMAP *buffer;
// es el espacio en pixel que recorre el jugador al andar
const int desplazamiento=4;

// Copiar el buffer a la pantalla del juego (screen)


void pintar_pantalla()
{
blit(buffer, screen, 0, 0, 0, 0, PANTALLA_ANCHO, PANTALLA_ALTO);
}

Se crean dos variables PANTALLA_ANCHO y PANTALLA_ALTO para guardar el tamao de la pantalla y as facilitar su uso.
El BITMAP *buffer se pone de forma global pues ser utilizado muchas veces y nos facilitar su uso. De igual forma se crea una
funcin pintar_pantalla() para que sea mas legible nuestro cdigo, esta funcin se encarga de volcar el contenido de buffer a la
pantalla.

Como tenemos ahora global.h, este fichero o librera debemos incluirlo en el cdigo principal. Y esto se hace mediante el comando
#include "global.h"

En el archivo main.cpp, para que sea algo mas modular se separa lo que es la inicializacin del Allegro y configuracin de la
resolucin a una funcin llamada inicia_allegro().

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);


}
Se crea una clase llamada player que se encarga de todo lo referente al usuario o jugador, como comprobar las teclas, pintar el
personaje, etc.

?
1 // Esta clase se encarga del manejo del jugador
2 class player
3 {
4 BITMAP *prota;
5 int x,y;
6 int direccion;
7 int animacion;
8
9 public:
10 player();
11 void pinta();
12 void teclado();
13};
14
15player::player()
16{
17 prota = load_bmp("personaje.bmp",NULL);
18 // inicializar vbles
19 direccion = 0;
20 animacion = 0;
21 x = 10;
22 y = 10;
23}
24
25void player::pinta()
26{
27 masked_blit(prota, buffer, animacion*32, direccion*32, x, y, 32,32);
28}
29
30void player::teclado()
31{
32 int ax = x;
33 int ay = y;
34 // teclas control usuario
35 if ( key[KEY_UP] )
36 {
37 y-=desplazamiento;
38 direccion = 3;
39 }
40 if ( key[KEY_DOWN] )
41 {
42 y+=desplazamiento;
43 direccion = 0;
44 }
45 if ( key[KEY_LEFT] )
46 {
47 x-=desplazamiento;
48 direccion = 1;
49 }
50 if ( key[KEY_RIGHT] )
51 {
52 x+=desplazamiento;
53 direccion = 2;
54 }
55 if ( ax != x || ay != y )
56 {
57 // entra si a cambiado alguna de las variables x,y
58 animacion++;
59 if ( animacion > 2 ) animacion = 0;
60 }
61
62 // limites
63 if ( x < 0 ) x = 0;
64 if ( x > PANTALLA_ANCHO ) x = PANTALLA_ANCHO;
65 if ( y < 0 ) y = 0;
66 if ( y > PANTALLA_ALTO ) y = PANTALLA_ALTO;
67}

Esta clase si se quiere se puede poner en otro archivo aparte y de esta manera separamos todo lo referente al jugador.

Y con todo esto se consigue tener una funcin principal mas corta y sencilla de entender.
?
1 // programa principal
2 int main()
3 {
4 inicia_allegro();
5
6 player jugador;
7 bool salir;
8
9 salir = false;
10
11 while ( !salir )
12 {
13 clear_to_color(buffer, 0xaaaaaa);
14
15 jugador.teclado();
16
17 jugador.pinta();
18
19 pintar_pantalla();
20
21 rest(60);
22
23 // tecla de salida
24 if ( key[KEY_ESC] ) salir = true;
25
26 }
27
28 destroy_bitmap(buffer);
29
30 return 0;
31}

El programa sigue haciendo exactamente lo mismo pero internamente para seguir programando es mas fcil.
Recuerda aadir el include del nuevo archivo global.h al inicio, justamente debajo de la llamada a la libreria allegro, tal y como se
muestra a continuacin:

?
1#include <allegro.h>
2#include "global.h"

En el caso que hayas decidido separar la clase player a otro archivo, debers poner su correspondiente include, si por ejemplo se ha
llamado el archivo player.h, debes poner #include "player.h"

Crear juego RPG en C++ y Allegro 4 (4) FPS

Aqu esta la cuarta entrega, en ella se aadir el primer escenario, y algunos cambios en el cdigo para mejorar la jugabilidad,
controlando los FPS.
Si quieres ver alguna de las anteriores entregas entra en el Contenido del Blog.

Recuerda, si tienes algn problema con los ejemplos de la pagina, o alguna duda. Puedes plantear tu pregunta en el foro de
programacin:

http://creatusjuegosdecero.webege.com/index.php

Hasta el momento solo se tiene un personaje andando, cuya animacin de movimiento es muy variable segn el valor que se le haya
dado a la funcin rest(). Algunos que tengan ordenadores mas nuevos y potentes le habrn tenido que poner un nmero mas alto, y los
que tengan ordenadores antiguos y lentos le habrn puesto un valor mas pequeo. Esto no es mas que un apao, 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 genrico que pueda funcionar en todos por igual.

Para ello, se debe de controlar los Fotogramas por Segundo (FPS).

Que son los FPS ?


Las imgenes por segundo (fotogramas por segundo o cuadros por segundo, en ingls frames per second o FPS) es la medida de la
frecuencia a la cual se muestran distintos fotogramas (frames). En informtica estos fotogramas estn constituidos por un nmero
determinado de pxeles que se distribuyen a lo largo de una red de texturas. La frecuencia de los fotogramas es proporcional al nmero
de pxeles que deben generarse, incidiendo en el rendimiento del ordenador que los reproduce. El nmero de FPS que el ojo humano
necesita para ver una imagen con fluidez es de 24.

Aparte del control de los FPS, tambin se aade el primer escenario. Esto conlleva a el control de colisiones, y superposiciones de
objetos.

Programacin
Se realizarn los siguientes cambios:

En global.h se aade

// 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;

// Funcin 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:

?
1 /*
2 Name: RPG
3 Author: Yadok - KODAYGAMES
4 Date: 27/08/15
5 Web: http://devcpp-allegro.blogspot.com/
6 Description:
7 Creacion de un juego al estilo RPG
8 mas informacion en la web
9 Version: curso 4
10*/
11
12
13#include < allegro .h >
14#include "global.h"
15#include "players.h"
16#include "mijuego.h"
17
18
19
20void inicia_allegro()
21{
22 allegro_init();
23 install_keyboard();
24
25 set_color_depth(32);
26 set_gfx_mode(GFX_AUTODETECT_WINDOWED, PANTALLA_ANCHO, PANTALLA_ALTO, 0, 0);
27
28 buffer = create_bitmap(PANTALLA_ANCHO, PANTALLA_ALTO);
29
30 LOCK_VARIABLE(contador_tiempo_juego);
31 LOCK_FUNCTION(inc_contador_tiempo_juego);
32
33 // Iniciamos el limitador de FPS
34 install_int_ex(inc_contador_tiempo_juego, BPS_TO_TIMER( FRAME_RATE ));
35}
36
37 // programa principal
38int main()
39{
40 inicia_allegro();
41
42 carga_juego();
43
44 salir = false;
45
46
47 while ( !salir )
48 {
49 if ( contador_tiempo_juego )
50 {
51 while ( contador_tiempo_juego )
52 {
53 actualiza_juego();
54
55 contador_tiempo_juego--;
56 }
57
58 clear_to_color(buffer, 0x00000);
59
60 pinta_juego();
61
62 pintar_pantalla();
63
64 }else{
65
66 rest(1);
67 }
68 // tecla de salida
69 if ( key[KEY_ESC] ) salir = true;
70 }
71
72 destroy_bitmap(buffer);
73
74 return 0;
75}
76END_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

?
1 // players.h
2
3
4 // Esta clase se encarga del manejo del jugador
5 class player
6 {
7 BITMAP *prota;
8 int x,y;
9 int direccion;
10 int animacion;
11
12 public:
13 void inicia();
14 void pinta();
15 void teclado();
16 int getx(){ return x; };
17 int gety(){ return y; };
18 void posiciona( int _x, int _y);
19};
20
21
22void player::inicia()
23{
24 prota = load_bmp("personaje.bmp",NULL);
25 // inicializar vbles
26 direccion = 0;
27 animacion = 0;
28 x = 540;
29 y = 280;
30}
31
32
33void player::pinta()
34{
35 masked_blit(prota, buffer, animacion*32, direccion*32, x, y, 32,32);
36}
37
38void player::teclado()
39{
40 int ax = x;
41 int ay = y;
42 // teclas control usuario
43 if ( key[KEY_UP] )
44 {
45 y-=desplazamiento;
46 direccion = 3;
47 }
48 if ( key[KEY_DOWN] )
49 {
50 y+=desplazamiento;
51 direccion = 0;
52 }
53 if ( key[KEY_LEFT] )
54 {
55 x-=desplazamiento;
56 direccion = 1;
57 }
58 if ( key[KEY_RIGHT] )
59 {
60 x+=desplazamiento;
61 direccion = 2;
62 }
63 if ( ax != x || ay != y )
64 {
65 // entra si a cambiado alguna de las variables x,y
66 animacion++;
67 if ( animacion > 2 ) animacion = 0;
68 }
69
70 // limites globales
71 if ( x < 0 ) x = 0;
72 if ( x > PANTALLA_ANCHO ) x = PANTALLA_ANCHO;
73 if ( y < 0 ) y = 0;
74 if ( y > PANTALLA_ALTO ) y = PANTALLA_ALTO;
75}
76
77void player::posiciona( int _x, int _y)
78{
79 x=_x;
80 y=_y;
81}
Y tambien se a aadido otro archivo en el que se pondr lo que es la base de nuestro juego. A este archivo lo he llamado mijuego.h

?
1 /*
2 mijuego.h
3
4 */
5
6 BITMAP *fondo;
7 BITMAP *choque;
8 BITMAP *alto;
9
10player jugador;
11
12
13// carga todo lo necesario antes de empezar el juego
14void carga_juego()
15{
16 jugador.inicia();
17 // cargamos imagenes del primer escenario
18 fondo = load_bmp("casa.bmp",NULL);
19 choque = load_bmp("casa-choque.bmp",NULL);
20 alto = load_bmp("casa-sup.bmp",NULL);
21}
22
23
24// actualiza el estado del juego
25void actualiza_juego()
26{
27 int ax,ay;
28 ax = jugador.getx();
29 ay = jugador.gety();
30 jugador.teclado();
31
32 // comprobar si colisiona con el mapa
33 bool choca = false;
34 int px = jugador.getx()-160;
35 int py = jugador.gety()-160+16;
36 for ( int ci=0; ci < 32; ci++)
37 {
38 for (int cj=0; cj < 16; cj++)
39 {
40
41 if ( getpixel( choque, px+ci, py+cj) == 0xff0000 ){
42 choca = true;
43 ci = 32;
44 cj = 16;
45 }
46 if ( getpixel( choque, px+ci, py+cj) == 0x00ff00 ) salir = true;
47 }
48 }
49 if ( choca ){
50 // vuelve al estado anterior
51 jugador.posiciona( ax,ay );
52 }
53
54}
55
56
57// Se encarga de pintar todo sobre el buffer
58void pinta_juego()
59{
60 blit( fondo, buffer, 0,0, 160, 160, 480,325);
61 jugador.pinta();
62 masked_blit( alto, buffer, 0,0, 160, 160, 480,325);
63}

Paso a Paso
Para el control de los FPS se ha creado una funcin llamada inc_contador_tiempo_juego, esta funcin solo se encarga de incrementar
el valor de la variable contador_tiempo_juego un determinado numero de veces por segundo.
?
1<allegro .h="">install_int_ex(inc_contador_tiempo_juego, BPS_TO_TIMER( FRAME_RATE ));</allegro>

Esta es la funcin que se encarga de que se llame segn la variable FRAME_RATE que en el ejemplo tiene un valor de 30.

El bucle principal tiene una primera condicin que se cumplir siempre que contador_tiempo_juego tenga un valor distinto a 0.
Dentro de esta condicin 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 teora, deber pintarlo 30, ya que mientras que el
valor de la variable sea 0 no har nada.

En player.h se han aadido algunas nuevas funciones que hacia falta, como son las que permite obtener la posicin actual getx(),
gety() y la funcin que sita al personaje en una posicin 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 colisin 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 funcin utilizada para controlar la colisin 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 algn pixel de color rojo, en este caso existe colisin y por tanto vuelve a su posicin anterior. Y en el caso de que
detecte algn pixel de color verde este provocar la finalizacin del programa.

?
1 bool choca = false;
2 int px = jugador.getx()-160;
3 int py = jugador.gety()-160+16;
4 for ( int ci=0; ci < 32; ci++)
5 {
6 for (int cj=0; cj < 16; cj++)
7 {
8
9 if ( getpixel( choque, px+ci, py+cj) == 0xff0000 ){
10 choca = true;
11 ci = 32;
12 cj = 16;
13 }
14 if ( getpixel( choque, px+ci, py+cj) == 0x00ff00 ) salir = true;
15 }
16}

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 posicin del
jugador dentro de la imagen choque, como se ha mostrado la imagen choque en la posicin (160,160), por eso se le resta 160 a la
posicin 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.

En la funcin 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++

Crear juego RPG en C++ y Allegro 4 (5) Escenarios


Continuamos con el curso de crea tu juego RPG en C++ y Allegro. En esta entrega se har que nuestro personaje pueda salir de la casa
al exterior, y recorrer el bosque.

Si quieres ver alguna de las anteriores entregas entra en el Contenido del Blog.

Recuerda, si tienes algn problema con los ejemplos de la pagina, o alguna duda. Puedes plantear tu pregunta en el foro de
programacin:

http://creatusjuegosdecero.webege.com/index.php

Para este curso necesitaremos tres imgenes del nuevo lugar, es decir, del bosque.
Uno con la imagen completa del nuevo escenario, una segunda imagen con las zonas de choque, y una tercera imagen con las cosas por
las que el protagonista pasar por debajo, como son las copas de los arboles. Es importante que las tres imgenes tengan el mismo
tamao.

El mapa de choque se utilizar para indicar con el color rojo las zonas que no podemos pisar, y por tanto chocaremos, y tambin con
otros colores indicaremos otras acciones. En el ejemplo se utilizar el color azul para detectar la puerta de la casa.
Programacin

?
1 /*
2 mijuego.h
3
4 */
5
6 BITMAP *casa_a;
7 BITMAP *casa_b;
8 BITMAP *casa_c;
9 BITMAP *bosq_a;
10 BITMAP *bosq_b;
11 BITMAP *bosq_c;
12
13
14 BITMAP *fondo;
15 BITMAP *choque;
16 BITMAP *cielo;
17
18 player jugador;
19
20 // indicar en que lugar estamos
21 // 1: casa
22 // 2: bosque
23 int lugar;
24
25
26 // carga todo lo necesario antes de empezar el juego
27 void carga_juego()
28 {
29 jugador.inicia();
30
31 casa_a = load_bmp("casa.bmp",NULL);
32 casa_b = load_bmp("casa-sup.bmp",NULL);
33 casa_c = load_bmp("casa-choque.bmp",NULL);
34
35 bosq_a = load_bmp("bosque.bmp",NULL);
36 bosq_b = load_bmp("bosque-sup.bmp",NULL);
37 bosq_c = load_bmp("bosque-choque.bmp",NULL);
38
39 // cargamos imagenes del primer escenario
40 fondo = casa_a;
41 choque = casa_c;
42 cielo = casa_b;
43
44 lugar = 1;
45 }
46
47
48 // actualiza el estado del juego
49 void actualiza_juego()
50 {
51 int cambio = 0;
52 int ax,ay;
53 ax = jugador.getx();
54 ay = jugador.gety();
55 jugador.teclado();
56
57 // comprobar si colisiona con el mapa
58 bool choca = false;
59 int px = jugador.getx();
60 int py = jugador.gety()+16;
61
62 if ( lugar == 1)
63 {
64 px = jugador.getx()-160;
65 py = jugador.gety()-160+16;
66 }
67 if (lugar == 2)
68 {
69 py=py+160;
70 }
71
72 for ( int ci=2; ci < 30; ci++)
73 {
74 for (int cj=0; cj < 16; cj++)
75 {
76
77 // color rojo
78 if ( getpixel( choque, px+ci, py+cj) == 0xff0000 ){
79 choca = true;
80 }
81 // color verde
82 if ( getpixel( choque, px+ci, py+cj) == 0x00ff00 ) cambio = 1;
83 // color azul
84 if ( getpixel( choque, px+ci, py+cj) == 0x0000ff ) cambio = 2;
85 // color amarillo
86 if ( getpixel( choque, px+ci, py+cj) == 0xffff00 ) salir = true;
87 }
88 }
89 if ( choca ){
90 // vuelve al estado anterior
91 jugador.posiciona( ax,ay );
92 }
93
94 switch ( lugar )
95 {
96 case 1: // casa
97 if ( cambio == 1 )
98 {
99 // cambiamos a otro lugar
100 lugar = 2;
101 fondo = bosq_a;
102 choque = bosq_c;
103 cielo = bosq_b;
104 jugador.posiciona( 410,370 );
105 }
106 break;
107 case 2: // bosque
108 if ( cambio == 2 )
109 {
110 // cambiamos a otro lugar
111 lugar = 1;
112 fondo = casa_a;
113 choque = casa_c;
114 cielo = casa_b;
115 // situamos al prota dentro de la casa
116 jugador.posiciona( 290,440 );
117 }
118 break;
119 default:
120 break;
121 }
122
123}
124
125
126
127// Se encarga de pintar todo sobre el buffer
128void pinta_juego()
129{
130 int ancho, alto;
131 int ax=0;
132 int ay=0;
133 int bx=0;
134 int by=0;
135
136 switch ( lugar )
137 {
138 case 1: // casa
139 bx=160;
140 by=160;
141 ancho = 480;
142 alto = 325;
143 break;
144 case 2: // bosque
145 ay=160;
146 ancho = PANTALLA_ANCHO;
147 alto = PANTALLA_ALTO;
148 break;
149 default:
150 break;
151 }
152
153 blit( fondo, buffer, ax, ay, bx, by, ancho, alto);
154 jugador.pinta();
155 masked_blit( cielo, buffer, ax, ay, bx, by, ancho, alto);
156}

Todas las modificaciones se han realizado solo en el archivo mijuego.h, por tanto todos los dems archivos se dejan tal cual, solo se
debe de sustituir este nuevo cdigo y aadir las nuevas imgenes.

Paso a Paso
Se han declarado nuevas variables del tipo BITMAP, que son las que contendr las imagenes de los escenarios. Se crea el concepto de
fases, controlado con la variable lugar, que contendr en que fase o lugar esta:

1 En la casa
2 En el bosque

En la funcin carga_juego(), cargamos todas las imgenes que se utilizarn en el programa. Las variables fondo, choque, y cielo,
sern variables generales que indicaran el lugar actual en el que estamos, por tanto, siempre que se cambie de lugar (fase) se debe de
actualizar estas variables con las nuevas imgenes del lugar.

?
1 for ( int ci=2; ci < 30; ci++)
2 {
3 for (int cj=0; cj < 16; cj++)
4 {
5
6 // color rojo
7 if ( getpixel( choque, px+ci, py+cj) == 0xff0000 ){
8 choca = true;
9 }
10 // color verde
11 if ( getpixel( choque, px+ci, py+cj) == 0x00ff00 ) cambio = 1;
12 // color azul
13 if ( getpixel( choque, px+ci, py+cj) == 0x0000ff ) cambio = 2;
14 // color amarillo
15 if ( getpixel( choque, px+ci, py+cj) == 0xffff00 ) salir = true;
16 }
17}

En la funcin de colisin se han aadido algunas condiciones, segn colores utilizados. Para el ejemplo utilizamos el rojo (0xff0000)
para bloquear el paso del personaje, el verde (0x00ff00) para salir de la casa, el azul (0x0000ff) para volver al interior de la casa. Se
han cambiado los valores del primer bucle for, ahora empieza en el 2 para acabar en el 29. Esto se ha realizado para reducir el tamao
de bloqueo del personaje, por tanto ahora el tamao de choque es de 28x16, tal y como se muestra en la siguiente imagen el recuadro
pintado de verde es lo que realmente se esta comprobando si choca.

En la funcin pinta_juego() siempre se pinta el fondo y el cielo. Lo nico que va variando son los parmetros de muestra, que
cambian segn el escenario o lugar, para ello se crean las variables ax,ay, bx,by, ancho y alto. Estos datos cambian segn las
propiedades de las imgenes utilizadas para los escenarios.
Aqui teneis las Imagenes bosque comprimida en RAR.
Y aqui tienen el archivo mijuego.h comprimido en RAR.

Crear juego RPG en C++ y Allegro 4 (6) - Scroll


Continuamos con el curso de crea tu juego RPG en C++ y Allegro. En esta entrega se har que nuestro personaje pueda recorrer el
extenso bosque, mediante la ayuda del scroll.

NOTA IMPORTANTE:
Para realizar este curso es necesario tener hecho los anteriores, ya que solo se hace referencia a cambios en el cdigo.

Que es un scroll ?
Se denomina scroll, desplazamiento, al movimiento en 2D de los contenidos que conforman el escenario de un videojuego.

En el ejemplo anterior solo llegaba a mostrarse un trozo del mapa bosque, ya que la imagen es superior a la resolucin utilizada en el
programa ( 800x600 ). Por ello, se crea un scroll para que cuando el personaje se acerque a uno de los bordes de la pantalla, se
desplace el escenario mostrando el resto del mapa.
Programacin
Dentro de mijuego.h se define una variable booleana para indicar si el mapa tiene o no desplazamiento, esta variable la llamamos
desplaza, si es TRUE es que tiene desplazamiento. Tambin se definen las variables enteras desplazamiento_map_x y
desplazamiento_map_y, estas variables contendr el valor del desplazamiento del scroll segun su eje x, y. Estas variables se definen
de forma global para que puedan ser utilizadas en todas las funciones.

En la funcion de carga_juego(), se inicializan las variables:

desplazamiento_map_x=0;
desplazamiento_map_y=0;
desplaza = false;

Se les da un valor de 0 y false, ya que inicialmente partimos de que el primer mapa no tendr scroll.

En la funcin actualiza_juego(), se controla el desplazamiento del scroll mediante el siguiente codigo:

if ( desplaza )
{
int d = desplazamiento / 2;
// controla el desplazamiento del mapa si esta en los bordes
if ( ax < 160 && desplazamiento_map_x > 0 )
{
desplazamiento_map_x-=d;
jugador.posiciona(ax+d,ay);
ax = jugador.getx();
ay = jugador.gety();
if ( ax < 60 && desplazamiento_map_x > 0 )
{
desplazamiento_map_x-=d;
jugador.posiciona(ax+d,ay);
}
}
if ( ay < 160 && desplazamiento_map_y > 0 )
{
desplazamiento_map_y-=d;
jugador.posiciona(ax,ay+d);
ax = jugador.getx();
ay = jugador.gety();
if ( ay < 60 && desplazamiento_map_y > 0 )
{
desplazamiento_map_y-=d;
jugador.posiciona(ax,ay+d);
}
}
if ( ax > PANTALLA_ANCHO-160 && desplazamiento_map_x < fondo->w-PANTALLA_ANCHO )
{
desplazamiento_map_x+=d;
jugador.posiciona(ax-d,ay);
ax = jugador.getx();
ay = jugador.gety();
if ( ax > PANTALLA_ANCHO-60 && desplazamiento_map_x < fondo->w-PANTALLA_ANCHO )
{
desplazamiento_map_x+=d;
jugador.posiciona(ax-d,ay);
}
}
if ( ay > PANTALLA_ALTO-160 && desplazamiento_map_y < fondo->h-PANTALLA_ALTO )
{
desplazamiento_map_y+=d;
jugador.posiciona(ax,ay-d);
ax = jugador.getx();
ay = jugador.gety();
if ( ay > PANTALLA_ALTO-60 && desplazamiento_map_y < fondo->h-PANTALLA_ALTO )
{
desplazamiento_map_y+=d;
jugador.posiciona(ax,ay-d);
}
}
ax = jugador.getx();
ay = jugador.gety();
}

En lineas generales lo que hace el cdigo es que cuando el jugador se acerca a uno de los bordes, ya sea por arriba, abajo, izquierda o
derecha, cuando esta a menos de 160 pixel se realiza un pequeo desplazamiento del escenario, en el caso de que se siga acercando al
borde y este de menos de 60 pixel, se realiza otro desplazamiento para evitar que el jugador quede fuera de pantalla. Este cdigo debe
de ir justo antes de la llamada del jugador.teclado().

Tambien se debe de cambiar la siguiente condicin al completo.

if (lugar == 2)
{
px = px + desplazamiento_map_x;
py = py + desplazamiento_map_y;
}

Para que actualice la posicin del protagonista cuando haya un desplazamiento.

switch ( lugar )
{
case 1: // casa
if ( cambio == 1 )
{
// cambiamos a otro lugar
lugar = 2;
fondo = bosq_a;
choque = bosq_c;
cielo = bosq_b;
jugador.posiciona( 410,370 );
desplazamiento_map_x=0;
desplazamiento_map_y=160;
desplaza=true;
}
break;
case 2: // bosque
if ( cambio == 2 )
{
// cambiamos a otro lugar
lugar = 1;
fondo = casa_a;
choque = casa_c;
cielo = casa_b;
// situamos al prota dentro de la casa
jugador.posiciona( 290,440 );
desplazamiento_map_x=0;
desplazamiento_map_y=0;
desplaza=false;
}
break;
default:
break;
}

Se aade en cada una de las fases el valor correspondiente, segn tenga o no scroll el prximo mapa que va a cargar.

Y finalmente se aade el desplazamiento del scroll a la hora de pintar los escenarios en la funcin pinta_juego()

void pinta_juego()
{
int ancho, alto;
int ax=0;
int ay=0;
int bx=0;
int by=0;

switch ( lugar )
{
case 1: // casa
bx=160;
by=160;
ancho = 480;
alto = 325;
break;
case 2: // bosque
ax = desplazamiento_map_x;
ay = desplazamiento_map_y;
ancho = PANTALLA_ANCHO;
alto = PANTALLA_ALTO;
break;
default:
break;
}

blit( fondo, buffer, ax, ay, bx, by, ancho, alto);


jugador.pinta();
masked_blit( cielo, buffer, ax, ay, bx, by, ancho, alto);
}

Y llegados a este punto ya se tendr el escenario del juego con scroll.

Si quieres ver alguna de las anteriores entregas entra en el Contenido del Blog.

Recuerda, si tienes algn problema con los ejemplos de la pagina, o alguna duda. Puedes plantear tu pregunta en el foro de programacin:

http://creatusjuegosdecero.webege.com/index.php
Crear juego RPG en C++ y Allegro 4 (7) Efectos sonoros

Continuamos con el curso de crea tu juego RPG en C++ y Allegro. En esta entrega se aadir algunos efectos sonoros.

NOTA IMPORTANTE:

Para realizar este curso es necesario tener hecho los anteriores, ya que solo se hace referencia a cambios en el cdigo.

Aadiendo Sonidos
Para este curso necesitaremos algunos recursos, concretamente los archivos de audio de los efectos que queramos aadir. Para ello se han buscado
algunas pginas gratuitas, donde se puede descargar el efecto deseado.
1. Pagina web de sonidos gratuitos
2. Pgina web de sonidos gratuitos

Es importante recordar que el Allegro solo reconoce el formato WAV sin compresin, as que se debe de asegurar que todos los archivos de audio
estn en este formato.

En este ejemplo aadiremos tres sonidos:

pasos.wav: el personaje har un ruido al caminar


abrir_puerta.wav: al cambiar de escenario pasando por la puerta, suena el sonido de abrir una puerta.
bosque.wav: es un sonido ambiente del bosque donde se oyen animales, que se repetir de forma cclica.

Programacin
Para poder utilizar sonidos en Allegro se debe de inicializar, y esto se aade a la funcin inicia_allegro() , justamente despues de ajustar la
resolucin de la pantalla.

1// incializa el audio en allegro

2if (install_sound(DIGI_AUTODETECT, MIDI_AUTODETECT, NULL) != 0) {

3 allegro_message("Error: inicializando sistema de sonido\n%s\n", allegro_error);

4}

6// inicializa todo lo referente al sonido


7inicia_sonido();

Todo lo dems referente al sonido se va a poner aparte en un archivo llamado audio.h, que se encargar de todo el audio de nuestro juego.

1 // audio.h

3 SAMPLE *spasos;

4 SAMPLE *spuerta1;

5 SAMPLE *sbosque;

7 // funcion que carga todos los ficheros de audio

8 void inicia_sonido(){

9 set_volume(230, 90);

10

11 spasos = load_wav("pasos.wav");

12 spuerta1 = load_wav("abrir_puerta.wav");

13 sbosque = load_wav("bosque.wav");
14}

15

16void sonido_pasos(){

17 play_sample ( spasos, 100,128, 3300, 0 );

18}

19

20void sonido_abrirpuerta(){

21 play_sample ( spuerta1, 100,128, 1300, 0 );

22}

23

24void sonido_ambiente(){

25 play_sample ( sbosque, 80,128, 900, 1 );

26}

27

28void para_sonido_ambiente(){

29 stop_sample( sbosque );

30}

El comando play_sample requiere cinco parmetros:

1. El SAMPLE que se quiere reproducir.


2. Indica el volumen, con un rango de 0 a 255 (mximo volumen).
3. Indica si se quiere que se escuche por la izquierda 0 o la derecha 255.
4. La frecuencia es relativa: 1000 representa la frecuencia a la que el sample fue grabado, 2000 es el doble, etc.
5. Repeticin, si esta a 1 indica que se repetir.

Ahora ya solo queda hacer las llamadas a las funciones de sonido, en el momento justo de cada una de las acciones a las que se quieren poner
sonido.

En el archivo players.h, se coloca la llamada al sonido_pasos. Dentro de la funcin teclado, justo cuando se comprueba que se ha pulsado alguna
de las teclas de direccin, dentro de la siguiente condicin tal y como se muestra a continuacin:

1if ( ax != x || ay != y )

2{

3 sonido_pasos();

4 // entra si a cambiado alguna de las variables x,y

5 animacion++;

6 if ( animacion > 2 ) animacion = 0;

7}
Dentro del archivo mijuego.h, se aadirn los sonidos de la puerta (sonido_abrirpuerta) y ambiente (sonido_ambiente), justo cuando se cambia de
escenario, en la funcin actualiza_juego(), despus de comprobar si existe colisin.

1 switch ( lugar )

2 {

3 case 1: // casa

4 if ( cambio == 1 )

5 {

6 // cambiamos a otro lugar

7 lugar = 2;

8 fondo = bosq_a;

9 choque = bosq_c;

10 cielo = bosq_b;

11 jugador.posiciona( 410,370 );

12 desplazamiento_map_x=0;

13 desplazamiento_map_y=160;

14 desplaza=true;

15 sonido_abrirpuerta();
16 sonido_ambiente();

17 }

18 break;

19case 2: // bosque

20 if ( cambio == 2 )

21 {

22 // cambiamos a otro lugar

23 lugar = 1;

24 fondo = casa_a;

25 choque = casa_c;

26 cielo = casa_b;

27 // situamos al prota dentro de la casa

28 jugador.posiciona( 290,440 );

29 desplazamiento_map_x=0;

30 desplazamiento_map_y=0;

31 desplaza=false;

32 sonido_abrirpuerta();

33 para_sonido_ambiente();

34 }
35 break;

36default:

37 break;

38}

Haz clic aqui para descargar los archivos wav comprimidos en RAR

Si quieres ver alguna de las anteriores entregas entra en el Contenido del Blog.

Recuerda, si tienes algn problema con los ejemplos de la pagina, o alguna duda. Puedes plantear tu pregunta en el foro de programacin:

http://creatusjuegosdecero.webege.com/index.php
Crear juego RPG en C++ y Allegro 4 (8) Msica

Continuamos con el curso de crea tu juego RPG en C++ y Allegro. En esta entrega se aadir msica de fondo, una diferente para cada
escenario, tal y como se muestra en el video.

NOTA IMPORTANTE:
Para realizar este curso es necesario tener hecho los anteriores, ya que solo se hace referencia a cambios en el cdigo.

Para este ejemplo se necesita dos archivos MIDI, para poder poner una msica a cada escenario. Los archivos MIDI se llamaran:

musica1: msica de fondo que se escuchara cuando este el personaje dentro de la casa.
musica2: msica de fondo que se escuchara cuando este el personaje en el bosque.

Los archivos utilizados para este curso, puedes descargarlo haciendo clic aqui.

Programacin
Siguiendo con el curso anterior, todo lo referente al sonido se incluye dentro de audio.h. Se declaran dos variables del tipo MIDI,
llamadas musica1, musica2. Dentro de la funcin inicia_sonido se inicializan estas dos variables de la siguiente forma:

?
1musica1 = load_midi("musica1.mid");
2musica2 = load_midi("musica2.mid");
Y se aaden dos funciones nuevas para reproducir la msica deseada.

?
1void musica_casa(){
2 play_midi(musica1,1);
3}
4
5void musica_bosque(){
6 play_midi(musica2,1);
7}

El segundo parmetro de la funcin play_midi, indica si se quiere que la msica sea repetitiva, como si se quiere que se repita se pone
un 1.
En la funcion carga_juego() de mijuego.h se aade una llamada a musica_casa, ya que el programa se inicia en ese escenario. Las
prximas llamadas a cambiar la musica de fondo se realizan en la funcion actualiza_juego(), donde se controla que se cambia de
escenario.

?
1 switch ( lugar )
2 {
3 case 1: // casa
4 if ( cambio == 1 )
5 {
6 // cambiamos a otro lugar
7 lugar = 2;
8 fondo = bosq_a;
9 choque = bosq_c;
10 cielo = bosq_b;
11 jugador.posiciona( 410,370 );
12 desplazamiento_map_x=0;
13 desplazamiento_map_y=160;
14 desplaza=true;
15 sonido_abrirpuerta();
16 sonido_ambiente();
17 musica_bosque();
18 }
19 break;
20case 2: // bosque
21 if ( cambio == 2 )
22 {
23 // cambiamos a otro lugar
24 lugar = 1;
25 fondo = casa_a;
26 choque = casa_c;
27 cielo = casa_b;
28 // situamos al prota dentro de la casa
29 jugador.posiciona( 290,440 );
30 desplazamiento_map_x=0;
31 desplazamiento_map_y=0;
32 desplaza=false;
33 sonido_abrirpuerta();
34 para_sonido_ambiente();
35 musica_casa();
36 }
37 break;

Y aqu acaba esta entrega.

Si quieres ver alguna de las anteriores entregas entra en el Contenido del Blog.

Recuerda, si tienes algn problema con los ejemplos de la pagina, o alguna duda. Puedes plantear tu pregunta en el foro de programacin:

http://creatusjuegosdecero.webege.com/index.php
Crear juego RPG en C++ y Allegro 4 (9) DAT

Continuamos con el curso de crea tu juego RPG en C++ y Allegro. En esta entrega se utilizar la aplicacin grabber con la idea de
unificar todos los archivos utilizados en uno solo.

NOTA IMPORTANTE:
Para realizar este curso es necesario tener hecho los anteriores, ya que solo se hace referencia a cambios en el cdigo.

GRABBER ficheros DAT


Si nunca ha utilizado esta utilidad, le aconsejo que se miren esta pgina, ya que aqu se centra mas en la programacin que en el
manejo del grabber y la creacin del archivo.

Se crea un archivo .DAT mediante la utilidad grabber. Este fichero contendr todas las imgenes, sonidos, y msicas que se han
utilizado en todo el proyecto, de esta forma se facilita el traslado del proyecto, sin que se pierda ningn archivo, y permite proteger el
contenido.
A cada uno de los archivos que se van poniendo se le ha puesto como nombre, el nombre que tenia el objeto y una letra d delante para
indicar que es un objeto del datafile, y seguido de una "i" si se trata de una imagen, una "s" si se trata de un sonido, o una "m" si se
trata de una msica.
Ejemplo: La imagen del prota llamada personaje.bmp se ha renombrado dentro del datafile como dipersonaje. La imagen casa-
sup.bmp se ha llamado como dicasasup. El sonido del bosque.wav queda dsbosque. Y la musica1.mid queda como dmmusica1.

El fichero datafile creado tiene una contrasea que es "cursoRPGkodaygames", y lleva una compresin global para reducir el tamao
de los archivos. El archivo queda como se muestra la imagen.
Programacin
En el archivo global.h definimos dos variables, una que contiene la contrasea del fichero DAT y la otra datosjuegos que es del tipo
DATAFILE.

// clave del fichero datafile


char evalc[19]="cursoRPGkodaygames";

DATAFILE *datosjuego;

En el archivo main.cpp, en la funcin inicia_allegro() se quita la llamada a la funcin inicia_sonido(), y se sustituye


por set_volume(230, 90); ya que no es necesaria la funcin inicia_sonido(). Tambien hay que aadir un nuevo #include para aadir el
archivo datosjuego.h que se genera a partir del fichero DAT.

#include <allegro.h>
#include "global.h"
#include "datosjuego.h"
#include "audio.h"
#include "players.h"
#include "mijuego.h"

En el archivo players.h, solo hay que cambiar una linea que es la que se encarga de cargar la imagen del personaje, debe sustituirse
por:

prota = (BITMAP *)datosjuego[dipersonaje].dat;

Este cambio se debe a que ya no se utiliza el archivo BMP directamente, sino que se extrae del archivo datosjuego.dat que lo contiene
todo.

En el archivo audio.h, se elimina todas las variables declaradas de forma global, y tambin se borra la funcin inicia_sonido(), ya que
todos estos datos que anteriormente se cargaba ahora lo hace cuando se carga el fichero DAT. El acceso a los sonidos y la msica
cambia quedando de la siguiente forma el archivo audio.h

// audio.h

void sonido_pasos(){
play_sample ( (SAMPLE *)datosjuego[dspasos].dat, 160,128, 3300, 0 );
}

void sonido_abrirpuerta(){
play_sample ( (SAMPLE *)datosjuego[dsabrirpuerta].dat, 100,128, 1300, 0 );
}

void sonido_ambiente(){
play_sample ( (SAMPLE *)datosjuego[dsbosque].dat, 80,128, 900, 1 );
}

void para_sonido_ambiente(){
stop_sample( (SAMPLE *)datosjuego[dsbosque].dat );
}

void musica_casa(){
play_midi((MIDI *)datosjuego[dmmusica1].dat,1);
}

void musica_bosque(){
play_midi((MIDI *)datosjuego[dmmusica2].dat,1);
}

En el archivo mijuego.h, se elimina las siguientes variables.

BITMAP *casa_a;
BITMAP *casa_b;
BITMAP *casa_c;
BITMAP *bosq_a;
BITMAP *bosq_b;
BITMAP *bosq_c;

Todas estas variables tampoco hacen falta, pues se utiliza directamente las del fichero DAT.

Por tanto en la funcin carga_juego(), se debe de eliminar tambin la inicializacin de las variables anteriores quedando la funcin de
la siguiente forma:

// carga todo lo necesario antes de empezar el juego


void carga_juego()
{

packfile_password(evalc);
datosjuego = load_datafile("datosjuego.dat");
if ( !datosjuego ){
allegro_message("Error: archivo datosjuego.dat no encontrado\n%s\n", allegro_error);
}
jugador.inicia();

fondo = (BITMAP *)datosjuego[dicasa].dat;


choque = (BITMAP *)datosjuego[dicasachoque].dat;
cielo = (BITMAP *)datosjuego[dicasasup].dat;

lugar = 1;

desplazamiento_map_x=0;
desplazamiento_map_y=0;
desplaza = false;

musica_casa();
}

En esta funcin se aade las primeras lineas para la carga del fichero DAT, la primera linea especificamos la contrasea del archivo.
En la segunda linea carga el archivo datosjuego.dat. Y en la siguiente condicin se comprueba de que no haya un error en la lectura
del archivo.

En la funcin actualiza_juego(), cuando se comprueba la variable lugar para saber si se cambia de escenario, se cambia las variables
fondo, choque y cielo.

switch ( lugar )
{
case 1: // casa
if ( cambio == 1 )
{
// cambiamos a otro lugar
lugar = 2;
fondo = (BITMAP *)datosjuego[dibosque].dat;
choque = (BITMAP *)datosjuego[dibosquechoque].dat;
cielo = (BITMAP *)datosjuego[dibosquesup].dat;
jugador.posiciona( 410,370 );
desplazamiento_map_x=0;
desplazamiento_map_y=160;
desplaza=true;
sonido_abrirpuerta();
sonido_ambiente();
musica_bosque();
}
break;
case 2: // bosque
if ( cambio == 2 )
{
// cambiamos a otro lugar
lugar = 1;
fondo = (BITMAP *)datosjuego[dicasa].dat;
choque = (BITMAP *)datosjuego[dicasachoque].dat;
cielo = (BITMAP *)datosjuego[dicasasup].dat;
// situamos al prota dentro de la casa
jugador.posiciona( 290,440 );
desplazamiento_map_x=0;
desplazamiento_map_y=0;
desplaza=false;
sonido_abrirpuerta();
para_sonido_ambiente();
musica_casa();
}
break;
default:
break;
}

Y aqu acaba todos los cambios que se deben hacer para poder utilizar el fichero DAT. Para evitar algunos problemas con los archivos,
les dejo el fichero DAT comprimido en RAR para descargar.

Si quieres ver alguna de las anteriores entregas entra en el Contenido del Blog.

Recuerda, si tienes algn problema con los ejemplos de la pagina, o alguna duda. Puedes plantear tu pregunta en el foro de programacin:
http://creatusjuegosdecero.webege.com/index.php

Crear juego RPG en C++ y Allegro 4 (10)

Continuamos con el curso de crea tu juego RPG en C++ y Allegro. En esta entrega se utilizar todo lo anterior para crear un nuevo
escenario.

En este curso, se repasar todos los puntos para crear un escenario nuevo para el juego.

Para crear un nuevo escenario en el juego, se necesita crear una imagen del escenario en cuestin. Mientras se esta creando se
recomienda hacerlo en un programa que permita capas, y hacer un mnimo de tres capas.
1. La parte inferior o suelo, en esta capa se pintar todo lo que va en el suelo por donde el protagonista podr moverse.
2. La capa media o choque, en esta capa se pintar todo aquello que pueda bloquear el paso al personaje, ya sea un edificio, como
un rbol.
3. La capa alta o superior, en esta capa debe ir todo lo que se quiera que se pinte por encima del personaje, es decir, que lo van a
tapar, como la copa de los arboles o los tejados de los edificios.

Todo esto se divide de esta manera para facilitar a la hora de hacer las tres capas que se necesitan para crear el escenario en cuestin.
Para ellos se necesita una capa base, que lo contiene todo. Una segunda capa superior, en la que solo se pintar las cosas de la capa
alta, y el resto se pintar del color rosa transparente. Y una tercera capa en la que se pintar las zonas bloqueadas con el color rojo, esta
capa se har a partir de la capa media.

Se recomienda tener bien claro lo que se desea hacer, ya que es muy laborioso hacer los escenarios como para que luego no guste o no
sirvan.

En el tutorial 5, se explica la funcin de colisin, en ella aparece el control de cuatro colores; rojo, verde, azul y amarillo. Si desea
aadir algn otro color es tan simple como aadir otra condicin e indicar el color y el valor de la variable cambio, de esta forma se
esta creando otro color con el que colisionar y realizar alguna accin.
Supongamos que se quiere aadir otro color para el control de acceso a un edificio.

?
1// color blanco
2if ( getpixel( choque, px+ci, py+cj) == 0xffffff ) cambio = 4;
El color blanco viene definido en hexadecimal 0xffffff pero se puede poner tambin en decimal si se desea. Si se cumple la condicin
la variable cambio vale 4, por tanto si cambio es 4, quiere decir que se ha pisado el color blanco. De esta manera en cualquiera de los
escenario se puede aadir alguna nueva funcin en el caso de pisar el color blanco.

Se puede aadir tantos colores como se desee, pero cuantos mas se aadan mas lento ser el proceso de colisin.

En cada escenario, es necesario definir que hacer en caso de colisionar con alguno de los colores, por tanto cuando la variable cambio
tiene algn valor indica que se ha colisionado con uno de los colores.

Por ejemplo, en el mapa bosque se ha aadido la opcin para poder pasar al nuevo escenario de la ciudad.

?
1 case 2: // bosque
2 if ( cambio == 2 )
3 {
4 // cambiamos a otro lugar
5 // casa
6 lugar = 1;
7 fondo = (BITMAP *)datosjuego[dicasa].dat;
8 choque = (BITMAP *)datosjuego[dicasachoque].dat;
9 cielo = (BITMAP *)datosjuego[dicasasup].dat;
10
11 // situamos al prota dentro de la casa
12 jugador.posiciona( 290,440 );
13 desplazamiento_map_x=0;
14 desplazamiento_map_y=0;
15 desplaza=false;
16 sonido_abrirpuerta();
17 para_sonido_ambiente();
18 musica_casa();
19 }
20 if ( cambio == 3 )
21 {
22 // cambiamos a otro lugar
23 // ciudad
24 lugar = 3;
25 fondo = (BITMAP *)datosjuego[dicity1].dat;
26 choque = (BITMAP *)datosjuego[dicity1choque].dat;
27 cielo = (BITMAP *)datosjuego[dicity1sup].dat;
28
29 // situamos al prota
30 jugador.posiciona( 500,540 );
31 desplazamiento_map_x=950;
32 desplazamiento_map_y=510;
33 desplaza=true;
34 para_sonido_ambiente();
35 musica_ciudad1();
36 }
37 break;

Tal y como muestra en el cdigo, cuando se esta en el mapa bosque si se colisiona con el color amarillo carga los datos de la ciudad,
sita al personaje en las coordenadas (500,540) de la pantalla, sita el mapa con un desplazamiento de (950,510), ya que el mapa es
mucho mas grande que la resolucin de la ventana. Se indica que el escenario tiene scroll, se detiene el sonido ambiente y se inicial la
nueva msica para la ciudad.

A la hora de pintar tambien hay que describir el escenario, quedando de la siguiente forma la funcin pinta_juego().

?
1 // Se encarga de pintar todo sobre el buffer
2 void pinta_juego()
3 {
4 int ancho, alto;
5 int ax=0;
6 int ay=0;
7 int bx=0;
8 int by=0;
9
10 switch ( lugar )
11 {
12 case 1: // casa
13 bx=160;
14 by=160;
15 ancho = 480;
16 alto = 325;
17 break;
18 case 2: // bosque
19 ax = desplazamiento_map_x;
20 ay = desplazamiento_map_y;
21 ancho = PANTALLA_ANCHO;
22 alto = PANTALLA_ALTO;
23 break;
24 case 3: // ciudad1
25 ax = desplazamiento_map_x;
26 ay = desplazamiento_map_y;
27 ancho = PANTALLA_ANCHO;
28 alto = PANTALLA_ALTO;
29 break;
30 default:
31 break;
32 }
33
34 blit( fondo, buffer, ax, ay, bx, by, ancho, alto);
35 jugador.pinta();
36 masked_blit( cielo, buffer, ax, ay, bx, by, ancho, alto);
37}
?
1
En este caso, tal y como se muestra ya sea el bosque o la ciudad, ambos tienen los mismos datos, quizs ms adelante cuando se tengan
mas escenario nos daremos cuenta de que se repite siempre lo mismo, que solo es diferente para los mapas que son mas pequeo que la
resolucin utilizada para el proyecto del juego (800,600), por tanto se podr simplificar, pero eso lo dejamos para otro momento.

Recordad, que cada escenario puede tener una msica y un sonido ambiente, tal y como lo tiene el escenario del bosque. Espero que
haya quedado ms claro de como se hacen los escenarios por si alguno se anima a hacer alguno mas.

Aqu tenis el mapa del nuevo escenario, la ciudad.


Llegados a este punto aqu os dejo todos los cdigos que hay hecho hasta el momento, con esto el juego tiene tres escenarios: la casa,
el bosque y la ciudad. En el archivo RAR viene comprimido el proyecto del DEV-C++ y todos los cdigos, que con el archivo DAT
tendr para poder ejecutarlo.

Si quieres ver alguna de las anteriores entregas entra en el Contenido del Blog.

Recuerda, si tienes algn problema con los ejemplos de la pagina, o alguna duda. Puedes plantear tu pregunta en el foro de
programacin:

http://creatusjuegosdecero.webege.com/index.php
Crear juego RPG en C++ y Allegro 4 (11) NPC

Continuamos con el curso de crea tu juego RPG en C++ y Allegro. Hasta el momento se tiene hecho que el jugador pueda explorar por
tres escenarios, aunque algunos de ellos son grandes estan algo vacios, me refiero a que no hay nadie, para arreglar esto en este curso
se va a mostrar como hacer un NPC.

Que son los NPC ?


En un juego un NPC es un personaje no jugador ( Non-Player Character )
Estos NPC, pueden servir para simplemente decorar los escenarios, o para que nos den pistas o misiones.

Requisitos para el curso


Para realizar el curso debe tener el codigo fuente de todo lo anterior y el fichero DAT que contiene todo el material multimedia (
imagenes, sonidos, etc ). Para asegurar que se parte con el mismo material se deja los siguientes archivos:

Archivo RAR con el Codigo Fuente del proyecto 5,78 KB descargar

Archivo RAR del Fichero DAT 14,10 MB descargar

Objetivo del curso


Se desea crear una clase que sirva para poner un NPC por pantalla. Inicialmente un NPC simple que no haga nada, donde le indicamos
la posicin en el mapa donde se quiere que aparezca y la direccin hacia donde mira este NPC.

Programacin
Para este curso aadimos un nuevo archivo, que llamamos npc.h, que contendr el siguiente cdigo.

?
1 // npc.h
2

3 /*

4 la posicion x,y es una posicion directa al mapa, por tanto debe pintarse

5 directamente en el fondo, y no en el buffer

6 */

8 class npc {

9 // posicion

10 int x,y;

11 int ax,ay;

12

13 int direccion;

14

15 int animacion;

16

17 int escena;

18

19 int estado;

20
21 BITMAP* imagen;

22

23 public:

24

25 void crea( BITMAP *_img, int _x, int _y, int dir, int _estado, int _lugar );

26 void pinta();

27};

28

29

30void npc::crea( BITMAP *_img, int _x, int _y, int dir, int _estado, int _lugar )

31{

32 x = _x;

33 y = _y;

34 direccion = dir;

35 animacion = 0;

36 escena = _lugar;

37

38 imagen = create_bitmap(_img->w, _img->h);

39
40 blit( _img, imagen, 0,0, 0,0, _img->w, _img->h);

41

42 // NPC parado

43 estado = _estado;

44}

45

46

47void npc::pinta()

48{

49 if ( lugar == escena )

50 {

51 // pinta el nuevo choque

52 rectfill( choque, x+2, y+1, x+30, y+31, 0xff0000);

53 masked_blit(imagen, fondo, animacion*32, direccion*32, x, y, 32,32);

54 }

55}

Se declara una clase llamada npc. Esta clase tiene varias variables privadas:

x,y : indica la posicin actual del NPC


ax,ay : indica la posicin anterior
direccion : indica hacia donde esta mirando, tiene un valor de 0 a 3.
animacion: indica el numero de la animacin, tiene un valor de 0 a 2.
escena: almacena el valor de la variable global lugar, e indica en que escenario debe mostrarse.
estado: indica si esta parado, andando, o etc.
imagen: guarda la imagen del NPC

Tiene dos funciones pblicas, crear y pinta.


crear(): se encarga de inicializar todas las variables, y recibe como parmetros la imagen que tendr el NPC, la posicin x,y donde se
situar el personaje dentro del mapa, direccin hacia donde mirar el personaje y el estado, en este caso solo tenemos un estado que es
parado y por ello no se tendr encuenta.

pinta(): Cuando el jugador se encuentre en el escenario correspondiente se muestra el NPC. Tambien pinta un rectangulo rojo en la
capa de choque, para que el jugador no pueda atravesar al NPC.

Recuerda de poner en el archivo main.cpp que se incluya el nuevo archivo con el comando #include "npc.h", este debe ser llamado
antes de incluir mijuego.h.

En el arcivo global.h se han pasado del archivo mijuego.h varias variables globales

?
1 BITMAP *fondo;

2 BITMAP *choque;

3 BITMAP *cielo;

5 // indicar en que lugar estamos


6 // 1: casa

7 // 2: bosque

8 // 3: ciudad

9 int lugar;

10

11int desplazamiento_map_x;

12int desplazamiento_map_y;

Y se ha aadido una nueva variable que se encargar de contar el total de ticks que realiza el programa.

?
1volatile unsigned int tiempo_total = 0;

3// Funcin para controlar la velocidad

4void inc_contador_tiempo_juego()

5{

6 contador_tiempo_juego++;

7 tiempo_total++;

8}

9END_OF_FUNCTION(inc_contador_tiempo_juego)
Se ha cambiado el desplazamiento del jugador, ahora depende de los frames, para que aunque se aumente el FRAME_RATE se
recorra el mismo espacio en el mismo tiempo. Sin este cambio cuando se aumentaba el FRAME_RATE el personaje corre mas.

?
1// es el espacio en pixel que recorre el jugador al andar

2const int desplazamiento= 120 / FRAME_RATE;

Quedando de el archivo de esta forma:

?
1 /*

2 Name: Curso RPG

3 Author: Yadok - KODAYGAMES

4 Date: 27/08/15 14:49

5 Web: http://devcpp-allegro.blogspot.com/

6 Description:

7 Creacion de un juego al estilo RPG

8 mas informacion en la web

9 Version: 10

10

11*/
12

13// clave del fichero datafile

14char evalc[19]="cursoRPGkodaygames";

15

16DATAFILE *datosjuego;

17

18// Ancho y alto de la pantalla

19const int PANTALLA_ANCHO = 800;

20const int PANTALLA_ALTO = 600;

21

22// En este BITMAP dibujaremos todo

23BITMAP *buffer;

24

25

26// Copiar el buffer a la pantalla del juego (screen)

27void pintar_pantalla()

28{

29 blit(buffer, screen, 0, 0, 0, 0, PANTALLA_ANCHO, PANTALLA_ALTO);

30}
31

32// controla el bucle principal

33bool salir;

34

35

36int cambio;

37

38// indicar en que lugar estamos

39// 1: casa

40// 2: bosque

41// 3: ciudad

42int lugar;

43

44int desplazamiento_map_x;

45int desplazamiento_map_y;

46

47BITMAP *fondo;

48BITMAP *choque;

49BITMAP *cielo;
50

51

52// Variable usada para la velocidad

53volatile unsigned int contador_tiempo_juego = 0;

54volatile unsigned int tiempo_total = 0;

55

56const int FRAME_RATE =30;

57

58// es el espacio en pixel que recorre el jugador al andar

59const int desplazamiento= 120 / FRAME_RATE;

60

61// Funcin para controlar la velocidad

62void inc_contador_tiempo_juego()

63{

64 contador_tiempo_juego++;

65 tiempo_total++;

66}

67END_OF_FUNCTION(inc_contador_tiempo_juego)
En el archivo main.cpp

?
1#include < allegro .h >

2#include "global.h"

3#include "datosjuego.h"

4#include "audio.h"

5#include "players.h"

6#include "npc.h"

7#include "mijuego.h"

En el archivo mijuego.h Se eliminan las lineas que se han pasado al global, y se aaden dos nuevas variables, quedando de la siguiente
forma

1player jugador;

3npc personajes[30];

4int npersonaje;

5
6bool desplaza;

npc personajes[30]; Sirve para guardar un maximo de 30 personajes de la clase NPC, o lo que es lo mismo, que se podr mostrar un
maximo de 30 npc. y la variable npersonaje se encarga de indicar cuantos estan activos, por tanto tendr un valor entre 0 y 29.

En la funcin carga_juego() se inicializa la variable cambio a cero, y se indica cuantos NPC se van a crear, en este ejemplo seran 2.

?
1desplazamiento_map_x=-160;

2desplazamiento_map_y=-160;

3desplaza = false;

5cambio = 0;

6npersonaje = 2;

8personajes[0].crea( (BITMAP *)datosjuego[diper004].dat, 160,120, 2,0,1);

9personajes[1].crea( (BITMAP *)datosjuego[diper002].dat, 50, 200, 3,0,1);

Las variables desplazamiento_map se utilizan para colocar el mapa con respecto a la pantalla.

En la funcion actualiza_juego() Se elimina la linea que inicializa la variable cambio=0. Tambien se elimina la parte en la que
comprueba la colision del jugador con el mapa.

?
1 // comprobar si colisiona con el mapa

2 bool choca = false;

3 int px = jugador.getx();

4 int py = jugador.gety()+16;

7 if ( lugar == 1)

8 {

9 px = jugador.getx()-160;

10 py = jugador.gety()-160+16;

11}

12if (lugar == 2 || lugar == 3)

13{

14 px = px + desplazamiento_map_x;

15 py = py + desplazamiento_map_y;

16}

17

18for ( int ci=2; ci < 30; ci++)

19{
20 for (int cj=0; cj < 16; cj++)

21 {

22

23 // color rojo

24 if ( getpixel( choque, px+ci, py+cj) == 0xff0000 ){

25 choca = true;

26 }

27 // color verde

28 if ( getpixel( choque, px+ci, py+cj) == 0x00ff00 ) cambio = 1;

29 // color azul

30 if ( getpixel( choque, px+ci, py+cj) == 0x0000ff ) cambio = 2;

31 // color amarillo

32 if ( getpixel( choque, px+ci, py+cj) == 0xffff00 ) cambio = 3;

33

34

35 }

36}

37if ( choca ){

38 // vuelve al estado anterior


39 jugador.posiciona( ax,ay );

40}

41

Todo este cdigo anterior es el que se debe de quitar de la funcin actualiza_juego(), ya que el control de choque del personaje se va a
poner en la clase player.

En la funcin pinta_juego() justo antes de donde se pinta el fondo al buffer, se hace un bucle para que pinte a todos los NPC

1for ( int z=0; z < npersonaje; z++ )

2{

3 personajes[z].pinta();

4}

6blit( fondo, buffer, ax, ay, bx, by, ancho, alto);

En el archivo player.h se aade la funcin de colisin, en la funcin de teclado().

1 if ( ax != x || ay != y )

2 {
3 if ( choca() )

4 {

5 x =ax;

6 y =ay;

7 }else{

8 int mx = desplazamiento_map_x;

9 int my = desplazamiento_map_y;

10

11 rectfill( choque, ax+4+mx, ay+17+my, ax+28+mx, ay+30+my, 0x000000);

12

13 // control choque para npcs

14 rectfill( choque, x+4+mx, y+17+my, x+28+mx, y+30+my, 0xffffff);

15

16 int num = FRAME_RATE / 12;

17 if ( tiempo_total % num == 0 )

18 {

19 sonido_pasos();

20 // entra si a cambiado alguna de las variables x,y

21 animacion++;
22 if ( animacion > 2 ) animacion = 0;

23 }

24 }

25}

En este cdigo anterior se aadi el comando rectfill, para pintar un rectngulo blanco sobre la imagen choque que servir para
controlar por donde va el jugador, y as pueda colisionar con los NPC. Primero se pinta un rectngulo en negro para borrar el anterior,
y luego se pinta otro en blanco en la nueva posicin del jugador. Tambin se debe aadir la nueva funcin choca(), con respecto a la
anterior esta cambia con una primera condicin y es que comprueba que el pixel no sea negro ni blanco, en ese caso entra y considera
que ha colisionado y comprueba de que color se trata, de esta forma ahora te colisionas con todos los colores menos el negro y el
blanco.

1 bool player::choca()

2 {

3 int mx = x+desplazamiento_map_x;

4 int my = y+desplazamiento_map_y;

6 bool resp=false;

7 for (int i=2; i < 30; i++ )

8 {
9 for (int j=16; j < 32; j++)

10 {

11

12 if ( getpixel ( choque, mx+i, my+j) != 0x000000 &&

13 getpixel ( choque, mx+i, my+j) != 0xffffff )

14 {

15 // si el color no es negro

16 resp = true;

17 // color verde

18 if ( getpixel( choque, mx+i, my+j) == 0x00ff00 ) cambio = 1;

19 // color azul

20 if ( getpixel( choque, mx+i, my+j) == 0x0000ff ) cambio = 2;

21 // color amarillo

22 if ( getpixel( choque, mx+i, my+j) == 0xffff00 ) cambio = 3;

23 }

24

25 }

26 }

27 return resp;
28}

Recordar de que hay que aadir la declaracin de la funcin dentro de las funciones pblicas de la clase player.

1 class player

2 {

3 BITMAP *prota;

4 int x,y;

5 int direccion;

6 int animacion;

8 public:

9 void inicia();

10 void pinta();

11 void teclado();

12 bool choca();

13 int getx(){ return x; };

14 int gety(){ return y; };

15 void posiciona( int _x, int _y);


16};

Este curso se divide en varias partes ya que para incluir el funcionamiento de los NPC de una forma eficiente se deben de cambiar
algunas cosas que ya estaban hechas. Estos cambios se explicaran en la prxima entrega. Esta entrega acaba aqu mostrando dos NPC
en la casa.

Recuerda Si tienes algn problema con los ejemplos de la pagina, o alguna duda. Puedes plantear tu pregunta en el foro de
programacin: http://creatusjuegosdecero.webege.com/index.php
Crear juego RPG en C++ y Allegro 4 (12) NPC II

Continuamos con el curso de crea tu juego RPG en C++ y Allegro. Seguimos con los NPC

Objetivo del curso


En esta entrega, se har que los NPC puedan tener movimiento, y tendrn varios tipos de movimientos ( horizontal, vertical, etc. ).

Programacin
En el archivo main.h en la funcion de inicia_allegro() al final se aadir el siguiente comando:
srand (time(NULL));

Este comando sirve para inicializar los valores aleatorios.

En el archivo players.h en la funcin de teclado() se elimina los cuatro if que controlan los limites globales de la pantalla.

// limites globales
if ( x < 0 ) x = 0;
if ( x > PANTALLA_ANCHO-32 ) x = PANTALLA_ANCHO-32;
if ( y < 0 ) y = 0;
if ( y > PANTALLA_ALTO-32 ) y = PANTALLA_ALTO-32;

Estas condiciones se habian puesto con la intencin de que el jugador no pueda desaparecer de la pantalla saliendo por uno de sus
bordes. Se elimina ya que esto es controlado por los mapas de choque, y si estan bien hechos el jugador no se sale de pantalla.

Se aade una nueva funcin llamada cambia_escenario(). Esta nueva funcin se encarga de borrar el rectangulo blanco creado por el
jugador, y esto debe hacerse antes de cambiar a otro escenario. Con el comando rectfill() se crea un rectangulo relleno de un color, en
nuestro caso se rellena del color negro (0x000000).
void player::cambia_escenario()
{
// siempre antes de cambiar a otro escenario se debe de borrar
// el cuadro de choque
int mx = desplazamiento_map_x;
int my = desplazamiento_map_y;

rectfill( choque, x+4+mx, y+17+my, x+28+mx, y+30+my, 0x000000);


}

En el archivo npc.h se aaden nuevas funciones para que nuestro npc pueda moverse. Se aaden dos nuevas variables privadas:
BITMAP* mifondo;
bool primer;
Y tambien se aaden dos nuevas funciones:
void actualiza();
bool chocanpc();

La funcion chocanpc(), se encarga de comprobar las colisiones. Y la funcin actualiza() se encarga del movimiento del npc.

En la funcin crea(), se aade la inicializacin de las nuevas variables:


mifondo = create_bitmap(32, 32);
primer = false;

La funcin chocanpc():

?
1 bool npc::chocanpc()
2 {
3 int ninix,niniy;
4 int nfinx,nfiny;
5
6 if ( direccion == 0 )
7 {
8 // abajo
9 ninix = 0;
10 niniy = 32 - desplazamiento;
11 nfinx = 32;
12 nfiny = 32;
13 }
14 if ( direccion == 1 )
15 {
16 // izquierda
17 ninix = 0;
18 niniy = 0;
19 nfinx = desplazamiento;
20 nfiny = 32;
21 }
22 if ( direccion == 2 )
23 {
24 // derecha
25 ninix = 32 - desplazamiento;
26 niniy = 0;
27 nfinx = 32;
28 nfiny = 32;
29 }
30 if ( direccion == 3 )
31 {
32 // arriba
33 ninix = 0;
34 niniy = 0;
35 nfinx = 32;
36 nfiny = desplazamiento;
37 }
38
39 // comprobar si colisiona con el mapa
40 for ( int ci=ninix; ci < nfinx; ci++)
41 {
42 for (int cj=niniy; cj < nfiny; cj++)
43 {
44
45 // color rojo
46 if ( getpixel( choque, x+ci, y+cj) == 0xff0000 ){
47 return true;
48 }
49 // color blanco prota
50 if ( getpixel( choque, x+ci, y+cj) == 0xffffff ){
51 return true;
52 }
53 }
54 }
55 return false;
56}

Esta funcin se encarga de controlar la colisin entre el NPC y el escenario controlando el color rojo, y la colisin entre el NPC y el
jugador con el color blanco. El sistema de colisin es igual que el del jugador mediante el comando getpixel() extrae el color del pixel
que hay en el mapa choque en una posicin concreta y la compara con los colores de choque, pero debido a que habr mas cantidad de
NPC para evitar que se relentice todo se ha mejorado un poco el sistema de colisin, ya que se ha reducido drasticamente el espacio
que se compara para comprobar si hay un pixel de color rojo o blanco. Segn la direccin se obtiene un rango de accin segn las
variables (ninix,niniy) y (nfinx,nfiny).

Como muestra la imagen, la zona pintada en verde indica el espacio que se comprueba para la colisin y segn el nmero indica la
direccin a la que pertenece. Por tanto si el NPC va hacia abajo se comprobar si existe colisin en el rectngulo verde con el numero
0.

Aqui tienen un video, donde se muestran las colisiones.

La funcin actualiza(), se encarga del movimiento del npc segn el valor de la variable estado:

0 : parado.
1 : movimiento horizontal.
2 : movimiento vertical.
3 : movimiento giro derecha. Camina en una misma direccin y cuando colisiona gira a la derecha.
4 : movimiento giro izquierda. Camina en una misma direccin y cuando colisiona gira a la izquierda.
5 : movimiento aleatorio. Camina en una misma direccin y cuando colisiona gira de forma aleatoria, y adems cada un
determinado tiempo tiene la posibilidad de cambiar de direccin.

Aqui tienen un video donde se muestran todos los tipos de movimiento que se van a crear en este tutorial.
?
1 void npc::actualiza()
2 {
3 // para indicar que se ejecuta dos veces
4 int num = FRAME_RATE / 6;
5
6 if ( tiempo_total % num == 0 )
7 {
8 if ( estado != 0 )
9 {
10 animacion++;
11 if ( animacion > 2 ) animacion = 0;
12 }
13
14 switch ( estado )
15 {
16 case 1: // camina horizontal
17 ax = x;
18 ay = y;
19 if ( direccion == 1 )
20 {
21 // camina izquierda
22 x-=desplazamiento;
23 if ( chocanpc() )
24 {
25 // posicion no valida
26 x = ax;
27 direccion = 2;
28 }
29 }
30 if ( direccion == 2 )
31 {
32 // camina derecha
33 x+=desplazamiento;
34 if ( chocanpc() )
35 {
36 // posicion no valida
37 x = ax;
38 direccion = 1;
39 }
40 }
41
42 if ( ax != x )
43 {
44 // borrar antiguo choque
45 rectfill( choque, ax+2, ay+1, ax+30, ay+31, 0x000000);
46
47 // pinta el nuevo choque
48 rectfill( choque, x+2, y+1, x+30, y+31, 0xff0000);
49
50 }
51
52 // cambie o no se cambia el fondo por la animacion
53
54 // restaura fondo anterior antes de pintar la nueva imagen
55 blit( mifondo, fondo, 0,0, ax,ay, 32,32);
56
57 // obtiene una copia del nuevo fondo que va a ser ocupado
58 blit( fondo, mifondo, x,y,0,0,32,32);
59
60 break;
61 case 2: // camina vertical
62 ax = x;
63 ay = y;
64 if ( direccion == 0 )
65 {
66 // camina abajo
67 y+=desplazamiento;
68 if ( chocanpc() )
69 {
70 // posicion no valida
71 y = ay;
72 direccion = 3;
73 }
74 }
75 if ( direccion == 3 )
76 {
77 // camina arriba
78 y-=desplazamiento;
79 if ( chocanpc() )
80 {
81 // posicion no valida
82 y = ay;
83 direccion = 0;
84 }
85 }
86
87 if ( ay != y )
88 {
89
90 // borrar antiguo choque
91 rectfill( choque, ax+2, ay+1, ax+30, ay+31, 0x000000);
92
93 // pinta el nuevo choque
94 rectfill( choque, x+2, y+1, x+30, y+31, 0xff0000);
95
96 }
97
98 // cambie o no se cambia el fondo por la animacion
99
100 // restaura fondo anterior antes de pintar la nueva imagen
101 blit( mifondo, fondo, 0,0, ax,ay, 32,32);
102
103 // obtiene una copia del nuevo fondo que va a ser ocupado
104 blit( fondo, mifondo, x,y,0,0,32,32);
105
106 break;
107 case 3: // camina giro derecha
108 ax = x;
109 ay = y;
110 if ( direccion == 0 )
111 {
112 // camina abajo
113 y+=desplazamiento;
114 if ( chocanpc() )
115 {
116 // posicion no valida
117 y = ay;
118 direccion = 1;
119 }
120 }
121 if ( direccion == 1 )
122 {
123 // camina izquierda
124 x-=desplazamiento;
125 if ( chocanpc() )
126 {
127 // posicion no valida
128 x = ax;
129 direccion = 3;
130 }
131 }
132 if ( direccion == 2 )
133 {
134 // camina derecha
135 x+=desplazamiento;
136 if ( chocanpc() )
137 {
138 // posicion no valida
139 x = ax;
140 direccion = 0;
141 }
142 }
143 if ( direccion == 3 )
144 {
145 // camina arriba
146 y-=desplazamiento;
147 if ( chocanpc() )
148 {
149 // posicion no valida
150 y = ay;
151 direccion = 2;
152 }
153 }
154 if ( ax != x || ay != y )
155 {
156 // se ha movido en una de las direcciones
157
158 // borrar antiguo choque
159 rectfill( choque, ax+2, ay+1, ax+30, ay+31, 0x000000);
160
161 // pinta el nuevo choque
162 rectfill( choque, x+2, y+1, x+30, y+31, 0xff0000);
163 }
164
165 // cambie o no se cambia el fondo por la animacion
166
167 // restaura fondo anterior antes de pintar la nueva imagen
168 blit( mifondo, fondo, 0,0, ax,ay, 32,32);
169
170 // obtiene una copia del nuevo fondo que va a ser ocupado
171 blit( fondo, mifondo, x,y,0,0,32,32);
172
173 break;
174 case 4: // camina giro izquierda
175 ax = x;
176 ay = y;
177 if ( direccion == 0 )
178 {
179 // camina abajo
180 y+=desplazamiento;
181 if ( chocanpc() )
182 {
183 // posicion no valida
184 y = ay;
185 direccion = 2;
186 }
187 }
188 if ( direccion == 1 )
189 {
190 // camina izquierda
191 x-=desplazamiento;
192 if ( chocanpc() )
193 {
194 // posicion no valida
195 x = ax;
196 direccion = 0;
197 }
198 }
199 if ( direccion == 2 )
200 {
201 // camina derecha
202 x+=desplazamiento;
203 if ( chocanpc() )
204 {
205 // posicion no valida
206 x = ax;
207 direccion = 3;
208 }
209 }
210 if ( direccion == 3 )
211 {
212 // camina arriba
213 y-=desplazamiento;
214 if ( chocanpc() )
215 {
216 // posicion no valida
217 y = ay;
218 direccion = 1;
219 }
220 }
221 if ( ax != x || ay != y )
222 {
223 // se ha movido en una de las direcciones
224
225 // borrar antiguo choque
226 rectfill( choque, ax+2, ay+1, ax+30, ay+31, 000000);
227
228 // pinta el nuevo choque
229 rectfill( choque, x+2, y+1, x+30, y+31, 0xff0000);
230
231 }
232
233 // cambie o no se cambia el fondo por la animacion
234
235 // restaura fondo anterior antes de pintar la nueva imagen
236 blit( mifondo, fondo, 0,0, ax,ay, 32,32);
237
238 // obtiene una copia del nuevo fondo que va a ser ocupado
239 blit( fondo, mifondo, x,y,0,0,32,32);
240
241 break;
242 case 5: // camina libre
243 if ( tiempo_total % 200 == 0 )
244 {
245 direccion = rand()%4;
246 }
247 ax = x;
248 ay = y;
249 if ( direccion == 0 )
250 {
251 // camina abajo
252 y+=desplazamiento;
253 if ( chocanpc() )
254 {
255 // posicion no valida
256 y = ay;
257 direccion = rand()%4;
258 }
259 }
260 if ( direccion == 1 )
261 {
262 // camina izquierda
263 x-=desplazamiento;
264 if ( chocanpc() )
265 {
266 // posicion no valida
267 x = ax;
268 direccion = rand()%4;
269 }
270 }
271 if ( direccion == 2 )
272 {
273 // camina derecha
274 x+=desplazamiento;
275 if ( chocanpc() )
276 {
277 // posicion no valida
278 x = ax;
279 direccion = rand()%4;
280 }
281 }
282 if ( direccion == 3 )
283 {
284 // camina arriba
285 y-=desplazamiento;
286 if ( chocanpc() )
287 {
288 // posicion no valida
289 y = ay;
290 direccion = rand()%4;
291 }
292
293 }
294
295 if ( ax != x || ay != y )
296 {
297 // se ha movido en una de las direcciones
298
299 // borrar antiguo choque
300 rectfill( choque, ax+2, ay+1, ax+30, ay+31, 0x000000);
301
302 // pinta el nuevo choque
303 rectfill( choque, x+2, y+1, x+30, y+31, 0xff0000);
304 }
305
306 // cambie o no se cambia el fondo por la animacion
307
308 // restaura fondo anterior antes de pintar la nueva imagen
309 blit( mifondo, fondo, 0,0, ax,ay, 32,32);
310
311 // obtiene una copia del nuevo fondo que va a ser ocupado
312 blit( fondo, mifondo, x,y,0,0,32,32);
313
314 break;
315 default: // parado
316 if ( tiempo_total % 300 == 0 )
317 {
318 direccion = rand()%4;
319 }
320 // pinta el nuevo choque
321 rectfill( choque, x+2, y+1, x+30, y+31, 0xff0000);
322
323 // restaura fondo anterior antes de pintar la nueva imagen
324 blit( mifondo, fondo, 0,0, x,y, 32,32);
325
326 // obtiene una copia del nuevo fondo que va a ser ocupado
327 blit( fondo, mifondo, x,y,0,0,32,32);
328
329 break;
330 }
331
332 }
333 }
Segn la variable estado se define el tipo de movimiento. Tanto en el movimiento vertical como horizontal, tiene un pequeo
inconveniente ya que no se definen dos direcciones, en el de horizontal no se tiene en cuenta las direcciones 0 y 3, de igual modo en la
vertical no se tiene en cuenta las direcciones 1 y 2. Esto puede provocar que el NPC no se mueva si a la funcin de crear el NPC recibe
como parmetro de direccin por ejemplo arriba o abajo, si le decimos que tiene movimiento horizontal. O tambin si se pone la
direccin derecha o izquierda y se le ha asignado como movimiento el vertical.

Este cdigo escrito anteriormente se puede mejorar, si os fijis existe algunas lineas que se repiten mucho. Esto da a entender que hay
cosas que no estn donde deberan. Esta mejora se pondr mas adelante.

Se modifica la funcin pinta(), ahora se aade el control del fondo donde se va a pintar el NPC y la llamada a la funcin actualiza().
?
1 void npc::pinta()
2 {
3 if ( lugar == escena )
4 {
5
6 if ( !primer )
7 {
8 // obtiene una copia de lo anterior
9 blit( fondo, mifondo, x,y,0,0,32,32);
10 primer = true;
11
12 }
13 actualiza();
14 masked_blit(imagen, fondo, animacion*32, direccion*32, x, y, 32,32);
15 }
16}

En el archivo mijuego.h, se aade una nueva funcion llamada carga_escenario(), que se encargar de cargar las imagenes del
escenario segun la variable lugar. Tambien se indicar si el escenario tiene scroll y se pondr la musica de cada escenario.

?
1 // carga los datos del escenario segun lugar
2 void carga_escenario()
3 {
4 jugador.cambia_escenario();
5 switch ( lugar )
6 {
7 case 1:// casa
8 fondo = (BITMAP *)datosjuego[dicasa].dat;
9 choque = (BITMAP *)datosjuego[dicasachoque].dat;
10 cielo = (BITMAP *)datosjuego[dicasasup].dat;
11
12 desplaza = false;
13
14 musica_casa();
15 break;
16
17 case 2:// bosque
18 fondo = (BITMAP *)datosjuego[dibosque].dat;
19 choque = (BITMAP *)datosjuego[dibosquechoque].dat;
20 cielo = (BITMAP *)datosjuego[dibosquesup].dat;
21
22 desplaza=true;
23
24 sonido_ambiente();
25 musica_bosque();
26 break;
27
28 case 3:// ciudad
29 fondo = (BITMAP *)datosjuego[dicity1].dat;
30 choque = (BITMAP *)datosjuego[dicity1choque].dat;
31 cielo = (BITMAP *)datosjuego[dicity1sup].dat;
32
33 desplaza=true;
34
35 musica_ciudad1();
36 break;
37
38 }
39}
Aqu se ve facilmente cuantos escenarios tiene el juego y facilita el querer aadir nuevos.
Al inicio de esta funcin se realiza una llamada a la funcin del jugador cambia_escenario(), para que borre su rectngulo de choque
del escenario anterior.

En la funcin actualiza_juego(), se cambia la parte en la que se comprueba el lugar y se hace uso de la nueva funcin
carga_escenario(), quedando el cdigo de la siguiente forma:
switch ( lugar )
{
case 1: // casa
if ( cambio == 1 )
{
// cambiamos a otro lugar
// bosque
lugar = 2;
carga_escenario();

jugador.posiciona( 410,370 );
desplazamiento_map_x=0;
desplazamiento_map_y=160;

cambio = 0;

}
break;
case 2: // bosque
if ( cambio == 2 )
{
// cambiamos a otro lugar
// casa
lugar = 1;
carga_escenario();

// situamos al prota dentro de la casa


jugador.posiciona( 290,440 );
desplazamiento_map_x=-160;
desplazamiento_map_y=-160;

para_sonido_ambiente();

cambio = 0;

}
if ( cambio == 3 )
{
// cambiamos a otro lugar
// ciudad
lugar = 3;
carga_escenario();

// situamos al prota dentro de la casa


jugador.posiciona( 500,540 );
desplazamiento_map_x=950;
desplazamiento_map_y=510;

para_sonido_ambiente();

cambio = 0;
}
break;
case 3: // ciudad
if ( cambio == 1 )
{
// cambiamos a otro lugar
// bosque
lugar = 2;
carga_escenario();

jugador.posiciona( 650,30 );
desplazamiento_map_x=200;
desplazamiento_map_y=0;

cambio = 0;
}
break;
}

En esta funcin se ha dejado el posicionamiento del personaje, ya que habr mapas al cual se podr acceder desde varios lugares.
Debido a esto tambin se a mantenido las variables de desplazamiento_map ya que segn la posicin del personaje, se tiene que situar
la pantalla.

Ya solo queda volver a carga_juego(), y cambiar las lineas que crean a los NPC.
npersonaje = 2;

personajes[0].crea( (BITMAP *)datosjuego[diper004].dat, 160,120, 2,4,1);


personajes[1].crea( (BITMAP *)datosjuego[diper002].dat, 50, 200, 3,5,1);
De esta forma se crean dos npc dentro de la casa, uno que se mover con giro a izquierda, y el segundo personaje se mover de una
forma aleatoria.

Si quieren ver como se hace para aadir un NPC mas, vean el siguiente video.

Recuerda

Si tienes algn problema con los ejemplos de la pagina, o alguna duda. Puedes plantear tu pregunta en el foro de
programacin: http://creatusjuegosdecero.webege.com/index.php

Crear juego RPG en C++ y Allegro 4 (13) Dialogos

Aqui esta una nueva entrega del curso Crea tu juego RPG en C++ y Allegro, esta vez nos vamos a centrar en los dilogos.
Objetivo del curso
En este curso, se crear una nueva clase que se encargue de los dilogos del juego. Siguiendo el estilo de juego RPG, cuando se
muestre el texto por pantalla se har de forma pausada para que de tiempo a leerlo y cuando el usuario pulse una tecla, salte al
siguiente texto. En esta primera entrega estar centrada en la clase dialogo.

Programacin
Se va a programar las siguientes funciones: menor, mayor, numlineas, crea, pinta, y cambia_texto.

La funcion menor, compara dos variables y devuelve el contenido de la variable de menor valor.

?
1int menor(int x, int y){
2 if ( x < y ){
3 return x;
4 }else{
5 return y;
6 }
7}

La funcin mayor, compara dos variables y devuelve el contenido de la variable de mayor valor.

?
1int mayor(int x, int y){
2 if ( x < y ){
3 return y;
4 }else{
5 return x;
6 }
7}

La funcin numlineas, calcula el nmero de lineas que se necesita para mostrar un texto dentro de un rectngulo determinado.

?
1 void MENSAJE::numlineas(){
2 int cont;
3 int espacio = 0;
4 char* mtexto = (char*)stexto.c_str();
5 nlineas=1;
6
7 if ( tancho+espaciado > ancho ){
8 // no cabe en una linea
9 string resto = stexto;
10 string trozo;
11 char caracter[] = " ";
12 char* caracter2;
13 int nuevoancho = 0;
14 int nc = 0;
15 int restoancho = 0;
16 do{
17 cont=1;
18 trozo = resto.substr(0,cont);
19 mtexto = (char*)trozo.c_str();
20 nuevoancho = text_length( fuente, mtexto );
21 espacio = 0;
22 while ( nuevoancho+espaciado < ancho ){
23 trozo = resto.substr(cont,1);
24 caracter2 = (char*)trozo.c_str();
25 if ( strcmp(caracter2,caracter)==0 ){
26 espacio = cont;
27 }
28 cont++;
29 trozo = resto.substr(0,cont);
30 mtexto = (char*)trozo.c_str();
31 nuevoancho = text_length( fuente, mtexto );
32 }
33 nc = resto.length();
34 trozo = resto.substr(cont,1);
35 caracter2 = (char*)trozo.c_str();
36 nlineas++;
37 if ( espacio >0 && cont < nc && strcmp(caracter2,caracter)!=0 )
38 {
39 resto = resto.substr(espacio);
40 }else{
41 resto = resto.substr(cont);
42 }
43 restoancho = text_length( fuente, resto.c_str() );
44 }while( restoancho+espaciado > ancho );
45
46 }
47}

Esta funcin sera mucho mas simple en el caso de que la fuente utilizada tenga la cualidad de que todos sus caracteres ocupen el
mismo ancho, es decir, que por ejemplo la "W" ocupe lo mismo que la "l". Pero como la mayora no es as, se complica un poco para
averiguar cuantos caracteres caben en un ancho determinado. Por ello, se va cogiendo carcter a carcter hasta completar una linea sin
llegar a sobrepasar el limite dado por el rectngulo. Debido a esto es algo complejo averiguar cuantas lineas van a ocupar, aunque si es
fcil averiguar cuantas lineas es el mximo que cabe en el espacio definido por el rectngulo. El nmero total de lineas que ocupar el
texto es almacenado en la variable nlineas.

La funcin crea, se encarga de inicializar las variables de la clase y define texto que se va a mostrar y las dimensiones del rectngulo
de texto.

?
1 void MENSAJE::crea(const char* t, FONT* f, int x1, int y1, int x2, int y2){
2 stexto = t;
3 tancho = text_length( f, t );
4 talto = text_height(f) + espaciado;
5 fuente = f;
6
7 mx1 = menor(x1,x2);
8 mx2 = mayor(x1,x2);
9 my1 = menor(y1,y2);
10 my2 = mayor(y1,y2);
11 ancho = abs( mx1 - mx2 );
12 alto = abs( my1 - my2 );
13
14 numlineas();
15
16};

Esta funcin recibe como parmetro el texto a mostrar, la fuente ( tipo de letra ), y las dimensiones del cuadro de dialogo. Inicializa los
valores que utiliza la clase, y se asegura que las coordenadas para definir el tamao del rectngulo estn de forma correcta. Y calcula el
ancho y alto del rectngulo.

La funcin pinta, se encarga de mostrar por pantalla el rectngulo de dialogo. Coloca en lo mejor posible el texto ajustado al tamao
del rectngulo.

?
1 void MENSAJE::pinta(BITMAP* b){
2 int cont;
3 int espacio = 0;
4 char* mtexto = (char*)stexto.c_str();
5 int linea=0;
6 int ni;
7 int altura = 0;
8 float exacto;
9 BITMAP *cuadro = create_bitmap(ancho,alto);
10
11
12 clear_to_color(cuadro, 0x222222);
13 set_trans_blender(0,0,0,130);
14 draw_trans_sprite(b, cuadro, mx1, my1);
15 set_trans_blender(0,0,0,255);
16
17 rect(b, mx1-1, my1-1, mx2-1, my2-1, 0xfcf902);
18 rect(b, mx1+1, my1+1, mx2+1, my2+1, 0x363712);
19 rect(b, mx1, my1, mx2, my2, 0x222222);
20
21
22 if ( tancho+espaciado > ancho ){
23 // no cabe en una linea
24 string resto = stexto;
25 string trozo;
26 char caracter[] = " ";
27 char* caracter2;
28 int nuevoancho = 0;
29 int nc = 0;
30 int restoancho = 0;
31
32 do{
33 cont=1;
34 trozo = resto.substr(0,cont);
35 mtexto = (char*)trozo.c_str();
36 nuevoancho = text_length( fuente, mtexto );
37 espacio = 0;
38 while ( nuevoancho+espaciado < ancho ){
39 trozo = resto.substr(cont,1);
40 caracter2 = (char*)trozo.c_str();
41 if ( strcmp(caracter2,caracter)==0 ){
42 espacio = cont;
43 }
44 cont++;
45 trozo = resto.substr(0,cont);
46 mtexto = (char*)trozo.c_str();
47 nuevoancho = text_length( fuente, mtexto );
48 }
49 nc = resto.length();
50 trozo = resto.substr(cont,1);
51 caracter2 = (char*)trozo.c_str();
52
53 if ( espacio >0 && cont < nc && strcmp(caracter2,caracter)!=0 ){
54 trozo = resto.substr(0,espacio);
55 mtexto = (char*)trozo.c_str();
56 resto = resto.substr(espacio);
57 }else{
58 trozo = resto.substr(0,cont);
59 mtexto = (char*)trozo.c_str();
60 resto = resto.substr(cont);
61 }
62
63
64 altura = alto - (talto*nlineas);
65 exacto = ( alto / nlineas );
66 ni = int( exacto );
67
68 textout_centre_ex(b, fuente, mtexto, mx1+1+int(ancho/2), my1+2+(ni*linea)-(talto/2)+(ni/2)
69, 0x363712, -1);
70 textout_centre_ex(b, fuente, mtexto, mx1+int(ancho/2), my1+1+(ni*linea)-(talto/2)+(ni/2) ,
710xffffff, -1);
72
73 linea++;
74 restoancho = text_length( fuente, resto.c_str() );
75 }while( restoancho+espaciado > ancho );
76
77 mtexto = (char*)resto.c_str();
78 textout_centre_ex(b, fuente, mtexto, mx1+1+int(ancho/2), my1+2+(ni*linea)-(talto/2)+(ni/2) ,
790x363712, -1);
80 textout_centre_ex(b, fuente, mtexto, mx1+int(ancho/2), my1+1+(ni*linea)-(talto/2)+(ni/2) ,
810xffffff, -1);
82
83 }else{
84
85 textout_centre_ex(b, fuente, mtexto, mx1+(ancho/2)+1, my1+1+((alto-talto)/2), 0x363712, -
861);
87 textout_centre_ex(b, fuente, mtexto, mx1+(ancho/2), my1+((alto-talto)/2), 0xffffff, -
881);
89
}

destroy_bitmap(cuadro);

};
Esta funcin se encarga de pintar un rectngulo, y sobre el se pinta el texto de manera que este centrado dentro de el todo el texto. En
el caso de que el texto no cabe en una linea segn el ancho del rectngulo, se realiza un bucle que se encarga de cojer carcter a
carcter y va formando las lineas para que no superen el ancho del rectngulo. Una vez que llega al ancho muestra la linea, y vuelve a
repetir el proceso con lo que queda por mostrar. Esta forma de hacerlo tiene un inconveniente, y es que cada vez que se muestra se
vuelve a calcular todo, por tanto es un proceso algo lento.

Aqu en este video se muestra un ejemplo utilizando la librera, creando varios cuadros de dilogos de distintos tamao, mostrando un
texto que se ajusta segn va variando el tamao de estos rectngulos.

La funcin cambia_texto, como su nombre indica sirve para cambiar el texto.

?
1void MENSAJE::cambia_texto( const char* t){
2 stexto = t;
3 tancho = text_length( fuente, t );
4
5 numlineas();
6};

Esta funcin se encarga de cambiar el texto a mostrar, actualizando los valores del texto.

La clase se define de la siguiente manera

?
1 class MENSAJE{
2
3 string stexto;
4
5 FONT *fuente;
6
7 // ancho total del texto enviado
8 int tancho;
9 int talto;
10
11 // para delimitar el rectangulo de vision
12 int mx1,my1;
13 int mx2,my2;
14 // ancho y alto del rectangulo
15 int ancho, alto;
16
17 // numero de lineas totales q caben en el rectangulo
18 int nlineas;
19
20 void numlineas();
21
22 public:
23
24 void crea(const char* t, FONT* f, int x1, int y1, int x2, int y2);
25
26 void pinta(BITMAP* b);
27
28 void cambia_texto( const char* t );
29
30};

La clase creada se ha llamado MENSAJE.

stexto : contiene el texto a mostrar


fuente : contiene el tipo de letra que se utilizar para escribir el texto.
tancho y talto : son dimensiones del texto segn la fuente utilizada.
mx1,my1 y mx2,my2 : son las coordenadas que delimitan nuestro cuadro de dialogo.
ancho, alto: son las dimensiones del cuadro de dialogo.
nlineas: indica el numero de lineas que ocupa el texto para mostrarse en el cuadro de dialogo.
numlineas() : funcin privada que se encarga de contar el nmero de lineas.
Para que no tengan problema con esta nueva librera, les dejo el siguiente link para descargar dialogos.h en archivo RAR.

Recuerda
Si tienes algn problema con los ejemplos de la pagina, o alguna duda. Puedes plantear tu pregunta en el foro de
programacin: http://creatusjuegosdecero.webege.com/index.php

Crear juego RPG en C++ y Allegro 4 (14) Dialogos II

Aqui esta una nueva entrega del curso Crea tu juego RPG en C++ y Allegro, seguimos con los dilogos.

NOTA IMPORTANTE:
Para realizar esta entrega es necesario tener hecho los anteriores,
ya que solo se hace referencia a cambios en el cdigo.
Objetivo del curso
En este curso, se crear una nueva clase que se encargue de los dilogos del juego. Siguiendo el estilo de juego RPG, cuando se
muestre el texto por pantalla se har de forma pausada para que de tiempo a leerlo y cuando el usuario pulse una tecla, salte al
siguiente texto. Continuando con lo anterior se aadir algunos dialogos a los NPC.

Programacin
Se ir comentando los cambios que se deben hacer para aadir los dialogos al programa.

En el archivo global.h se aade lo siguiente:


int hablando;

Esta variable se utilizar para controlar cuando se esta mostrando un cuadro de dialogo.

En el archivo main.cpp se aade el comando para incluir la nueva libreria dialogos.h, quedando de la siguiente forma:
#include < allegro.h >
#include "global.h"
#include "datosjuego.h"
#include "audio.h"
#include "dialogos.h"
#include "players.h"
#include "npc.h"
#include "mijuego.h"

En el archivo players.h se aade una nueva variable y cuatro funciones a la clase player. Para controlar cuando el jugador esta
hablando.
class player
{
BITMAP *prota;
int x,y;
int direccion;
int animacion;
bool hablar;

public:
void inicia();
void pinta();
bool choca();
void teclado();
int getx(){ return x; };
int gety(){ return y; };
void posiciona( int _x, int _y);
void cambia_escenario();
bool accion();
void habla();
void no_habla();
bool hablando(){ return hablar; };
};

La funcion accion() devuelve true en el caso de que se pulse una de las teclas de accion. Las teclas de accion definidas para el ejemplo
son return e intro.
bool player::accion()
{
return key[KEY_ENTER] || key[KEY_ENTER_PAD] ;
}

void player::habla()
{
hablar = true;
}

void player::no_habla()
{
hablar = false;
}

En la funcin teclado(), se aade una condicin para que cuando este hablando no se pueda mover el jugador.
void player::teclado()
{
int ax = x;
int ay = y;

if ( !hablar )
{
// 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;
}

}
...
El resto del codigo de esta funcin se mantiene igual.

En el archivo mijuego.h se han realizado varios cambios para facilitar la programacin mas adelante, por ello primero se va a
comentar los cambios que no tienen que ver con la programacin de los dialogos.

Se crea una nueva funcin llamada scroll_escenario(), esta contiene todo lo referente al scroll que estaba en la funcion de
actualiza_juego().
void scroll_escenario()
{

int ax,ay;

ax = jugador.getx();
ay = jugador.gety();

if ( desplaza )
{
int d = desplazamiento / 2;
// controla el desplazamiento del mapa si esta en los bordes
if ( ax < scroll_rango1 && desplazamiento_map_x > 0 )
{
desplazamiento_map_x-=d;
jugador.posiciona(ax+d,ay);
ax = jugador.getx();
ay = jugador.gety();
if ( ax < scroll_rango2 && desplazamiento_map_x > 0 )
{
desplazamiento_map_x-=d;
jugador.posiciona(ax+d,ay);
ax = jugador.getx();
ay = jugador.gety();
}
}
if ( ay < scroll_rango1 && desplazamiento_map_y > 0 )
{
desplazamiento_map_y-=d;
jugador.posiciona(ax,ay+d);
ax = jugador.getx();
ay = jugador.gety();
if ( ay < scroll_rango2 && desplazamiento_map_y > 0 )
{
desplazamiento_map_y-=d;
jugador.posiciona(ax,ay+d);
ax = jugador.getx();
ay = jugador.gety();
}
}
if ( ax > PANTALLA_ANCHO-scroll_rango1 && desplazamiento_map_x < fondo->w-PANTALLA_ANCHO )
{
desplazamiento_map_x+=d;
jugador.posiciona(ax-d,ay);
ax = jugador.getx();
ay = jugador.gety();
if ( ax > PANTALLA_ANCHO-scroll_rango2 && desplazamiento_map_x < fondo->w-PANTALLA_ANCHO )
{
desplazamiento_map_x+=d;
jugador.posiciona(ax-d,ay);
ax = jugador.getx();
ay = jugador.gety();
}
}
if ( ay > PANTALLA_ALTO-scroll_rango1 && desplazamiento_map_y < fondo->h-PANTALLA_ALTO )
{
desplazamiento_map_y+=d;
jugador.posiciona(ax,ay-d);
ax = jugador.getx();
ay = jugador.gety();
if ( ay > PANTALLA_ALTO-scroll_rango2 && desplazamiento_map_y < fondo->h-PANTALLA_ALTO )
{
desplazamiento_map_y+=d;
jugador.posiciona(ax,ay-d);
ax = jugador.getx();
ay = jugador.gety();
}
}

Se crea una nueva funcin llamada cambia_escenario(), esta contiene todo lo referente al cambio de escenario que se encontraba
dentro de la funcin de actualiza_juego().
void cambia_escenario()
{

switch ( lugar )
{
case 1: // casa
if ( cambio == 1 )
{
// cambiamos a otro lugar
// bosque
lugar = 2;
carga_escenario();
// situamos al prota dentro de la casa
jugador.posiciona( 410,370 );
desplazamiento_map_x=0;
desplazamiento_map_y=160;
cambio = 0;

}
break;
case 2: // bosque
if ( cambio == 2 )
{
// cambiamos a otro lugar
// casa
lugar = 1;
carga_escenario();
// situamos al prota cerca de la puerta
jugador.posiciona( 290,440 );
desplazamiento_map_x=-160;
desplazamiento_map_y=-160;
sonido_abrirpuerta();
para_sonido_ambiente();
cambio = 0;

}
if ( cambio == 3 )
{
// cambiamos a otro lugar
// ciudad
lugar = 3;
carga_escenario();
// situamos al prota en el camino
jugador.posiciona( 500,540 );
desplazamiento_map_x=950;
desplazamiento_map_y=508;
para_sonido_ambiente();
cambio = 0;
}
break;
case 3: // ciudad
if ( cambio == 1 )
{
// cambiamos a otro lugar
// bosque
lugar = 2;
carga_escenario();
// situamos al prota en el camino del bosque
jugador.posiciona( 650,30 );
desplazamiento_map_x=200;
desplazamiento_map_y=0;
cambio = 0;
}
break;
default:
break;
}
}

Se crea una nueva funcin llamada evento_escenario(), que se encarga de controlar las posibles acciones que se pueden realizar en
cada escenario, entre esos eventos se encuentra los dialogos de los NPC.
void evento_escenario()
{
int pzx = jugador.getx() + desplazamiento_map_x;
int pzy = jugador.gety() + desplazamiento_map_y;
switch ( lugar )
{
case 1:// casa
break;
case 2: // bosque
break;
case 3: // ciudad

if ( personajes[0].posicion_cerca(pzx,pzy)
&& jugador.accion() && !jugador.hablando() )
{
dialogo.cambia_texto(" Dejame!! estoy ocupadooooo!! ");
hablando = 1;
}

if ( personajes[4].posicion_cerca(pzx,pzy)
&& jugador.accion() && !jugador.hablando() )
{
dialogo.cambia_texto(" Aparta!!, no tengo tiempo para hablar con pueblerinos. Tengo que seguir
con mi ronda de vigilancia. " );
hablando = 1;
}

if ( personajes[5].posicion_cerca(pzx,pzy)
&& jugador.accion() && !jugador.hablando() )
{
dialogo.cambia_texto(" Soy la reina de los mares!! .. paseando por la calle voy ^_^ " );
hablando = 1;
}

if ( personajes[6].posicion_cerca(pzx,pzy)
&& jugador.accion() && !jugador.hablando() )
{
dialogo.cambia_texto(" Me han dicho que han visto un goblin merodeando por el bosque, debes
tener cuidado cuando vuelvas a tu casa." );
hablando = 1;
}

if ( hablando == 1 && !jugador.accion() )


{
hablando = 2;
jugador.habla();
}

// obliga a esperar minimo 1 segundo


if ( hablando > FRAME_RATE && jugador.accion() ){
hablando = 0;
}

if ( hablando == 0 && !jugador.accion() && jugador.hablando() )


{
jugador.no_habla();
}

break;
default:
break;
}
}
Dejando la funcon actualiza_juego(), mas facil de entender.
// actualiza el estado del juego
void actualiza_juego()
{

scroll_escenario();

jugador.teclado();

evento_escenario();

cambia_escenario();
}

En la funcin carga_juego(), se aade las siguientes lineas para aadir a un total de ocho NPC por todo el mapa de la ciudad, y se
crean dos cuadros de dialogos, en uno de ellos se muestra un texto fijo y en el otro se mostrar los textos de los dialogos con los NPC.
npersonaje = 8;

personajes[0].crea( (BITMAP *)datosjuego[diper001].dat, 1300,700, 1,1,3);


personajes[1].crea( (BITMAP *)datosjuego[diper005].dat, 280, 450, 0,2,3);
personajes[2].crea( (BITMAP *)datosjuego[diper005].dat, 230, 280, 3,2,3);
personajes[3].crea( (BITMAP *)datosjuego[diper003].dat, 960, 310, 2,3,3);
personajes[4].crea( (BITMAP *)datosjuego[diper005].dat, 1120, 450, 0,4,3);
personajes[5].crea( (BITMAP *)datosjuego[diper004].dat, 900, 650, 1,5,3);
personajes[6].crea( (BITMAP *)datosjuego[diper006].dat, 850, 800, 0,0,3);
personajes[7].crea( (BITMAP *)datosjuego[diper001].dat, 530, 280, 1,5,3);

texto.crea("Demo Dialogos. Ejemplo del Curso Crea tu juego RPG en C++ y Allegro ",
font, 5,5,230,60 );

dialogo.crea("", font, 10, PANTALLA_ALTO-100, PANTALLA_ANCHO-10, PANTALLA_ALTO-10);


hablando = 0;
La variable texto y dialogo se deben de declarar como variable global dentro del archivo mijuego.h
MENSAJE texto, dialogo;

const int scroll_rango1 = 200;


const int scroll_rango2 = 90;

La variable texto, crea un cuadro de dialogo en la esquina superior izquierda donde se muestra el mensaje "Demo Dialogos. Ejemplo
del Curso Crea tu juego RPG en C++ y Allegro". La variable dialogo crea un cuadro que ocupa todo el ancho de la parte de abajo de
la pantalla, con una altura de 90 pixel, y que inicialmente no contiene ningun texto.

Las variables scroll_rango1 y scroll_rango2 son utilizadas para definir el rango de accin del scroll.

En la funcion pinta(), se ha aadido para que se muestren los dialogos.


texto.pinta(buffer);

if ( hablando > 1 )
{
dialogo.pinta(buffer);
hablando++;
}

Estas lineas se aaden al final, despues de pintar del cielo.

En el archivo npc.h, se aade una nueva funcin a la clase NPC que se llama posicion_cerca().
bool posicion_cerca(int _x, int _y);

Esta funcin se encarga de controlar si la posicin pasada por parmetro esta cerca de la posicin del NPC.
bool npc::posicion_cerca(int _x, int _y)
{
int d = 32 + (desplazamiento*2);
int d2 =abs ( _x - x ) + abs ( _y - y );
return d2 <= d ;
}

La funcion posicin_cerca() mediante los parametros que recibe, calcula dos nmeros. La variable "d" contiene el valor de sumar el
desplazamiento*2 mas 32, estos valores se deben a que nuestros personajes todos tienen una dimension de 32 pixels. Y el
desplazamiento*2 es para que no tengas que estar totalmente pegado al npc, en este caso permitimos que estes a dos pasos de el. Es
decir, "d" es la distancia mxima a la que se puede estar de un npc para poder hablar con el o interactuar.
La variable "d2" tiene el valor de abs( _x-x) + abs(_y-y). Esto significa que se calcula la distancia que hay entre ambos puntos, en cada
uno de sus ejes y se suma. la funcin abs() transforma el valor que recibe por parametro a su valor absoluto, es decir, si recibe como
parmetro un 3 o un -3 en ambos casos devuelve 3 ya que es como ignorar el signo. El valor de la variable "d2" tendra la suma de la
distancia que hay en el eje x + la distancia en el eje y.

Con la condicion que hay en el "return d2 <= d ", lo que se consigue es que sea verdadero siempre que d2 sea menor o igual que d, es
decir, que la suma de las distancias de los dos ejes x,y sea menor o igual que la distancia mxima que se permite para interactuar con el
NPC. En la siguiente imagen vemos su rango de accion pintado en verde.

Recordar que el control de choque del jugador solo contempla la mitad inferior de la imagen del personaje, es por ello que existe mas
variaciones de posiciones por abajo del NPC.
De esta forma se ha controlado que la distancia sea la correcta, pero no esta del todo bien resuelto la forma para interactuar con el
NPC.

Segun vemos en la imagen anterior, el jugador esta dentro de la zona verde del NPC por tanto puede interactuar con el. Pero como se
ve ninguno esta mirando hacia el NPC y por tanto no debera dejar de interactuar con algo que se tiene a la espalda. Se debe aadir
para arreglar este fallo una funcin que nos diga si el jugador esta mirando hacia una posicion concreta, como es la del NPC, pero esto
lo dejo para hacer en otra entrega.

Llegado a este punto, ya estar completo para poder compilar y tendras algo parecido a lo que se muestra en el siguiente video.

Recuerda
Si tienes algn problema con los ejemplos de la pagina, o alguna duda. Puedes plantear tu pregunta en el foro de
programacin: http://creatusjuegosdecero.webege.com/index.php
Crear juego RPG en C++ y Allegro 4 (14A) Dialogos II+

Aqui esta una nueva entrega del curso Crea tu juego RPG en C++ y Allegro, seguimos con los dilogos, para hacer un retoque.

NOTA IMPORTANTE:
Para realizar esta entrega es necesario tener hecho los anteriores,
ya que solo se hace referencia a cambios en el cdigo.

En el curso anterior se dijo "tendras algo parecido a lo que se muestra en el siguiente video", esto se debe a que no se tena el tipo de
letras que se utiliza en el video y por tanto no sale igual. As que en esta entrega se explica como cambiar una fuente a nuestros
dilogos.

Requisitos
Para esta entrega se necesita tener todo lo anterior, y adems la nueva fuente (tipo de letra) que se va a utilizar.

1. Archivo RAR con la nueva fuente 2,96 KB descargar


descargar
2. Archivo RAR del Fichero DAT 14,20 MB

Se tiene dos opciones de descarga no es necesario descargar los dos. La primera opcin solo descargas la nueva fuente y se debe de
aadir al fichero DAT. En la segunda opcin descargas todo el fichero DAT completo con la nueva fuente ya incluida.
Si has escogido la primera opcin
Debes aadir la nueva fuente al fichero DAT utilizando la utilidad grabber. En el grabber se hace clic con el boton derecho y en el
menu se selecciona la opcion New - Font. Cuando pida el nombre se le escribe "dftextos", ya que en los ejemplos se utilizar este
nombre. Una vez creado se coloca el cursor sobre el nombre y se pulsa el boton derecho y se selecciona la opcin Grab, y selecciona
el archivo "lao16.pcx" que es el archivo que contiene la nueva fuente. Una vez hecho esto ya solo falta guardar el archivo y lo tendrs
listo para utilizar con el curso.

Programacin
Llegado a este punto, teniendo listo el fichero DAT, ya solo queda hacer un pequeo cambio en el cdigo para poder utilizar el nuevo
tipo de letra.

En el archivo mijuego.h, en la funcin carga_juego(), se debe de cambiar la siguiente linea:


dialogo.crea("", font, 10, PANTALLA_ALTO-100, PANTALLA_ANCHO-10, PANTALLA_ALTO-10);

Por esta otra:


dialogo.crea("", (FONT *)datosjuego[dftextos].dat, 10, PANTALLA_ALTO-100, PANTALLA_ANCHO-10,
PANTALLA_ALTO-10);

De esta forma le estamos indicando que ese cuadro de dialogo utilice la nueva fuente. Recordar que para acceder a un fichero guardado
en el dat se necesita el nombre con el que se ha guardado por ello es importante que si se escogi la primera opcin de descargar solo
la fuente, que el nombre sea igual, en este caso debe ser "dftextos".

Recuerda
Si tienes algn problema con los ejemplos de la pagina, o alguna duda. Puedes plantear tu pregunta en el foro de programacin:
http://creatusjuegosdecero.webege.com/index.php

Crear juego RPG en C++ y Allegro 4 (15) Lucha

Continuamos con el curso de crea tu juego RPG en C++ y Allegro. En esta entrega se aadir algo de accin.

Objetivo del curso


En esta entrega, se aadir la accin atacar al jugador, una nueva clase llamada enemigo al cual se puede atacar, nuevos sonidos.

Programacin
En el archivo audio.h, se aade lo siguiente:
void sonido_espada_aire(){
play_sample ( (SAMPLE *)datosjuego[dsespada1].dat, 100,128, 1200, 0 );
}

void sonido_espada_da(){
play_sample ( (SAMPLE *)datosjuego[dsespada2].dat, 160,128, 2300, 0 );
}

void sonido_muere(){
play_sample ( (SAMPLE *)datosjuego[dsmuerte01].dat, 120,128, 1000, 0 );
}

Estas tres funcines representa los tres nuevos sonidos que se van a aadir al proyecto, el primero cuando se pulsa la tecla ataque, el
segundo cuando golpea a un enemigo, y el tercero un sonido de muere enemigo. El funcionamiento en todas ellas es el mismo,
mediante el comando play_sample se ejecuta un sample que debe estar incluido en el fichero DAT con los nombres que vienen dados
entre corchetes: dsespada1, dsespada2 y dsmuerte01.

En el archivo player.h, se aade una variable privada llamada ataca, y dos funciones: atacando() y no_ataca(). Quedando la clase de
la siguiente manera:
class player
{
BITMAP *prota;
int x,y;
int direccion;
int animacion;
bool hablar;
int ataca;

public:
void inicia();
void pinta();
bool choca();
void teclado();
int getx(){ return x; };
int gety(){ return y; };
void posiciona( int _x, int _y);
void cambia_escenario();
bool accion();
void habla();
void no_habla();
bool hablando(){ return hablar; };
bool atacando(){ return ataca>1; };
void no_ataca(){ ataca = -3; };
}

Como se puede observar las dos nuevas funciones son bastantes sencilla. atacando() se encarga de devolver TRUE cuando el valor de
la variable ataca es superior a 1. La funcin no_ataca() asigna un valor a la variable ataca, en este caso -3.
En la funcin inicia(), se debe aadir la inicializacin de la nueva variable, por tanto se aade lo siguiente:
ataca = 0;

La funcin pinta(), cambia a lo siguiente:


void player::pinta()
{
if ( ataca > 1 && ( direccion == 1 || direccion == 3 ) )
{
masked_blit((BITMAP *)datosjuego[diespada].dat, buffer, 0, direccion*96, x-32, y-32, 96,96);
}

masked_blit(prota, buffer, animacion*32, direccion*32, x, y, 32,32);

if ( ataca > 1 && ( direccion == 0 || direccion == 2 ) )


{
masked_blit((BITMAP *)datosjuego[diespada].dat, buffer, 0, direccion*96, x-32, y-32, 96,96);
}
if ( ataca > 1 || ataca < 0) ataca++;
}

Existen muchas formas de haber programado esto. En el caso de poseer las imagenes de nuestro personaje portando la espada, en todas
direcciones y con animacin, la programacin hubiese sido aadir una condicin y poner otro masked_blit.
En este caso, no se posee dicha imagen. Se ha creado una imagen con el arma aparte. Debido a esto en la funcin se aade la nueva
imagen, que es la espada del personaje. Llegado a este punto, se puede hacer de dos formas. Una de ella ser poner la imagen espada
para cada una de las distintas direcciones y animaciones, lo cual es bastante tedioso programar.
Y la otra forma de hacerlo, es la que finalmente se ha utilizado. Para no complicar la programacion de colocar la espada en cada una de
las situaciones, se ha creado una imagen que es tres veces mas grande que el personaje, si el personaje es de 32x32, la imagen de la
espada es de 96x96. Esto se ha hecho asi para que no se tenga que colocar la espada al personaje mediante programacin, sino cuando
se crea la imagen, de esta forma la espada independientemente de la direccion de personaje siempre se situa en el mismo lugar en la
posicin x-32, y-32.
Existen dos condiciones parecidas, ya que segn la direccion interesa que la espada se pinte por encima del personaje o por debajo.
La ultima condicin, se encarga de incrementar la variable ataca, siempre que no sea ni 0, ni 1.
La funcion teclado()
void player::teclado()
{
int ax = x;
int ay = y;

if ( !hablar )
{
// 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 )
{
// entra si a cambiado alguna de las variables x,y
if ( choca() )
{
x =ax;
y =ay;
}else{
int mx = desplazamiento_map_x;
int my = desplazamiento_map_y;

rectfill( choque, ax+4+mx, ay+17+my, ax+28+mx, ay+30+my, 0x000000);

// control choque para npcs


rectfill( choque, x+4+mx, y+17+my, x+28+mx, y+30+my, 0xffffff);
}
int num = FRAME_RATE / 12;
if ( tiempo_total % num == 0 )
{
sonido_pasos();

animacion++;
if ( animacion > 2 ) animacion = 0;
}
}
if ( ataca > (FRAME_RATE / 4) ) ataca = 0;
}

En esta funcin, se aadi la tecla que se utiliza para atacar ( la barra espaciadora ), se ha cambiado lo que controla la animacin, para
que aunque se choque se siga moviendo el personaje.

En el archivo npc.h, se ha aadido la nueva clase enemigo, ya que se ha creado como una derivada de la clase npc.
class enemigo : public npc {
int vida;
int v_actual;
bool muerto;

public:
void crea( BITMAP *_img, int _x, int _y, int dir, int _estado, int _lugar, int v );
void herida( int d );
void pinta();
bool ha_muerto() { return muerto; };
};

void enemigo::crea( BITMAP *_img, int _x, int _y, int dir, int _estado, int _lugar, int v )
{
x = _x;
y = _y;
direccion = dir;
animacion = 0;
escena = _lugar;

imagen = create_bitmap(_img->w, _img->h);


mifondo = create_bitmap(32, 32);

blit( _img, imagen, 0,0, 0,0, _img->w, _img->h);

estado = _estado;

primer = false;
vida = v;
v_actual = vida;
muerto = false;
};

void enemigo::herida( int d )


{
if ( !muerto )
{
v_actual-=d;
if ( v_actual <= 0 )
{
muerto = true;
blit( mifondo, fondo, 0,0, x,y, 32,32);
rectfill( choque, x+2, y+1, x+30, y+31, 0x000000);
sonido_muere();
}
}
};

void enemigo::pinta()
{
if ( lugar == escena && !muerto )
{

if ( !primer )
{
// obtiene una copia de lo anterior
blit( fondo, mifondo, x,y,0,0,32,32);
primer = true;

}
actualiza();
masked_blit(imagen, fondo, animacion*32, direccion*32, x, y, 32,32);

int nm = (v_actual * 30 ) / vida;

if ( !muerto )
{
rectfill( fondo, x+1, y, x+nm, y+5, 0x00ff00);
rect( fondo, x, y, x+31, y+5, 0x000000);
}
}
}

Un enemigo se ha considerado que es igual a un NPC, pero con vida. Para controlar la vida tiene tres variables, vida, vida actual
(v_actual), muerto. Vida indica el mximo de puntos que tiene de vida, vida actual es un valor entre 0 y el mximo, y muerto indica
que la vida lleg a cero.
La funcin herida(), es la que se encarga de ir restando el dao que recibe.
La funcin pinta() es igual que la de el NPC pero con el aadido, de que pinta un marcador de vida.

En la declaracin de la clase npc, las variables privadas las declaramos como variables protegidas, aadiendo al principio de la clase la
palabra protected.
class npc {
protected:

En la funcin posicion_cerca() del npc, se aade el control del escenario.


bool npc::posicion_cerca(int _x, int _y)
{
int d = 32 + (desplazamiento*2);
int d2 =abs ( _x - x ) + abs ( _y - y );
return d2 <= d && lugar == escena ;
}

En el archivo mijuego.h, se crea una variable global:


enemigo malo;

En la funcion carga_juego(), se inicializa la nueva variable con la que se crea el enemigo.


malo.crea( (BITMAP *)datosjuego[diene001].dat, 380, 280, 3,5,2,100);

En la funcin evento_escenario(), al principio justo despues de la declaracion de las variables pzx,pzy , se aade una condicin para
controlar que se golpea al enemigo.
void evento_escenario()
{
int pzx = jugador.getx() + desplazamiento_map_x;
int pzy = jugador.gety() + desplazamiento_map_y;

if ( jugador.atacando() && malo.posicion_cerca(pzx,pzy)


&& !malo.ha_muerto() )
{
int xn = 2 + rand()%2;
jugador.no_ataca();
sonido_espada_da();
malo.herida(xn);
}

...

En la funcin actualiza_juego(), se cambia el orden en el que se llaman las funciones. Esto se debe a que con el orden antiguo no se
llega a ver la imagen de la espada cuando se golpea al enemigo.
// actualiza el estado del juego
void actualiza_juego()
{

scroll_escenario();

evento_escenario();

jugador.teclado();

cambia_escenario();
}

En la funcin pinta_juego(), se aade la siguiente linea para mostrar al enemigo, justo despues de que se muestren los NPC y antes de
que se pinte el fondo en el buffer.
malo.pinta();

Y con esto ya se tiene todo para tener un enemigo en el mapa. Solo faltara aadir las nuevas imagenes y sonidos al fichero DAT.

Haz clic aqui para descargar el nuevo fichero DAT comprimido en RAR

Recuerda
Si tienes algn problema con los ejemplos de la pagina, o alguna duda. Puedes plantear tu pregunta en el foro de
programacin: http://creatusjuegosdecero.webege.com/index.php

Crear juego RPG en C++ y Allegro 4 (16) Barra de vida

Aqu esta una nueva entrega del curso crea tu juego RPG en C++ y Allegro. En esta entrega se har que se muestre por pantalla la
barra de vida.
Objetivo
Aadir al jugador un nuevo parmetro, que controle la vida. Y crear una funcin que se encargue de mostrar por pantalla la vida actual
del personaje (jugador), mostrando una barra de vida.

Programacin
En el archivo players.h, en la definicin de la clase se aaden dos variables privadas para controlar la vida.
int vida;
int vidamax;

La variable vida contendr el valor de la vida actual de personaje. La variable vidamax contendr el valor maximo de vida que puede
tener el personaje.

Al ser estas dos variables privadas, se crean dos funciones para que se pueda obtener los valores de estas variables.
int getvida(){ return vida; };
int getvidamax(){ return vidamax; };

En la funcin player::inicia(), se debe inicializar las dos nuevas variables. En este caso se inicializan con el valor de 200.
vida = 200;
vidamax = 200;

En el archivo mijuego.h, se crea la nueva funcin llamada pinta_barra_vida().


void pinta_barra_vida()
{
int n = (jugador.getvida()*150) / jugador.getvidamax() ;
rectfill( buffer, PANTALLA_ANCHO-162, 10, PANTALLA_ANCHO-8, 25, 0x003300);
rectfill( buffer, PANTALLA_ANCHO-160, 12, PANTALLA_ANCHO-160+n, 23, 0x00ff00);
rectfill( buffer, PANTALLA_ANCHO-160, 12, PANTALLA_ANCHO-160+n, 15, 0xbbffaa);
rect( buffer, PANTALLA_ANCHO-162, 10, PANTALLA_ANCHO-8, 25, 0x000000);
}

Esta funcin se encarga como su nombre indica de pintar la barra. La variable n tiene el valor de la ecuacion ( vida*150 / vidamax ), de
esta forma se obtiene un valor proporcional al valor de vida actual con respecto a 150, que es el tamao total de la barra, para mostrar
por pantalla.
Con los comandos rectfill se pintan tres rectangulos relleno, el primero es el que va al fondo que es de color verde oscuro (0x003300),
el segundo es de color verde (0x00ff00), y el tercero de verde claro (0xbbffaa). Y finalmente con el comando rect se pinta un
rectngulo sin relleno, que se utiliza para pintar el borde del rectngulo de color negro (0x000000).

En la funcin pinta_juego(), se aade al final la llamada a la funcin pinta_barra_vida();

Si todo esta correctamente, se mostrar en la esquina superior derecha, una barra de vida.

Recuerda
Si tienes algn problema con los ejemplos de la pagina, o alguna duda. Puedes plantear tu pregunta en el foro de
programacin: http://creatusjuegosdecero.webege.com/index.php

Crear juego RPG en C++ y Allegro 4 (17) Lucha II

Continuamos con el curso de crea tu juego RPG en C++ y Allegro. En esta entrega se continuar el sistema de lucha.
Objetivo
Aadir un nuevo movimiento al enemigo, cuando entra en combate, la posibilidad de golpear al jugador. Controlar que no se golpee si
no esta enfrente del enemigo. Aadir mas de un enemigo.

Programacin
En el archivo audio.h, se aaden dos nuevas funciones para los dos nuevos sonidos: espada choca, y herido.
void sonido_espada_choca(){
play_sample ( (SAMPLE *)datosjuego[dsespada3].dat, 110,128, 900, 0 );
}

void sonido_herido(){
play_sample ( (SAMPLE *)datosjuego[dsaa].dat, 160,128, 900, 0 );
}

En el archivo players.h, se aaden dos funciones nuevas a la clase. La funcin dire() que devuelve el valor de la variable direccion. Y
la funcin herido() que se utiliza para disminuir la vida segn el parmetro dado.
class player
{
BITMAP *prota;
int x,y;
int direccion;
int animacion;
bool hablar;
int ataca;

int vida;
int vidamax;
public:
void inicia();
void pinta();
bool choca();
void teclado();
int getx(){ return x; };
int gety(){ return y; };
void posiciona( int _x, int _y);
void cambia_escenario();
bool accion();
void habla();
void no_habla();
bool hablando(){ return hablar; };
bool atacando(){ return ataca>1; };
void no_ataca(){ ataca = -3; };
int dire(){ return direccion; };

int getvida(){ return vida; };


int getvidamax(){ return vidamax; };
void herido(int n){ vida-=n; };
};

En el archivo npc.h, se cambia la funcin posicion_cerca(). Se quitan los parmetros que reciba, y se calcula la posicin del jugador.
bool npc::posicion_cerca()
{
int _x = jugador.getx() + desplazamiento_map_x;
int _y = jugador.gety() + desplazamiento_map_y;
int d = 32 + (desplazamiento*2);
int d2 =abs ( _x - x ) + abs ( _y - y );
return d2 <= d && lugar == escena ;
}

Esta funcin devuelve true en el caso de que el jugador este cerca del enemigo. Las dos primeras lneas declaran dos variables que
contienen la posicin del jugador. Como la funcin de posicin del jugador es con respecto a la pantalla y no con respecto al escenario,
es por ello que se le aade el desplazamiento del mapa para obtener la posicin real en el escenario. El resto del cdigo se mantiene
igual, calculando la distancias entre ambos puntos.
Recuerda que al cambiar la funcin npc::posicion_cerca(), en la funcin evento_escenario() de mijuego.h debes modificar todas las
llamadas, ya que ahora no tiene parmetros.

En la clase enemigo, se aade una nueva funcin para evitar el fallo que haba antiguamente de que si el jugador estaba cerca pero no
mira hacia el objetivo, esta funcin se encarga de comprobar si el jugador esta de frente al objetivo.
class enemigo : public npc {
int vida;
int v_actual;
bool muerto;
int golpeado;
public:
void crea( BITMAP *_img, 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();
bool frente();
};

Tambien se a aadido una nueva funcin en la que se controla el movimiento del enemigo cuando entra en lucha contra el jugador.

En la funcin crea(), se tiene que inicializar la nueva variable golpeado con el valor cero.

void enemigo::herida( int d )


{
if ( !muerto )
{
int num = FRAME_RATE / 2;
v_actual-=d;

if ( v_actual <= 0 )
{
muerto = true;
blit( mifondo, fondo, 0,0, x,y, 32,32);
rectfill( choque, x+2, y+1, x+30, y+31, 0x000000);
sonido_muere();
}else{
// dao defensivo del enemigo
if ( tiempo_total % num == 0 )
{
if ( rand()%2 == 1 )
{
sonido_herido();
jugador.herido(5+rand()%5);
}
}
}
}
};

La funcin herida() ha cambiado, ahora se aadi que cada vez que reciba un dao tenga la oportunidad de responder con un dao al
jugador, de esta manera se crea como un dao defensivo del enemigo.

El funcionamiento de herida() es el siguiente: primero se comprueba que no este muerto, en el caso de no estarlo se resta el dao
pasado por parametro a la vida. En el caso de que la vida actual llegue a cero muere el enemigo. Y aqui llega lo nuevo, sino esta
muerto se comprueba si el tiempo_total es divisible entre num, y si el numero aleatorio obtenido es 1, en ese caso el enemigo daa al
jugador.

bool enemigo::frente()
{
int jx = jugador.getx() + desplazamiento_map_x;
int jy = jugador.gety() + desplazamiento_map_y;

int d = jugador.dire();

if ( jx > x )
{
if ( abs ( jy - y ) < desplazamiento*2 )
{
if ( d == 1 )
{
return true;
}else{
return false;
}
}
}

if ( jx < x )
{
if ( abs ( jy - y ) < desplazamiento*2 )
{
if ( d == 2 )
{
return true;
}else{
return false;
}
}
}

if ( jy < y )
{
if ( abs ( jx - x ) < desplazamiento*2 )
{
if ( d == 0 )
{
return true;
}else{
return false;
}
}
}

if ( jy > y )
{
if ( abs ( jx - x ) < desplazamiento*2 )
{
if ( d == 3 )
{
return true;
}else{
return false;
}
}
}

return false;
}

Esta funcin frente(), se encarga de comprobar en que direccion esta el jugador. En el caso de que este mirando hacia el enemigo se
considera que esta de frente y devuelve true.

Se aade tambin la funcin movimiento().


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;
}

// borrar antiguo choque


rectfill( choque, ax+2, ay+1, ax+30, ay+31, 0x000000);

// pinta el nuevo choque


rectfill( choque, x+2, y+1, x+30, y+31, 0xff0000);
}

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;
}
}
}

// cambie o no se cambia el fondo por la animacion

// restaura fondo anterior antes de pintar la nueva imagen


blit( mifondo, fondo, 0,0, ax,ay, 32,32);

// obtiene una copia del nuevo fondo que va a ser ocupado


blit( fondo, mifondo, x,y,0,0,32,32);
}

Esta funcin movimiento(), hace que el enemigo intente ir hacia donde este el jugador. En el caso de estar cerca al jugador realiza de
forma aleatoria un ataque. Se podra decir que es la IA del enemigo cuando entra modo lucha.

En la funcin pinta(), se a aadido alguna cosas que anteriormente estaban fuera.


void enemigo::pinta()
{

if ( lugar == escena && !muerto )


{

if ( !primer )
{
// obtiene una copia de lo anterior
blit( fondo, mifondo, x,y,0,0,32,32);
primer = true;
}

if ( v_actual == vida )
{
actualiza();
}else{
movimiento();
}
masked_blit(imagen, fondo, animacion*32, direccion*32, x, y, 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( fondo, x+1, y, x+nm, y+5, 0x00ff00);
rect( fondo, x, y, x+31, y+5, 0x000000);
}
}

if ( lugar != escena && v_actual < vida )


{
int num = FRAME_RATE / 5;
if ( tiempo_total % num == 0 )
{
v_actual++;
}
}
}

Se controla si el jugador a golpeado al enemigo, teniendo una pequea probilidad de pararlo (1 de 10). Tambien si el jugador se va del
escenario donde se encuentra el enemigo, en el caso de estar herido el enemigo recuperar vida poco a poco. Mientras el enemigo
tenga la vida al completo se mueve como un npc y si esta herido se mueve segun la funcin movimiento().

En el archivo mijuego.h, despues de la declaracin de la variable jugador se realiza el #include "npc.h", por ello se debe de eliminar el
include de npc.h del archivo main.cpp.

Se crean dos variables globales malos[], nmalos, para controlar los enemigos. Quedando de la siguiente forma el inicio del archivo
mijuego.h.
player jugador;

#include "npc.h"

npc personajes[30];
int npersonaje;

enemigo malos[30];
int nmalos;

MENSAJE texto, dialogo;

int mision;

// para saber si el mapa tiene scroll


bool desplaza;

const int scroll_rango1 = 200;


const int scroll_rango2 = 90;

En la funcin carga_juego(), se aade la creacin de dos enemigos, sustituyendo la linea donde se creaba al enemigo malo.crea().
nmalos = 2;
malos[0].crea( (BITMAP *)datosjuego[diene001].dat, 380, 280, 3,5,2,100);
malos[1].crea( (BITMAP *)datosjuego[diene001].dat, 400, 720, 0,5,2,100);

En la funcin evento_escenario(), se elimina lo siguiente:


if ( jugador.atacando() && malo.posicion_cerca(pzx,pzy)
&& !malo.ha_muerto() )
{
int xn = 2 + rand()%2;
jugador.no_ataca();
sonido_espada_da();
malo.herida(xn);
}

Esto ya no es necesario aqui, ya que esto se controla en la clase enemigo.

En la funcin pinta_juego(), se elimina la llamada malo.pinta(), y se sustituye por lo siguiente:


for ( int z=0; z < nmalos; z++ )
{
malos[z].pinta();
}

De esta forma se pinta tantos malos como se tengan, segn la variable nmalos y la dimensin de la variable malos[], que en este caso
es de 30.

Hasta aqu llega la programacin, solo hay que actualizar el fichero DAT que contiene los nuevos sonidos, etc.

Haz clic aqui para descarcar fichero DAT en formato RAR

Recuerda
Si tienes algn problema con los ejemplos de la pagina, o alguna duda. Puedes plantear tu pregunta en el foro de
programacin: http://creatusjuegosdecero.webege.com/index.php

Crear juego RPG en C++ y Allegro 4 (18) Experiencia

Continuando con nuestro juego, despus de la lucha cuando se mata al enemigo debemos recibir experiencia. Pues de esa parte nos
vamos a encargar en este tutorial.

Objetivo
Hacer que el personaje tenga experiencia y nivel. Y segn un numero de experiencia suba un nivel. Tambin se mostrar al igual que la
barra de vida, la barra de experiencia para que el jugador sepa cuanto le falta para el prximo nivel.
Programacin
Empezamos por el archivo global.h, aqu se aade unas nuevas variables.
bool lvl_up;
int nlvlup;
int lux, luy;

Con estas variable se controlar lo siguiente:

lvl_up se utiliza para indicar si se ha subido nivel, si es TRUE entonces indica que si sube nivel.
nlvlup se utiliza para mostrar por pantalla la imagen LVL UP!, va contando el numero de veces que se ha mostrado.
lux,luy sirven para posicionar por pantalla la imagen LVL UP!.

En el archivo players.h, se aaden nuevas variables y funciones para la experiencia, quedando la clase de la siguiente forma:
class player
{
BITMAP *prota;
int x,y;
int direccion;
int animacion;
bool hablar;
int ataca;

int vida;
int vidamax;

int enivel;
int exp;

public:
void inicia();
void pinta();
bool choca();
void teclado();
int getx(){ return x; };
int gety(){ return y; };
void posiciona( int _x, int _y);
void cambia_escenario();
bool accion();
void habla();
void no_habla();
bool hablando(){ return hablar; };
bool atacando(){ return ataca>1; };
void no_ataca(){ ataca = -3; };
int dire(){ return direccion; };

int getvida(){ return vida; };


int getvidamax(){ return vidamax; };
void herido(int n){ vida-=n; };

void sube_experiencia( int e );

int getnivel(){ return enivel; };


int getexp(){ return exp; };
};

La variable enivel contiene el nivel actual del personaje, y la variable exp contiene la experiencia actual. Se han aadido dos funciones
para poder ver los valores de las dos nuevas variables, la funcin getnivel() y getexp(). Y la funcin sube_experiencia(), tiene como
parmetro la cantidad de experiencia que se aumenta, se encarga de aumentar el valor de la experiencia y comprobar si se ha llegado a
un nuevo nivel.
void player::sube_experiencia( int e )
{
exp = exp + e;
int nxp = 100 * ( enivel + 1 );

if ( exp >= nxp && nxp != 1 )


{
exp = exp - nxp;
enivel++;
// ha subido de nivel !!
sonido_sube_nivel();
lvl_up = true;

nxp = 100 * ( enivel + 1 );


while ( exp >= nxp )
{
exp = exp - nxp;
enivel++;
// ha subido de nivel !!
sonido_sube_nivel();
nxp = 100 * ( enivel + 1 );
}
}
}

La variable nxp contiene el valor de la ecuacin 100 * (enivel + 1), esto quiere decir que al nivel 1 el valor de nxp es 200, a nivel 2 es
300, a nivel 3 es 400, y as sucesivamente. Esta ecuacin tiene una progresin aritmtica, quiere decir que la diferencia entre dos
trminos sucesivos cualesquiera de la secuencia es una constante, en este caso es 100. Al usar este tipo de ecuacin se esta haciendo
que la dificultad de subir un nivel sea relativamente sencillo. Esta ecuacin no es la mas correcta pero para nosotros nos vale por el
momento, prximamente se har un curso exclusivo hablando de esta ecuacin y la dificultad.

En la esta funcin se encarga de sumar la experiencia que se recibe por el parmetro e. Luego se compara con el nivel de experiencia
que se necesita para subir el nivel actual (npx) y si es superior la experiencia actual (exp), a esta experiencia actual se le resta lo npx.
Se calcula el valor del prximo nivel de experiencia (npx) y si exp sigue siendo mayor se repite hasta que no lo sea.

En la funcion player::inicia(), se inicializan a cero las nuevas variables.


exp = 0;
enivel = 0;

En el archivo audio.h, se aade la funcion del sonido para cuando sube de nivel.
void sonido_sube_nivel(){
play_sample ( (SAMPLE *)datosjuego[dsubenivel].dat, 110,128, 1300, 0 );
}

En el archivo npc.h, en la clase enemigo se aade una nueva variable exp, y una funcin para actualizar este valor.
class enemigo : public npc {
int vida;
int v_actual;
bool muerto;
int golpeado;
int exp;
public:
void crea( BITMAP *_img, 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();
bool frente();
void daexp(int e){ exp = e; };
};

En la funcin enemigo::crea(), se debe inicializar la nueva variable, para nuestro caso le daremos el valor por defecto de 50.
exp = 50;

Se aade en la funcin enemigo::herida() la llamada a la funcin sube_experiencia(), que se aadir despus del sonido_muere().
jugador.sube_experiencia(exp);

En el archivo mijuego.h, se aade una nueva barra en la funcin pinta_barra_vida(), aparte de pintar la barra de vida, ahora tambin
pintar la barra de experiencia.
void pinta_barra_vida()
{

int n = (jugador.getvida()*150) / jugador.getvidamax() ;


rectfill( buffer, PANTALLA_ANCHO-162, 10, PANTALLA_ANCHO-8, 25, 0x003300);
rectfill( buffer, PANTALLA_ANCHO-160, 12, PANTALLA_ANCHO-160+n, 23, 0x00ff00);
rectfill( buffer, PANTALLA_ANCHO-160, 12, PANTALLA_ANCHO-160+n, 15, 0xbbffaa);
rect( buffer, PANTALLA_ANCHO-162, 10, PANTALLA_ANCHO-8, 25, 0x000000);

int nxp = 100 * ( jugador.getnivel() + 1 );


int n2 = (jugador.getexp()*150) / nxp ;

rectfill( buffer, PANTALLA_ANCHO-162, 30, PANTALLA_ANCHO-8, 45, 0x000033);


rectfill( buffer, PANTALLA_ANCHO-160, 32, PANTALLA_ANCHO-160+n2, 43, 0x0000ff);
rectfill( buffer, PANTALLA_ANCHO-160, 32, PANTALLA_ANCHO-160+n2, 35, 0xbbaaff);
rect( buffer, PANTALLA_ANCHO-162, 30, PANTALLA_ANCHO-8, 45, 0x000000);
textprintf_centre_ex( buffer, font, PANTALLA_ANCHO - 80, 34, 0xFFFFFF, -1, "Niv: %d",
jugador.getnivel() );
}

Se crea una nueva funcin llamada pinta_lvlup(), que se encargar de mostrar una imagen con el texto LVL UP! por encima del
jugador.
void pinta_lvlup()
{
if ( lvl_up )
{
nlvlup = 1;
lux = jugador.getx();
luy = jugador.gety();
lvl_up = false;
}
if ( nlvlup > 0 )
{
masked_blit ( (BITMAP *)datosjuego[dilvlup].dat, buffer, 0,0, lux, luy - nlvlup*2, 32,32 );
int num = FRAME_RATE / 10;
if ( tiempo_total % num == 0 )
{
nlvlup++;
}
if ( nlvlup > 40 ) nlvlup = 0;
}
}

Con la variable num, se obtiene un valor para que la imagen se muestre 10 veces en un segundo, y se va aumentando la variable nlvlup
para contar cuantas veces se ha mostrado, cuando supere las 40 se reinicia a cero.

En la funcin pinta_juego(), se aade la llamada a la funcion pinta_lvlup() justo despus de la llamada a la


funcion pinta_barra_vida().

Y aqui ya termina la programacin, solo falta tener el ultimo archivo DAT, que contiene el sonido y la imagen.

Puedes descargar archivo DAT haciendo clic aqui

A continuacin teneis un video donde se muestra como el jugador lucha contra varios enemigos. Dos de ellos dan mas experiencia,
que son los soldados, de este modo suben varios niveles. Cuando se sube de nivel aparece encima del jugador la imagen que pone
"LVL UP".

RPG (19): Nivel de Dificultad

Este tema es terico, y no se va a realizar nada de programacin. En el se explicar la ecuacin utilizada para la subida de nivel.

En la funcin player::sube_experiencia(), se encuentra la ecuacin que determina los niveles.


nxp = 100 * ( enivel + 1 );

Esta ecuacin tiene una progresin aritmtica, que quiere decir que la diferencia entre dos trminos sucesivos cualesquiera de la
secuencia es una constante, en este caso es 100.
Como ya se dijo anteriormente esta ecuacin no es la mas indicada, debido a que la dificultad de subir un nivel va disminuyendo. Para
que se entienda esto, pondremos un ejemplo.

Segun la ecuacin, esta es la lista de experiencia necesaria para cada nivel.

Niv. Exp.
1 200
2 300
3 400
4 500
5 600

La experiencia por defecto que da al matar un enemigo es de 50, a continuacin se muestra cuantos enemigos se deben matar para
subir dicho nivel.

Ene.
Niv. Exp.
lvl. 1
1 200 4
2 300 6
3 400 8
4 500 10
5 600 12

Supongamos que se crea otros tipos enemigos, pero de un nivel superior. Enemigos de nivel 2, 3, etc.. y estos enemigos darn mas
experiencia segn nivel. Se Muestra en una tabla cuantos enemigo de cada nivel se debe matar para subir al siguiente nivel, se
presupone que no puedas matar a un enemigo que este 2 niveles por encima de tu nivel.
Ene. Ene. Ene. Ene. Ene. Ene. Ene. Ene. Ene. Ene.
Niv. Exp.
lvl. 1 lvl. 2 lvl. 3 lvl. 4 lvl. 5 lvl. 6 lvl. 7 lvl. 40 lvl. 100 lvl. 101
1 200 4
2 300 6 4
3 400 8 5.3 4
4 500 10 6.6 5 4
5 600 12 8 6 4.8 4
6 700 14 9.3 7 5.6 4.6 4
7 800 16 10.6 8 6.4 5.3 4.5 4
40 4100 82 54.6 41 32.8 27.3 23.4 20.5 4
100 10100 202 134.6 101 80.8 67.3 57.7 50.5 9.85 4
101 10200 204 136 102 81.6 68 58.2 51 9.95 4.03 4

Como se muestra en la tabla cada enemigo mayor se va disminuyendo el valor para el siguiente nivel, tendiendo a 4. Para el nivel 100
es necesario matar 4 de nivel 100, y para subir al nivel 101 tan solo se debe matar 4,03 enemigos de nivel 100. Es decir, la progresin
es tan pequea que para niveles altos resultar mas fcil subir al siguiente nivel, siempre y cuando se este matando enemigos de tu
nivel o uno inferior. Llegara un punto que ya mataras enemigos de dos o tres niveles por debajo y aun as seguiras subiendo
igualmente.

Por tanto, tal y como se ha demostrado no interesa que tenga una progresin aritmtica ya que la diferencia entre un nivel y el
consecutivo es un valor constante, y en nuestro caso es el 100, este valor en niveles pequeo este bien, pero para niveles altos el valor
es ridculo.

Quizs lo que nos interesa es un valor que vaya aumentando que no sea constante. A esto se llama progresin geomtrica cuando cada
elemento se obtiene multiplicando el elemento anterior por una constante.

Probamos a cambiar la ecuacion a lo siguiente:


npx = 200 * 2 ^( enivel - 1 )
NIV EXP ENE
1 200 4
2 400 8
3 800 16
4 1600 32
5 3200 64

Con esta nueva ecuacin, vemos que conforme se sube de nivel se duplica el numero de experiencia necesaria para subir de nivel, por
tanto, cuanto mas niveles se suba mas difcil resulta subir.

En esta imagen, se muestran las dos ecuaciones representadas grficamente y se puede comprobar que la segunda ecuacin que se ha
elegido como buena opcin, quizs no lo sea tanto, debido a que aumenta el doble a cada nivel que va subiendo.

Veamos la tabla de enemigos de esta nueva ecuacin.


NIV EXP ENE 2 3 4 5 6
1 200 4
2 400 8 4
3 800 16 8 4
4 1600 32 16 8 4
5 3200 64 32 16 8 4
6 6400 128 64 32 16 8 4

Como se muestra en la tabla, los valores son exactamente los mismos. Por tanto si se quiere subir al nivel 4, solo tienes que matar 4 de
nivel 4. Si eres nivel 4 y quieres subir al 5 solo tienes que matar 4 de nivel 5, es decir, que debes matar siempre a 4 de un nivel superior
al tuyo o 8 de tu nivel para subir al siguiente nivel.

Escoger una buena ecuacin es importante, ya que en este modo de juego que se esta programando no se tendr limite de nivel, por
tanto, si se escoge una ecuacin como la primera llegar a ser muy fcil el subir niveles cuando seas un nivel alto. En cambio con la
segunda opcin la dificultad es siempre la misma debes matar el mismo numero de enemigos de tu nivel independientemente del nivel
en el que te encuentres.

Toda esta informacin es orientativa, ya que he considerado como algo normal el hecho de matar 8 enemigos de tu nivel para subir al
nivel siguiente. La dificultad no solo se representa en matar 4 o 8 enemigos, si no tambin en cuanto nos cuesta matar un enemigo. Es
recomendable que si se va a poner armas que aumentan su dao, tambin sea algo progresivo, es decir, que aumente pero siempre la
misma proporcin para que el jugador siempre intente conseguir armas de su nivel para facilitar el poder matar a un enemigo.

Crear juego RPG en C++ y Allegro 4 (20) Inventario

Continuamos con el curso de crea tu juego RPG en C++ y Allegro. En esta entrega se har el inventario.
Objetivo
En este curso se aadir los objetos. Estos objetos se podr conseguir matando enemigos. Tambien se aade el inventario del jugador,
que tiene un limite de 12 objetos.

Programacin
En el archivo global.h, se aade una nueva variable, que se va a encargar de controlar cuando se esta mostrando el inventario, esta
variable se llamar swinv.
int swinv;

En el archivo players.h, se aaden nuevas funciones y una variable para el manejo del inventario.
class player
{
BITMAP *prota;
int x,y;
int direccion;
int animacion;
bool hablar;
int ataca;

int vida;
int vidamax;

int enivel;
int exp;

int inventario[12];

public:
void inicia();
void pinta();
bool choca();
void teclado();
int getx(){ return x; };
int gety(){ return y; };
void posiciona( int _x, int _y);
void cambia_escenario();
bool accion();
void habla();
void no_habla();
bool hablando(){ return hablar; };
bool atacando(){ return ataca>1; };
void no_ataca(){ ataca = -3; };
int dire(){ return direccion; };

int getvida(){ return vida; };


int getvidamax(){ return vidamax; };
void herido(int n){ vida-=n; };

BITMAP* getimg(){ return prota; };

void sube_experiencia( int e );

int getnivel(){ return enivel; };


int getexp(){ return exp; };

void obtiene_objeto( int id );


int getinventario(int id){ return inventario[id]; };
};

La variable inventario es una matriz de 12 elementos, en los cuales se almacenar el id del objeto. La funcin obtiene_objeto(), recibe
el id de un objeto y lo asigna a una posicin de la matriz siempre que exista un hueco vacio, en el caso de estar el inventario lleno, el
objeto en cuestin se pierde.
La funcin getinventario(), devuelve segn el parmetro id, lo que se contiene en la posicin id del vector inventario.
void player::obtiene_objeto( int id ){
int n = 0;
while ( n < 12 && inventario[n] != 0 )
{
n++;
}
// tiene un hueco
if ( inventario[n] == 0 ){
inventario[n] = id;
}
}

Esta funcin recibe el id del objeto, e intenta almacenarlo en una de las casillas del vector inventario. Inicializa n a cero, y se repite el
bucle hasta que encuentre un hueco vacio, es decir, que inventario[n]=0 o que n sea mayor que 11. En el caso de encontrar un hueco,
asigna el valor de id, al vector inventario en la posicin n.

En el archivo npc.h, en la funcin enemigo::herida() se aade justo despues de subir la experiencia al jugador, lo siguiente para que el
jugador reciba un objeto.
if ( v_actual <= 0 )
{
muerto = true;
blit( mifondo, fondo, 0,0, x,y, 32,32);
rectfill( choque, x+2, y+1, x+30, y+31, 0x000000);
sonido_muere();
jugador.sube_experiencia(exp);
// siempre obtiene un objeto aleatorio
int q = objetos[rand()%6].id;
jugador.obtiene_objeto( q );
}else{

En el archivo mijuego.h, se aade lo siguiente despues de la declaracin de la variable jugador.


player jugador;

struct m_objetos{
int id;
};

m_objetos objetos[6];

#include "npc.h"

Se crea una esctrutura llamada m_objetos, que ahora mismo le aadimos una variable llamada id. Y luego se declara la variable
objetos, que se trata de un vector de dimension 6, de la clase m_objetos.

En la funcin carga_juego(), se inicializan las nuevas variables, que son el vector de objetos y la variable swinv.
// carga objetos
for (int e=0; e < 6; e++){
objetos[e].id = dio001 + e;
}

swinv=0;

Se crea una nueva funcin llamada control_inventario().


void control_inventario(){
if ( swinv == 0 && key[KEY_I] ){
swinv = 1;
}

if ( swinv == 3 && !key[KEY_I] ) swinv=0;

if ( swinv == 2 && key[KEY_I] ){


swinv = 3;
}
}

Esta funcin se encarga de controlar cuando se pulsa la tecla I, para mostrar el inventario.

Se aade la llamada a la funcin control_inventario() dentro de la funcin actualiza_juego().


// actualiza el estado del juego
void actualiza_juego()
{

scroll_escenario();

evento_escenario();

jugador.teclado();

control_inventario();

cambia_escenario();
}

Se crea una funcin que muestre el inventario por pantalla, que se llama pinta_inventario().
void pinta_inventario()
{
if ( swinv > 0 )
{

int jx = jugador.getx();
int jy = jugador.gety();

if ( jx > PANTALLA_ANCHO/2 ){
jx = (PANTALLA_ANCHO/4) - 160;
}else{
jx = (PANTALLA_ANCHO*3/4) - 160;
}

if ( jy > PANTALLA_ALTO/2 ){
jy = (PANTALLA_ALTO/4) - 81;
}else{
jy = (PANTALLA_ALTO*3/4) - 81;
}

// se muestra fondo inventario


masked_blit( (BITMAP *)datosjuego[diinventario].dat, buffer, 0,0, jx,jy, 320,162);

masked_blit( jugador.getimg(), buffer, 32,0, jx+65,jy+60, 32,32);


masked_blit( (BITMAP *)datosjuego[dio005].dat, buffer, 0,0, jx+115,jy+74, 32,32);
masked_blit( (BITMAP *)datosjuego[dio006].dat, buffer, 0,0, jx+12,jy+74, 32,32);

int id;
for ( int i=0; i < 4; i++){
for ( int j=0; j < 3; j++){
int num = (j*4) + i ;
id = jugador.getinventario( num );
if ( id != 0 ){
int posx = jx + 172 + i*34;
int posy = jy + 40 + j*34;
masked_blit( (BITMAP *)datosjuego[id].dat, buffer, 0,0, posx,posy, 32,32);
}
}
}

if ( !key[KEY_I]) swinv = 2;
}
}
Esta funcin se podra haber simplificado mucho, pero debido a la imagen que utilic para mostrar el contenido del inventario y que
cambia de posicion para que se pueda seguir jugando mientras ves el inventario se ha complicado un poco.

El codigo que hay del principio de la funcin hasta el comentario "//se muestra fondo inventario", esa parte se encarga de calcular la
posicin de la ventana inventario representada por la posicion jx,jy. Todo esto depende de la posicion del jugador, se divide la pantalla
en 4 partes y se coloca el inventario en el que no se encuentra el jugador siguiendo la siguiente regla, si la posicion x es menor de la
mitad de la pantalla, la posicion x del inventario ser mayor de la mitad, sino es a la inversa. Y asi igualmente para el eje y.

Despues de todo esto se pinta de forma transparente con el comando masked_blit, priemro el fondo del inventario, luego el personaje,
y despues dos objetos ( armadura y espada )

El doble bucle for se utiliza para mostrar todos los posibles objetos que tenga el jugador en el inventario.

En la funcion pinta_juego(), se aade la llamada a la funcin pinta_inventario(). Esta llamada se pondr despues de pintar la barra de
vida y el lvlup.
pinta_barra_vida();

pinta_lvlup();

pinta_inventario();

Y llegado a este punto si todo se ha copiado correctamente solo nos faltar actualizar el fichero DAT para tener las nuevas imgenes
que se utilizan en este curso.

Haz clic para descargar el fichero DAT en formato RAR.


Crear juego RPG en C++ y Allegro 4 (21) Inventario II

Otra entrega del curso crea tu juego RPG en C++ y Allegro. En esta entrega se continua el inventario.

Objetivo
En esta entrega, haremos que se pueda equipar el personaje con algunos de los objetos recibidos, y poder cambiar los objetos de lugar.

Programacin
En el archivo global.h, se aade lo siguiente:
DATAFILE *datobjetos;

// control raton en inventario


int swraton;
int nsel;
int nid;
Se crea una nueva variable del tipo DATAFILE para el manejo del nuevo fichero de objetos.
Con estas tres variables, se va a controlar si se ha pulsado el boton del raton swraton, en que casilla se ha pulsado nsel, y cual es el
objeto en caso de tener nid.

La funcin pintar_pantalla(), se borra de global.h, para ponerlo en el main.cpp.

En el archivo audio.h, se aade dos funciones:


void sonido_boton(){
play_sample ( (SAMPLE *)datosjuego[dsboton1].dat, 110,128, 1100, 0 );
}

void sonido_boton2(){
play_sample ( (SAMPLE *)datosjuego[dsboton2].dat, 100,128, 1400, 0 );
}

La funcin sonido_boton se ha utilizado para poner sonido cuando se suelta un objeto. La funcin sonido_boton2 se ha utilizado para
poner el sonido cuando se coje el objeto. Tanto dsboton1 y dsboton2 son nuevos sonidos que se han aadido al fichero DAT.

En el archivo players.h, se han aadido todo lo referente a la equipacin que lleva actualmente el personaje y algunas funciones para
acceder y manejar las nuevas variables del equipo y el inventario.

Aadir lo siguiente en la parte privada de la clase player.


// equipacion actual
int casco;
int armadura;
int arma;
int anillo;

Aadir lo siguiente al final, en la parte pblica de la clase player.


void cambia_inventario(int p1, int p2 );
void pon_inventario(int pos, int id){ inventario[pos]=id; };

int getarma(){ return arma; };


int getarmadura(){ return armadura; };
int getcasco(){ return casco; };
int getanillo(){ return anillo; };

void pon_arma(int id){ arma=id; };


void pon_armadura(int id);
void pon_casco(int id){ casco=id; };
void pon_anillo(int id){ anillo=id; };

Estas funciones son muy bsicas, las "get" para obtener el valor, y las funciones "pon" para asignar un valor. La funcion
pon_inventario(pos, id), segun la posicion dada, coloca el objeto id en el inventario.
void player::cambia_inventario(int p1, int p2 )
{
int t;
t = inventario[p1];
inventario[p1] = inventario[p2];
inventario[p2] = t;
};

La funcion player::cambia_inventario(), intercambia dos posiciones del inventario.


void player::pon_armadura(int id)
{
armadura = id;
if ( id != -1 )
prota = (BITMAP *)datobjetos[equipo_personaje(armadura)].dat;
if ( id == -1 )
prota = (BITMAP *)datosjuego[dipersonaje0].dat;
};

La funcin player::pon_armadura(), es un poco mas compleja que las dems ya que al quitar o cambiar la armadura tambien se
cambia la imagen del personaje. En el caso de que id sea distinto de -1 indica que se esta equipando una armadura. En el caso de que id
sea -1, indica de que no tiene ninguna armadura, por tanto esta desnudo.

En la funcin player::inicia(), se inicializan las nuevas variables para que el personaje tenga una equipacin inicial.
casco = -1;
armadura = 4;
arma = 5;
anillo = -1;

prota = (BITMAP *)datobjetos[equipo_personaje(armadura)].dat;

Segn estos datos, estamos indicando de que no tendr casco (-1), la armadura ser la (4) el traje normal, el arma el (5) la espada, y
anillo no tiene (-1). Y finalmente se asigna a la imagen del personaje, la imagen correspondiente a la armadura seleccionada.

Se crea un nuevo archivo llamado misobjetos.h, que se encargar del manejo de los objetos.

?
1 // misobjetos.h
2
3 #include < vector >
4 using namespace std;
5
6 struct m_objetos{
7 int id;
8 int tipo;
9 char nombre[50];
10 int img;
11};
12
13vector< m_objetos > lobj;
14
15
16int tipo( int id ){
17 for(int i = 0; i < lobj.size(); i++)
18 {
19 if ( lobj[i].id == id ) return lobj[i].tipo;
20 }
21 return -1;
22}
23
24int equipo_personaje( int id ){
25 for(int i = 0; i < lobj.size(); i++)
26 {
27 if ( lobj[i].id == id && lobj[i].tipo < 5 ) return lobj[i].img;
28 }
29 return -1;
30}
31
32void lee_objetos(){
33 char contenido[255];
34
35 m_objetos temp;
36
37 packfile_password(NULL);
38 PACKFILE *fichero;
39
40 fichero = pack_fopen("objetos.txt","r");
41
42 while ( !pack_feof(fichero) )
43 {
44 pack_fgets( contenido, 255, fichero);
45 temp.id = atoi( contenido );
46
47 pack_fgets( contenido, 255, fichero);
48 temp.tipo = atoi( contenido );
49
50 pack_fgets( temp.nombre, 255, fichero);
51
52 pack_fgets( contenido, 255, fichero);
53 temp.img = atoi( contenido );
54
55 lobj.push_back( temp );
56 }
57
58 pack_fclose(fichero);
59};

Se crea una estructura llamada m_objetos, que contiene id, tipo, nombre y img. Se crea un vector de la estructura que se llama lboj, en
la cual se va a guardar todos los objetos.

La funcion tipo(), devuelve el tipo del objeto segun el id recibido por parmetro.

La funcin equipo_personaje(), devuelve el valor de img del objeto si es un tipo de objeto equipable. En caso contrario devuelve -1
para indicar que no se ha encontrado.

La funcin lee_objetos(), se encarga de leer un archivo objetos.txt, en el cual estan almacenados todos los objetos que se utilizaran en
el juego. Conteniendo de cada objeto el id, tipo, nombre y imagen. El id es el numero de la imagen dentro del fichero DAT, el tipo se
utiliza para de que tipo de objeto es:

1. armas
2. cascos
3. armaduras
4. anillos
5. pcimas
6. otros

La img, no es mas que el numero de una imagen dentro del fichero DAT de objetos.

En el archivo mijuego.h, se suprime todo lo referente a los objetos, ya que se ha puesto en el nuevo fichero misobjetos.h.

En la funcin carga_juego(), antes de inicializar la variable jugador se carga el nuevo archivo objetos.dat, y se realiza la llamada a la
funcin lee_objetos(). Y se inicializa la variable swraton a -1. Se debe borrar el bucle for que haba para cargar los objetos.
datobjetos = load_datafile("objetos.dat");
if ( !datobjetos ){
allegro_message("Error: archivo objetos.dat no encontrado\n%s\n", allegro_error);
}
lee_objetos();

jugador.inicia();

swraton=-1;

La funcin pinta_inventario(), cambia tanto, que he decidido ponerla entera.

?
1 void pinta_inventario()
2 {
3 if ( swinv > 0 )
4 {
5
6 int jx = jugador.getx();
7 int jy = jugador.gety();
8 int posx;
9 int posy;
10
11 if ( jx > PANTALLA_ANCHO/2 ){
12 jx = (PANTALLA_ANCHO/4) - 160;
13 }else{
14 jx = (PANTALLA_ANCHO*3/4) - 160;
15 }
16
17 if ( jy > PANTALLA_ALTO/2 ){
18 jy = (PANTALLA_ALTO/4) - 81;
19 }else{
20 jy = (PANTALLA_ALTO*3/4) - 81;
21 }
22
23 // se muestra fondo inventario
24 masked_blit( (BITMAP *)datosjuego[diinventario].dat, buffer, 0,0, jx,jy, 320,162);
25
26 if ( swraton == 1 && mouse_b&2 )
27 {
28 swraton = -2;
29 }
30
31 masked_blit( jugador.getimg(), buffer, 32,0, jx+65,jy+60, 32,32);
32
33 if ( jugador.getcasco() != -1 )
34 {
35 masked_blit((BITMAP *)datobjetos[equipo_personaje(jugador.getcasco())].dat, buffer,
36 32, 0, jx+65, jy+60, 32,32);
37 }
38
39
40 posx=jx+116;
41 posy=jy+40;
42 if ( mouse_x > posx && mouse_x < posx+32 && mouse_y > posy && mouse_y < posy+32 )
43 {
44 if ( mouse_b&1 && swraton != -2)
45 {
46 // se ha pulsado dentro de un hueco
47 if ( swraton == -1 )
48 {
49 // primer clic
50 swraton = 1;
51 nsel = -4;
52 nid = jugador.getanillo();
53 if ( nid == -1 ){
54 swraton = -1;
55 }else{
56 sonido_boton2();
57 }
58 }else{
59
60 if ( nid != jugador.getanillo() && tipo(nid) == 4 )
61 {
62 // cambia objeto de sitio
63 if ( jugador.getanillo() != -1 ){
64 jugador.pon_inventario(nsel,jugador.getanillo());
65 }else{
66 jugador.pon_inventario(nsel,0);
67 }
68
69
70 jugador.pon_anillo(nid);
71 sonido_boton();
72
73 swraton = -2;
74 }
75 }
76 }
77 }
78
79
80
81 if ( jugador.getanillo() != -1 )
82 {
83 masked_blit( (BITMAP *)datobjetos[jugador.getanillo()].dat, buffer, 0,0,
84 jx+116,jy+40, 32,32);
85 }
86
87 posx=jx+116;
88 posy=jy+74;
89 if ( mouse_x > posx && mouse_x < posx+32 && mouse_y > posy && mouse_y < posy+32 )
90 {
91 if ( mouse_b&1 && swraton != -2)
92 {
93 // se ha pulsado dentro de un hueco
94 if ( swraton == -1 )
95 {
96 // primer clic
97
98 swraton = 1;
99 nsel = -1;
100 nid = jugador.getarmadura();
101 if ( nid == -1 ){
102 swraton = -1;
103 }else{
104 sonido_boton2();
105 }
106 }else{
107
108 if ( nid != jugador.getarmadura() && tipo(nid) == 3 )
109 {
110 // cambia objeto de sitio
111 if ( jugador.getarmadura() != -1 ){
112 jugador.pon_inventario(nsel,jugador.getarmadura());
113 }else{
114 jugador.pon_inventario(nsel,0);
115 }
116
117 jugador.pon_armadura(nid);
118 sonido_boton();
119
120 swraton = -2;
121 }
122 }
123 }
124 }
125
126 if ( jugador.getarmadura() != -1 )
127 {
128 masked_blit( (BITMAP *)datobjetos[jugador.getarmadura()].dat, buffer, 0,0,
129 jx+116,jy+74, 32,32);
130 }
131
132
133 posx=jx+13;
134 posy=jy+74;
135 if ( mouse_x > posx && mouse_x < posx+32 && mouse_y > posy && mouse_y < posy+32 )
136 {
137 if ( mouse_b&1 && swraton != -2)
138 {
139 // se ha pulsado dentro de un hueco
140 if ( swraton == -1 )
141 {
142 // primer clic
143
144 swraton = 1;
145 nsel = -2;
146 nid = jugador.getarma();
147 if ( nid == -1 ){
148 swraton = -1;
149 }else{
150 sonido_boton2();
151 }
152 }else{
153
154 if ( nid != jugador.getarma() && tipo(nid) == 1 )
155 {
156 // cambia objeto de sitio
157 if ( jugador.getarma() != -1 ){
158 jugador.pon_inventario(nsel,jugador.getarma());
159 }else{
160 jugador.pon_inventario(nsel,0);
161 }
162
163 jugador.pon_arma(nid);
164 sonido_boton();
165
166 swraton = -2;
167 }
168 }
169 }
170 }
171
172
173 if ( jugador.getarma() != -1 )
174 {
175 masked_blit( (BITMAP *)datobjetos[jugador.getarma()].dat, buffer, 0,0,
176 jx+13,jy+74, 32,32);
177 }
178
179 posx=jx+13;
180 posy=jy+40;
181 if ( mouse_x > posx && mouse_x < posx+32 && mouse_y > posy && mouse_y < posy+32 )
182 {
183 if ( mouse_b&1 && swraton != -2)
184 {
185 // se ha pulsado dentro de un hueco
186 if ( swraton == -1 )
187 {
188 // primer clic
189
190 swraton = 1;
191 nsel = -3;
192 nid = jugador.getcasco();
193 if ( nid == -1 ){
194 swraton = -1;
195 }else{
196 sonido_boton2();
197 }
198 }else{
199
200 if ( nid != jugador.getcasco() && tipo(nid) == 2 )
201 {
202 // cambia objeto de sitio
203 if ( jugador.getcasco() != -1 ){
204 jugador.pon_inventario(nsel,jugador.getcasco());
205 }else{
206 jugador.pon_inventario(nsel,0);
207 }
208
209
210 jugador.pon_casco(nid);
211 sonido_boton();
212
213 swraton = -2;
214 }
215 }
216 }
217 }
218
219 if ( jugador.getcasco() != -1 )
220 {
221 masked_blit( (BITMAP *)datobjetos[jugador.getcasco()].dat, buffer, 0,0,
222 jx+13,jy+40, 32,32);
223 }
224
225 int id;
226 for ( int i=0; i < 4; i++){
227 for ( int j=0; j < 3; j++){
228 int num = (j*4) + i ;
229 id = jugador.getinventario( num );
230 posx = jx + 172 + i*34;
231 posy = jy + 40 + j*34;
232 if ( id != 0 ){
233 masked_blit( (BITMAP *)datobjetos[id].dat, buffer, 0,0, posx,posy,
234 32,32);
235 }
236 if ( mouse_x > posx && mouse_x < posx+32 && mouse_y > posy && mouse_y <
237 posy+32 )
238 {
239 if ( mouse_b&1 && swraton != -2)
240 {
241 // se ha pulsado dentro de un hueco
242 if ( swraton == -1 )
243 {
244 // primer clic
245 if ( id != 0 )
246 {
247 swraton = 1;
248 nsel = num;
249 nid = id;
250 sonido_boton2();
251 }
252 }else{
253
254 if ( nsel >= 0 )
255 {
256 if ( nsel != num && nsel != -1)
257 {
258 // cambia objeto de sitio
259 jugador.cambia_inventario(nsel,num);
260 sonido_boton();
261 swraton = -2;
262 }
263 }else{
264
265 // ha quitado algo del ekipo
266 if ( nsel == -1 && (tipo(id) == 3 || id == 0) )
267 {
268 // armadura
269 jugador.pon_inventario( num, nid );
270 if ( id == 0 ) id = -1;
271 jugador.pon_armadura( id );
272 sonido_boton();
273 swraton = -2;
274 }
275 if ( nsel == -2 && (tipo(id) == 1 || id == 0) )
276 {
277 // arma
278 jugador.pon_inventario( num, nid );
279 if ( id == 0 ) id = -1;
280 jugador.pon_arma( id );
281 sonido_boton();
282 swraton = -2;
283 }
284 if ( nsel == -3 && (tipo(id) == 2 || id == 0) )
285 {
286 // arma
287 jugador.pon_inventario( num, nid );
288 if ( id == 0 ) id = -1;
289 jugador.pon_casco( id );
290 sonido_boton();
291 swraton = -2;
292 }
293
294 if ( nsel == -4 && (tipo(id) == 4 || id == 0) )
295 {
296 // arma
297 jugador.pon_inventario( num, nid );
298 if ( id == 0 ) id = -1;
299 jugador.pon_anillo( id );
300 sonido_boton();
301 swraton = -2;
302 }
303 }
304 }
305 }
306
307 }
if ( swraton == -2 && !mouse_b&1 ) swraton=-1;
}
}

if ( !key[KEY_I]) swinv = 2;
}
}

En el archivo main.cpp, se aade la funcion pintar_pantalla().


// Copiar el buffer a la pantalla del juego (screen)
void pintar_pantalla()
{

if ( swinv > 0 )
{
masked_blit( (BITMAP *)datosjuego[dicursor].dat, buffer, 0,0,mouse_x, mouse_y, 16,16);
}
if ( swinv > 0 && swraton > -1 )
{
masked_blit( (BITMAP *)datobjetos[nid].dat, buffer, 0,0,mouse_x, mouse_y, 32,32);
}

blit(buffer, screen, 0, 0, 0, 0, PANTALLA_ANCHO, PANTALLA_ALTO);


}

En esta funcin se ha aadido, que mediante la variable swinv se muestre la flecha del raton. Esto es para que cuando se muestre el
inventario, se muestre la flecha del raton y si se pulsa sobre un objeto se pinte el objeto donde esta el cursor.

En el inicio del main.cpp, se aade el include del nuevo archivo.


#include < allegro.h >
#include "datosjuego.h"
#include "global.h"
#include "audio.h"
#include "misobjetos.h"
#include "dialogos.h"
#include "players.h"
#include "mijuego.h"

En la funcin inicia_allegro(), se debe inicializar el raton que se aade justo despues de inicializar el teclado.
allegro_init();
install_timer();
install_keyboard();
install_mouse();

En el archivo npc.h, se cambia lo referente al objeto ya que se ha cambiado el formato y uso. En la funcin enemigo::herida(), se
elimina la siguiente linea:
int q = objetos[rand()%6].id;

Para ser sustituida por las siguientes:


int q2 = lobj.size();
int q = lobj[rand()%q2].id;

Ya que ahora se utiliza lobj para almacenar la lista de objetos.

Llegados a este puntos, solo nos faltan algunos archivos como son:

objetos.dat: contiene las nuevas imagenes de los objetos


objetos.txt: contiene informacin de los objetos (id, tipo, nombre, img).
datosjuego.dat contiene el material multimedia del juego ( imagenes, sonidos, etc ).
Haz clic aqui para descargar los objetos TXT y DAT en archivo RAR
Haz clic aqu para descargar el fichero datosjuego.dat en RAR.
Haz clic aqu para descargar el fichero datosjuego.h en RAR.

Crear juego RPG en C++ y Allegro 4 (22) Tiendas

Aqu esta una nueva entrega del curso crea tu juego RPG en C++ y Allegro. Dedicada a explicar como programar una tienda.

Objetivo
Se trata de crear un npc con el que se hable y te de opcin a comprar objetos o vender los objetos que tengamos en nuestro inventario.
En esta primera parte nos vamos a centrar en los objetos y sus caractersticas.
Programacin
Para crear la tienda debemos tener una lista de objetos. De cada objeto debemos tener un mnimo de informacin, el nombre, la
imagen, descripcin, etc. Todo cdigo referente a los objetos se pondr en el archivo misobjetos.h. Este archivo cambia mucho con
respecto al anterior, por ello he decidido poner todo su contenido.

Para almacenar estos objetos se crea una estructura llamada m_objetos.

?
1 struct m_objetos{
2 int nid;
3 int id;
4 int tipo;
5 char nombre[30];
6 char descripcion[255];
7 int img;
8 int precio;
9 };
10
11vector< m_objetos > lobj;
nid: es un nmero para distinguir a los objetos. De esta manera si queremos guardar un objeto en el inventario, solo se guarda este
dato. Ya que mediante este dato se puede acceder al resto de informacin.
id: es el id de la imagen almacenada en el archivo objetos.dat
tipo: Indica de que tipo de archivo se trata:

1. armas
2. cascos
3. armaduras
4. anillos
5. pocimas
6. otros

nombre: contiene el nombre del objeto, con un mximo de 30 caracteres.


descripcin: contiene la descripcion del objeto, teniendo un limite de 255 caracteres.
img: es el id de una imagen almacenada en el archivo objetos.dat, que ser utilizada en el caso de que sea un objeto equipable, es
decir, que el tipo sea entre 1 y 4.
precio: es el precio del objeto cuando se vende en la tienda.
En el archivo misobjetos.h, se pone las siguientes funciones:

?
1 int id_img_objeto( int id )
2 {
3 for(int i = 0; i < lobj.size(); i++)
4 {
5 if ( lobj[i].nid == id ) return lobj[i].id;
6 }
7 return 0;
8 }
9
10
11char* descripcion_objeto( int id )
12{
13 for(int i = 0; i < lobj.size(); i++)
14 {
15 if ( lobj[i].nid == id ) return lobj[i].descripcion;
16 }
17 return "";
18}
19
20char* nombre_objeto( int id )
21{
22 for(int i = 0; i < lobj.size(); i++)
23 {
24 if ( lobj[i].nid == id ) return lobj[i].nombre;
25 }
26 return "";
27}
28
29int precio_objeto( int id )
30{
31 for(int i = 0; i < lobj.size(); i++)
32 {
33 if ( lobj[i].nid == id ) return lobj[i].precio;
34 }
35 return 0;
36}
37
38
39int tipo( int id ){
40 for(int i = 0; i < lobj.size(); i++)
41 {
42 if ( lobj[i].nid == id ) return lobj[i].tipo;
43 }
44 return -1;
45}
46
47int equipo_personaje( int id ){
48 for(int i = 0; i < lobj.size(); i++)
49 {
50 if ( lobj[i].nid == id && lobj[i].tipo < 5 ) return lobj[i].img;
51 }
52 return -1;
53}
54
55void lee_objetos(){
56 char contenido[255];
57
58 m_objetos temp;
59
60 packfile_password(NULL);
61 PACKFILE *fichero;
62
63 fichero = pack_fopen("objetos.txt","r");
64
65 while ( !pack_feof(fichero) )
66 {
67 pack_fgets( contenido, 255, fichero);
68 temp.nid = atoi( contenido );
69
70 pack_fgets( contenido, 255, fichero);
71 temp.id = atoi( contenido );
72
73 pack_fgets( contenido, 255, fichero);
74 temp.tipo = atoi( contenido );
75
76 pack_fgets( contenido, 255, fichero);
77 strncpy( temp.nombre, contenido, 29 );
78 temp.nombre[30]='\0';
79
80 pack_fgets( temp.descripcion, 255, fichero);
81
82 pack_fgets( contenido, 255, fichero);
83 temp.img = atoi( contenido );
84
85 pack_fgets( contenido, 255, fichero);
86 temp.precio = atoi( contenido );
87
88 lobj.push_back( temp );
89 }
90
91 pack_fclose(fichero);
92};

La funcin id_img_objeto(), recibe un id y devuelve el id de la imagen almacenada.


La funcin descripcion_objeto(), recibe un id de objeto y devuelve su descripcin.
La funcin nombre_objeto(), recibe un id de objeto y devuelve el nombre.
La funcin precio_objeto(), recibe un id de objeto y devuelve el precio.
La funcin tipo(), recibe un id de objeto y devuelve el tipo.
La funcin equipo_personaje(), recibe un id de objeto y devuelve el id de la imagen si el objeto es equipable.

Todas estas funciones, actuan del mismo modo. Reciben un id del objeto y van recorriendo el vector en busca del objeto que tenga
dicho id, y devuelve lo que se solicita de dicho objeto.

La funcin lee_objetos(), accede al archivo objetos.txt y vuelca su contenido a la memoria almacenandola en la variable lobj, que
contendr la lista de objetos.

El archivo objetos.txt debe tener unas caracteristicas especiales, para que el programa funcione y no de ningun error.

Por cada objeto debe tener:


un entero que indique el id
un entero para indicar el id de la imagen del objeto
un entero para indicar el tipo el cual debe tener un valor entre el 1 y el 6.
una cadena de caracteres para indicar el nombre, a ser posible que no supere los 30 caracteres.
una cadena de caracteres para indicar la descripcin, con un mximo de 255 caracteres, en el caso de superar este valor el programa
fallar.
un entero para indicar el id de la imagen secundaria
un entero para indicar el precio del objeto.

Ejemplo de un objeto:

1
0
6
Margarita
flor del campo
0
1

A continuacin dejo el archivo que se utiliza para nuestro ejemplo, pero si uno quiere puede modificarlo para aadir mas objetos,
cambiar nombres, descripciones, precios, etc. Se recomienda que no se borren estos primeros objetos, ya que algunos son utilizados
por el jugador al empezar.

Haz clic aqui para descargar archivo objetos.txt en RAR.

Mediante la instruccion pack_fgets, se va obteniendo una linea del archivo. Y segun la linea que sea se realiza una operacin
diferente. Se considera que una linea no puede superar los 255 caracteres, en el caso de que en el archivo exista una linea supere este
valor el programa dar un error.
Cuando se lee el nombre, aunque se obtiene la linea entera de 255 caracteres, el programa guarda en el nombre solo los 29 primeros
caracteres e ignora los demas. El caracter 30 se le asigna el valor de fin de linea.
En la mayora de los casos aunque se lee una cadena de caracteres es necesario transformarlo en un formato numrico, para ellos se
utiliza el comando atoi(), que se encarga de transformar una cadena de caracteres a un entero.

Al inicio del archivo misobjetos.h se debe aadir la librera vector que se utiliza en nuestras funciones.
#include < vector >
using namespace std;

Si deseas tener el archivo misobjetos.h, puedes descargar el RAR haciendo clic aqui.

Aunque se ha variado la clase objeto, las imagenes no se han tocado, por tanto se mantiene el archivo objetos.dat del tema anterior.

Se crea un nuevo archivo llamado botones.h, que contendr todo lo necesario para crear botones de texto.
?
1 /* botones.h
2 F. Finalizado : 22-12-12
3 Autor: KodayGames
4
5 clase TBOTON
6 para crear texto como boton
7 */
8
9
10#ifndef _BOTONES_H_
11#define _BOTONES_H_
12
13#include < allegro.h >
14
15
16class TBOTON{
17 public:
18 void crear(const char* text, const FONT* bfuente);
19 void pinta(BITMAP* bfondo, int x, int y, int color1, int color2);
20 bool ratonb();
21
22 private:
23 int bx, by;
24 int bpulsa;
25 int bancho,balto;
26 const char* btext;
27 const FONT* bfuente;
28};
29
30// incializa las variables de la clase
31void TBOTON::crear(const char* text, const FONT* fuente)
32{
33 btext = text;
34 bpulsa = 0;
35 bfuente = fuente;
36 bancho = text_length(bfuente, btext);
37 balto = text_height(bfuente);
38};
39
40// muestra en pantalla el texto, sobre el fondo indicado, con la fuente indicada, en la posicion x,y
41// con el color1 cuando esta sealado y color2 cuando no
42void TBOTON::pinta(BITMAP* bfondo, int x, int y, int color1, int color2)
43{
44 bx=x;
45 by=y;
46
47 if (mouse_x >= bx && mouse_x < bx+bancho+1 && mouse_y >= by && mouse_y < by+balto+1)
48 {
49 textout_ex(bfondo, bfuente, btext, bx, by, color1, -1);
50 }else{
51 textout_ex(bfondo, bfuente, btext, bx, by, color2, -1);
52 }
53};
54
55// comprueba si el raton esta situado encima de nuestro texto y a pulsado el boton del raton
56bool TBOTON::ratonb()
57{
58 return (mouse_x >= bx && mouse_x <= bx+bancho && mouse_y >= by && mouse_y <= by+balto && mouse_b&1 );
59};
60
61#endif

La clase TBOTON se utiliza para poner un texto por pantalla, y que pueda ser pulsado con el raton.

Tiene tres funciones crear, pinta y ratonb.


crear inicializa la clase, requiere el texto a mostrar y la fuente (tipo de letra).
pinta muestra el texto sobre la imagen, en la posicion x,y. Si el raton esta encima del texto se muestra con el color1 y si no con el
color2.
ratonb se utiliza para saber si el raton esta situado encima del texto y se ha pulsado el boton del raton.

Haz clic aqui para descargar en RAR el archivo botones.h


Bueno esto es todo por ahora, aun no se ha empezado con las tiendas, pero se debe ir poco a poco. Este tema de las tiendas abarcar
mas de una entrega. Es por ello, que con esto nuevo que se acaba de hacer no se podr comprobar hasta que se acabe todo lo referente
a las tiendas.

Crear juego RPG en C++ y Allegro 4 (23) Tiendas II

Aqu esta una nueva entrega del curso crea tu juego RPG en C++ y Allegro. Continuamos con las tiendas.

Se crea un archivo nuevo llamado tiendas.h, donde irn todo lo referente a la tienda.

En el se crea una estructura llamada objetoventa, que contiene cuatro variables:


nid : para almacenar el id del objeto
id : almacena el id de la imagen del fichero dat
nombre: nombre del objeto.
precio: precio de venta.

Quedando de la siguiente forma:


struct objetoventa{
int nid;
// id imagen
int id;
char *nombre;
int precio;
};

vector vlista_tienda;

El vector vlista_tienda tendr almacenado la lista de objetos que tiene la tienda para vender.
// la lista de objetos para que no se haga muy lento
// se crea con la imagen, nombre, precio
void lee_lista_objetos(){
char contenido[255];

packfile_password(NULL);
PACKFILE *fichero;

objetoventa elemento;

fichero = pack_fopen(clista_tienda,"r");

while ( !pack_feof(fichero) )
{
pack_fgets( contenido, 255, fichero);
elemento.nid = atoi( contenido );

elemento.id = id_img_objeto( elemento.nid );

elemento.nombre = nombre_objeto( elemento.nid );


pack_fgets( contenido, 255, fichero);
elemento.precio = atoi( contenido );

vlista_tienda.push_back( elemento );
}

pack_fclose(fichero);
};

La funcin lee_lista_objetos(), lee un archivo que viene dado por la variable clista_tienda. Este archivo tiene por cada objeto dos datos
el id del objeto, y el precio de venta. Aunque en la lista vlista_tienda se almacenan cuatro datos, dos son cogidos del archivo que se
esta leyendo y los otros se obtiene de la lista de objetos lobj, mediante las funciones id_img_objeto() y nombre_objeto(). Finalmente
con el comando push_back() se aade la variable elemento al vector vlista_tienda.
void borra_lista_objetos(){

int it = 0;
int max = vlista_tienda.size();
while ( it < max )
{
it++;
vlista_tienda.pop_back();
}
}

Con esta funcin, se borra todo el contenido del vector vlista_tienda.


void lee_tienda(int n){
char contenido[255];

packfile_password(NULL);
PACKFILE *fichero;

fichero = pack_fopen("tiendas.txt","r");
int num = 0;
while ( !pack_feof(fichero) && num != n )
{
num++;

pack_fgets( contenido, 255, fichero);


ifondo_tienda = atoi( contenido );

pack_fgets( contenido, 255, fichero);


x_tienda = atoi( contenido );

pack_fgets( contenido, 255, fichero);


y_tienda = atoi( contenido );

pack_fgets( clista_tienda, 255, fichero);


}

pack_fclose(fichero);
borra_lista_objetos();
lee_lista_objetos();
};

La funcin lee_tienda(), mediante el parmetro "n" se lee la tienda numero "n", del fichero tiendas.txt. Esto se ha hecho as para que
se pueda tener variedad de tiendas, y sea fcil de crear.
El archivo tiendas.txt debe tener cuatro datos por cada tienda:
ifondo_tienda: contiene el id de la imagen de fondo de la tienda.
x_tienda, y_tienda: Es la posicin donde se quiere que se muestre la tienda, de este modo podemos ajustar el texto con respecto a la
imagen de fondo.
clista_tienda: Es el nombre del archivo que contiene la lista de objetos que se venden en dicha tienda.

Para nuestro ejemplo necesitaras tres archivos, el tiendas.txt que tiene la lista de tiendas, en nuestro caso dos tiendas. Y los archivos
list01.txt y list02.txt que contiene cada uno los objetos que se vende en cada una de las tiendas.

Para descargar el archivo RAR haz clic aqui.

void pinta_tienda()
{
if (muestra_tienda == true )
{
MENSAJE tdesc;
tdesc.crea("",font,
x_tienda+5, y_tienda+415,
x_tienda+485, y_tienda+500);

if (swtienda == 0)
{
// muestra flecha e inicia a cero
swtienda = 1;
inicia_objeto = 0;
moviendo = 0;
tienda_estado = 1;
swinv = 0;
sel_obj = -1;
}

TBOTON comprar, vender, salir;


TBOTON descripcion, precios;

comprar.crear("Comprar",(FONT *)datosjuego[dftextos].dat );
vender.crear("Vender",(FONT *)datosjuego[dftextos].dat );
salir.crear("Salir",(FONT *)datosjuego[dftextos].dat );
descripcion.crear("Descripcion",(FONT *)datosjuego[dftextos].dat );
precios.crear("Precio",(FONT *)datosjuego[dftextos].dat );

masked_blit( (BITMAP*)datotiendas[ifondo_tienda].dat, buffer,


0,0,0,0, PANTALLA_ANCHO, PANTALLA_ALTO);

rect(buffer, x_tienda, y_tienda, x_tienda+490, y_tienda+372,0xffffff );

comprar.pinta( buffer, x_tienda+10, y_tienda+374, 0xffff00, 0xffffff );


vender.pinta( buffer, x_tienda+125, y_tienda+374, 0xffff00, 0xffffff );
salir.pinta( buffer, x_tienda+230, y_tienda+374, 0xffff00, 0xffffff );
descripcion.pinta( buffer, x_tienda+34, y_tienda, 0xffff00, 0xffffff );
precios.pinta( buffer, x_tienda+390, y_tienda, 0xffff00, 0xffffff );
textprintf_ex( buffer, (FONT *)datosjuego[dftextos].dat, x_tienda+305, y_tienda+374, 0xFFFFFF, -1,
"Dinero %9d ",
jugador.getdinero() );

if ( tienda_estado == 1 )
{

if ( vlista_tienda.size() > 10 )
{

rect(buffer, x_tienda+470, y_tienda, x_tienda+490, y_tienda+372,0xffffff );


rect(buffer, x_tienda+470, y_tienda+30, x_tienda+490, y_tienda+342,0xffffff );
triangle( buffer, x_tienda+473,y_tienda+25, x_tienda+487,y_tienda+25,
x_tienda+480,y_tienda+5,0xffffff );
triangle( buffer, x_tienda+473,y_tienda+347, x_tienda+487,y_tienda+347,
x_tienda+480,y_tienda+367,0xffffff );

// barra central
// distancia unidad
float ny1 = ( 340.0 - 32.0 ) / vlista_tienda.size() ;
// parte superior por donde se inicia
int ny2 = int(inicia_objeto * ny1);
// siempre se muestran 10 - longitud barra
int ny3 = int(10.0 * ny1);

rectfill(buffer, x_tienda+472, y_tienda+32+ny2, x_tienda+488, y_tienda+32+ny3+ny2, 0xffffff


);

if ( mouse_x > x_tienda+460 && mouse_x < x_tienda+490 &&


mouse_y > y_tienda && mouse_y < y_tienda+30)
{
// esta situado encima de boton
triangle( buffer, x_tienda+473,y_tienda+25, x_tienda+487,y_tienda+25,
x_tienda+480,y_tienda+5,0xffff00 );
if ( mouse_b&1 )
{
// pulsa hacia arriba
inicia_objeto--;
if (inicia_objeto < 0 ) inicia_objeto = 0;
}
}

if ( mouse_x > x_tienda+460 && mouse_x < x_tienda+490 &&


mouse_y > y_tienda+342 && mouse_y < y_tienda+372)
{
// esta situado encima de boton
triangle( buffer, x_tienda+473,y_tienda+347, x_tienda+487,y_tienda+347,
x_tienda+480,y_tienda+367,0xffff00 );
if ( mouse_b&1 )
{
// pulsa hacia arriba
inicia_objeto++;
if (inicia_objeto+10 > vlista_tienda.size() ) inicia_objeto--;
}
}
}

for ( int it=inicia_objeto; it < vlista_tienda.size() && it < inicia_objeto+10; it++ )


{
int ty = y_tienda+((it+1)*34) - (inicia_objeto*34);
masked_blit( (BITMAP*)datobjetos[vlista_tienda[it].id].dat, buffer,
0,0,x_tienda,ty, 32,32);

textprintf_ex( buffer, (FONT *)datosjuego[dftextos].dat, x_tienda+34, ty, 0xFFFFFF, -1, "%s",


vlista_tienda[it].nombre );

textprintf_ex( buffer, (FONT *)datosjuego[dftextos].dat, x_tienda+367, ty, 0xFFFFFF, -1,


"%8d", vlista_tienda[it].precio );

int numi = it - inicia_objeto;

if ( mouse_x > x_tienda+1 && mouse_x < x_tienda+470 &&


mouse_y > y_tienda+32+(numi*34) && mouse_y < y_tienda+64+(numi*34))
{
rect(buffer, x_tienda+2, y_tienda+34+(numi*34),
x_tienda+468, y_tienda+63+(numi*34),0xffff00 );

tdesc.cambia_texto( descripcion_objeto(vlista_tienda[it].nid) );
tdesc.pinta(buffer);
textprintf_ex( buffer, (FONT *)datosjuego[dftextos].dat, x_tienda+274, y_tienda,
0xFFFFFF, -1,
"Tienes:%2d", jugador.cuantos_objetos(vlista_tienda[it].nid) );

if ( mouse_b&1 )
{
// intenta comprar objeto
// comprobar que existe hueco para la compra
// tiene dinero suficiente ?
if ( jugador.num_inventario() < 12 &&
jugador.getdinero() > vlista_tienda[it].precio )
{
sel_obj = it;
}else{
if ( swerror == 0 )
{
swerror = 1;
}
}
}// fin se pulso el raton
}// fin dentro de un objeto

if ( swerror == 2 ) swerror=0;
if ( swerror == 1 && !mouse_b&1)
{
sonido_error();
swerror=2;
}

if ( sel_obj != -1 && !mouse_b&1 )


{
jugador.menosdinero( vlista_tienda[sel_obj].precio );
jugador.obtiene_objeto( vlista_tienda[sel_obj].nid );
sel_obj = -1;
sonido_boton();
}

}
// fin compras

//inicia ventas
if ( tienda_estado == 2 )
{
int obj_tienes = jugador.num_inventario();

if ( obj_tienes == 0 )
{
textprintf_ex( buffer, (FONT *)datosjuego[dftextos].dat,
x_tienda+75, y_tienda+185, 0xFFFFFF, -1, "No tienes objetos en tu inventario" );
}

if ( obj_tienes > 10 )
{
//pinta barra desplazamiento
rect(buffer, x_tienda+470, y_tienda, x_tienda+490, y_tienda+372,0xffffff );
rect(buffer, x_tienda+470, y_tienda+30, x_tienda+490, y_tienda+342,0xffffff );
triangle( buffer, x_tienda+473,y_tienda+25, x_tienda+487,y_tienda+25,
x_tienda+480,y_tienda+5,0xffffff );
triangle( buffer, x_tienda+473,y_tienda+347, x_tienda+487,y_tienda+347,
x_tienda+480,y_tienda+367,0xffffff );

// barra central
// distancia unidad
float ny1 = ( 340.0 - 32.0 ) / obj_tienes ;
// parte superior por donde se inicia
int ny2 = int(inicia_objeto * ny1);
// siempre se muestran 10 - longitud barra
int ny3 = int(10.0 * ny1);

rectfill(buffer, x_tienda+472, y_tienda+32+ny2, x_tienda+488, y_tienda+32+ny3+ny2, 0xffffff


);

if ( mouse_x > x_tienda+470 && mouse_x < x_tienda+490 &&


mouse_y > y_tienda && mouse_y < y_tienda+30)
{
// esta situado encima de boton
triangle( buffer, x_tienda+473,y_tienda+25, x_tienda+487,y_tienda+25,
x_tienda+480,y_tienda+5,0xffff00 );
if ( mouse_b&1 )
{
// pulsa hacia arriba
inicia_objeto--;
if (inicia_objeto < 0 ) inicia_objeto = 0;
}
}

if ( mouse_x > x_tienda+460 && mouse_x < x_tienda+490 &&


mouse_y > y_tienda+342 && mouse_y < y_tienda+372)
{
// esta situado encima de boton
triangle( buffer, x_tienda+473,y_tienda+347, x_tienda+487,y_tienda+347,
x_tienda+480,y_tienda+367,0xffff00 );
if ( mouse_b&1 )
{
// pulsa hacia arriba
inicia_objeto++;
if (inicia_objeto+10 > obj_tienes ) inicia_objeto= obj_tienes - 10;
}
}

}
int it = 0;
for ( int i=0; i < 12; i++ )
{
if ( jugador.getinventario(i) != 0 &&
it < 10+inicia_objeto && it >= inicia_objeto)
{
int mid = jugador.getinventario(i);
int ty = y_tienda+((it+1)*34) - (inicia_objeto*34);

masked_blit( (BITMAP*)datobjetos[id_img_objeto(mid)].dat, buffer,


0,0,x_tienda,ty, 32,32);

textprintf_ex( buffer, (FONT *)datosjuego[dftextos].dat, x_tienda+34, ty, 0xFFFFFF, -1, "%s",


nombre_objeto(mid) );

textprintf_ex( buffer, (FONT *)datosjuego[dftextos].dat, x_tienda+367, ty, 0xFFFFFF, -1,


"%8d", precio_objeto(mid) );

int numi = it - inicia_objeto;

if ( mouse_x > x_tienda+1 && mouse_x < x_tienda+470 &&


mouse_y > y_tienda+32+(numi*34) && mouse_y < y_tienda+64+(numi*34))
{
rect(buffer, x_tienda+2, y_tienda+34+(numi*34),
x_tienda+468, y_tienda+63+(numi*34),0xffff00 );

tdesc.cambia_texto( descripcion_objeto(jugador.getinventario(i)) );
tdesc.pinta(buffer);

if ( mouse_b&1 )
{
// vende objeto
sel_obj = i;
}// fin se pulso el raton
}// fin dentro de un objeto

it++;
}else{

if ( jugador.getinventario(i) != 0 && it < inicia_objeto) it++;


}
}

if ( sel_obj != -1 && !mouse_b&1 )


{

jugador.masdinero( precio_objeto(jugador.getinventario(sel_obj)) );
// objeto con id cero no existe
jugador.pon_inventario( sel_obj, 0);
sel_obj = -1;
sonido_boton();
obj_tienes = jugador.num_inventario();

if ( obj_tienes == 11 ) inicia_objeto = 1;
if ( inicia_objeto > 0 && obj_tienes < 11 ) inicia_objeto=0;

}
// fin ventas

if ( comprar.ratonb() && tienda_estado != 1)


{
tienda_estado = 1;
inicia_objeto = 0;
sel_obj = -1;
sonido_boton();
}
if ( vender.ratonb() && tienda_estado != 2)
{
tienda_estado = 2;
inicia_objeto = 0;
sel_obj = -1;
sonido_boton();
}

if ( salir.ratonb() ){
// oculta la flecha y sale
swtienda = 0;
muestra_tienda = false;
sonido_boton();
}
}
}

La funcin pinta_tienda(), es la que se encarga de todo referente a la tienda.


Primero se comprueba si se muestra la tienda. Se crea una variable tipo MENSAJE que se encarga de mostrar la descripcin de los
objetos. Cuando entra la primera vez swtienda vale cero y por tanto se inicializan todas la variables utilizadas en la funcin tienda. Se
inicializan a cero las variables inicia_objeto, swinv, Y se inicializa a 1 la variable tienda_estado. La variable swtienda pasa a valer 1,
para que la prxima vez que entre no se vuelvan a inicializar las variables. Y la variable sel_obj se inicializa a -1, para indicar que no
hay ninguno seleccionado.

Se crean tres botones, comprar, vender y salir. Que se declaran del tipo TBOTON. Al igual que las variables descripcin, y precios. Y
se inicializan utilizando la funcin crear, que definen el texto que tienen y el tipo de letra que se utilizar.

Mediante la variable tienda_estado, se consideran dos posibles valores: 1 para cuando se muestra el listado de comprar, y 2 para
cuando se muestra nuestros objetos para vender.

En el caso de que se tengan que mostrar mas de 10 objetos entonces se pinta la barra de desplazamiento, que nos servir para movernos
por la lista de objetos.

Todo se muestra con una posicin relativa a las variables x_tienda, y_tienda. Para que se pueda situar donde queramos con tan solo
cambiar el valor de estas variables.

En la opcin comprar, la lista a mostrar es vlista_tienda. Y en la opcin de venta, la lista que se muestra es el inventario del jugador.

Si deseas descargar el archivo tiendas.h en RAR haz clic aqui.

Y hasta aqui hemos llegado por hoy, aun no ha acabado esto de la tienda. Aunque ya tenemos el codigo principal, hay que intergrarlo
en el juego.

Crear juego RPG en C++ y Allegro 4 (24) Tiendas III

Aqu esta una nueva entrega del curso crea tu juego RPG en C++ y Allegro. Continuamos con las tiendas.
En la entrega anterior se puso la funcin pinta_tienda(), y se explic un poco su funcionamiento. Ahora iremos explicando algunas
partes del cdigo.
rect(buffer, x_tienda+470, y_tienda, x_tienda+490, y_tienda+372,0xffffff );
rect(buffer, x_tienda+470, y_tienda+30, x_tienda+490, y_tienda+342,0xffffff );
triangle( buffer, x_tienda+473,y_tienda+25, x_tienda+487,y_tienda+25,
x_tienda+480,y_tienda+5,0xffffff );
triangle( buffer, x_tienda+473,y_tienda+347, x_tienda+487,y_tienda+347,
x_tienda+480,y_tienda+367,0xffffff );

Este cdigo se encarga de dibujar la barra de desplazamiento, dibujando dos rectngulos y dos tringulos. El comando rect recibe una
imagen que es en la que se va a dibujar el rectngulo, y dos posiciones para indicar las dimensiones del rectngulo dando la esquina
superior izquierda, y la esquina inferior derecha. Y finalmente se indica el color en el que se pinta.
El comando triangle funciona de manera similar solo que ahora pide tres posiciones, las tres esquinas del tringulo.
// distancia unidad
float ny1 = ( 340.0 - 32.0 ) / vlista_tienda.size() ;
// parte superior por donde se inicia
int ny2 = int(inicia_objeto * ny1);
// siempre se muestran 10 - longitud barra
int ny3 = int(10.0 * ny1);

Estas variables contienen informacin para poder pintar la barra interior que va moviendose dentro de la barra de desplazamiento.
ny1: contiene el tamao total de la barra de desplazamiento dividido por la cantidad de objetos que se van a mostrar.
ny2: contiene donde se va a situar la barra, segn el elemento mostrado que se indica en inicia_objeto.
ny3: contiene el tamao de la barra. Como en el espacio que tenemos solo se puede mostrar 10 objetos, por eso se multiplica por 10.
if ( mouse_x > x_tienda+460 && mouse_x < x_tienda+490 &&
mouse_y > y_tienda && mouse_y < y_tienda+30)
{
// esta situado encima de boton
triangle( buffer, x_tienda+473,y_tienda+25, x_tienda+487,y_tienda+25,
x_tienda+480,y_tienda+5,0xffff00 );
if ( mouse_b&1 )
{
// pulsa hacia arriba
inicia_objeto--;
if (inicia_objeto < 0 ) inicia_objeto = 0;
}
}

if ( mouse_x > x_tienda+460 && mouse_x < x_tienda+490 &&


mouse_y > y_tienda+342 && mouse_y < y_tienda+372)
{
// esta situado encima de boton
triangle( buffer, x_tienda+473,y_tienda+347, x_tienda+487,y_tienda+347,
x_tienda+480,y_tienda+367,0xffff00 );
if ( mouse_b&1 )
{
// pulsa hacia abajo
inicia_objeto++;
if (inicia_objeto+10 > vlista_tienda.size() ) inicia_objeto--;
}
}

Estas dos condiciones se encargan de controlar los dos botones de la barra de desplazamiento, la primera para el botn de arriba y la
segunda para el de abajo. Se controla si esta encima de la imagen del botn y si se ha pulsado. Dependiendo de cual pulse se aade o se
resta a la variable inicia_objeto, esta variable indica en que objeto se empieza a mostrar.
for ( int it=inicia_objeto; it < vlista_tienda.size() && it < inicia_objeto+10; it++ )
{
int ty = y_tienda+((it+1)*34) - (inicia_objeto*34);
masked_blit( (BITMAP*)datobjetos[vlista_tienda[it].id].dat, buffer,
0,0,x_tienda,ty, 32,32);

textprintf_ex( buffer, (FONT *)datosjuego[dftextos].dat, x_tienda+34, ty, 0xFFFFFF, -1, "%s",


vlista_tienda[it].nombre );

textprintf_ex( buffer, (FONT *)datosjuego[dftextos].dat, x_tienda+367, ty, 0xFFFFFF, -1,


"%8d", vlista_tienda[it].precio );
int numi = it - inicia_objeto;

if ( mouse_x > x_tienda+1 && mouse_x < x_tienda+470 &&


mouse_y > y_tienda+32+(numi*34) && mouse_y < y_tienda+64+(numi*34))
{
rect(buffer, x_tienda+2, y_tienda+34+(numi*34),
x_tienda+468, y_tienda+63+(numi*34),0xffff00 );

tdesc.cambia_texto( descripcion_objeto(vlista_tienda[it].nid) );
tdesc.pinta(buffer);
textprintf_ex( buffer, (FONT *)datosjuego[dftextos].dat, x_tienda+274, y_tienda,
0xFFFFFF, -1,
"Tienes:%2d", jugador.cuantos_objetos(vlista_tienda[it].nid) );

if ( mouse_b&1 )
{
// intenta comprar objeto
// comprobar que existe hueco para la compra
// tiene dinero suficiente ?
if ( jugador.num_inventario() < 12 &&
jugador.getdinero() > vlista_tienda[it].precio )
{
sel_obj = it;
}else{
if ( swerror == 0 )
{
swerror = 1;
}
}
}// fin se pulso el raton
}// fin dentro de un objeto

Con este bucle for se van mostrando los 10 posibles objetos que se pueden comprar. La variable ty contiene la posicin absoluta donde
se irn pintando cada una de las lineas. Con el comando masked_blit se pinta la imagen del objeto. Con los dos textprintf_ex se
muestran el nombre y el precio del objeto. La variable numi se utiliza para obtener un valor que vaya de 0 a 10.
La condicin comprueba si el ratn esta situado encima de alguno de los objetos, en ese caso se pinta un rectngulo que rodea al objeto
y muestra la descripcin del objeto utilizando la variable tdesc.
En el caso de que se haga clic se comprueba que se tenga hueco en el inventario y dinero para comprarlo, en el caso que se pueda
sel_obj toma el valor de it, que nos indica que objeto se a comprado.

Todo esto se repite para la venta de objetos que tiene el jugador en el inventario, cambiando la lista de objetos que se mira, ahora se
comprueba todos los objetos que se tiene en el inventario.
if ( comprar.ratonb() && tienda_estado != 1)
{
tienda_estado = 1;
inicia_objeto = 0;
sel_obj = -1;
sonido_boton();
}

if ( vender.ratonb() && tienda_estado != 2)


{
tienda_estado = 2;
inicia_objeto = 0;
sel_obj = -1;
sonido_boton();
}

if ( salir.ratonb() ){
// oculta la flecha y sale
swtienda = 0;
muestra_tienda = false;
sonido_boton();
}

Estas tres condiciones, se encarga de comprobar si se pulsa alguno de los botones, y realiza la accin correspondiente, ya sea cambiar a
la ventana de compra, a la de venta o salir de la tienda. Espero que con esto haya quedado un poco mas claro en funcionamiento de la
funcin.

Seguimos con los cambios o aadidos que se deben hacer al cdigo para tener operativo la tienda.
En el archivo audio.h, se aade un nuevo sonido.
void sonido_error(){
play_sample ( (SAMPLE *)datosjuego[dserror].dat, 110,128, 1000, 0 );
}

En el archivo global.h se declara la nueva variable datotiendas.


DATAFILE *datotiendas;

int swtienda;
int sel_obj;
int swerror;

En el archivo mijuego.h. se quita la declaracin del jugador, y se pone en el archivo players.h, despus de la definicin de la clase.
player jugador;

En la funcin carga_juego(), se aade lo siguiente, justo despus de cargar los otros archivos DAT.
datotiendas = load_datafile("datostienda.dat");
if ( !datobjetos ){
allegro_message("Error: archivo datostienda.dat no encontrado\n%s\n", allegro_error);
}

Y se modifica la cantidad de NPC, y enemigos, y se inicializan las nuevas variables.


cambio = 0;
npersonaje = 10;

personajes[0].crea( (BITMAP *)datosjuego[diper001].dat, 1300,700, 1,1,3);


personajes[1].crea( (BITMAP *)datosjuego[diper005].dat, 280, 450, 0,2,3);
personajes[2].crea( (BITMAP *)datosjuego[diper005].dat, 230, 280, 3,2,3);
personajes[3].crea( (BITMAP *)datosjuego[diper003].dat, 960, 310, 2,3,3);
personajes[4].crea( (BITMAP *)datosjuego[diper005].dat, 1120, 450, 0,4,3);
personajes[5].crea( (BITMAP *)datosjuego[diper004].dat, 900, 650, 1,5,3);
personajes[6].crea( (BITMAP *)datosjuego[diper006].dat, 850, 800, 0,0,3);
personajes[7].crea( (BITMAP *)datosjuego[diper001].dat, 530, 280, 1,5,3);
personajes[8].crea( (BITMAP *)datosjuego[diper007].dat, 334, 170, 0,0,4);
personajes[9].crea( (BITMAP *)datosjuego[diper008].dat, 142, 170, 0,0,4);

nmalos = 3;
malos[0].crea( (BITMAP *)datosjuego[diene001].dat, 380, 280, 3,5,2,100);
malos[1].crea( (BITMAP *)datosjuego[diene001].dat, 400, 720, 0,5,2,100);
malos[2].crea( (BITMAP *)datosjuego[diene001].dat, 380, 240, 0,5,2,100);

texto.crea("Demo tiendas. Ejemplo del Curso Crea tu juego RPG en C++ y Allegro ",
font, 5,5,230,60 );

dialogo.crea("", (FONT *)datosjuego[dftextos].dat, 10, PANTALLA_ALTO-100, PANTALLA_ANCHO-10,


PANTALLA_ALTO-10);
hablando = 0;

mision = 1;
swraton=-1;

swinv=0;
muestra_tienda = false;
swtienda=0;

Este cdigo sustituye al anterior que va desde la inicializacin a cero de cambio hasta el final de la funcin.

En la funcin carga_escenario(), se aade el nuevo escenario de la tienda.


case 4:// tienda1
fondo = (BITMAP *)datosjuego[ditienda1].dat;
choque = (BITMAP *)datosjuego[ditienda1choque].dat;
cielo = (BITMAP *)datosjuego[ditienda1sup].dat;

desplaza=false;
sonido_abrirpuerta();
break;
Se aade un nuevo case, para el escenario de la tienda. Para indicar las imagenes del nuevo escenario.

En la funcin cambia_escenario(), se sustituye el cdigo a partir del case 3, hasta el final de la funcin, incluida el cierre de la llave (
} ).
case 3: // ciudad
if ( cambio == 1 )
{
// cambiamos a otro lugar
// bosque
lugar = 2;
carga_escenario();
// situamos al prota en el camino del bosque
jugador.posiciona( 650,30 );
desplazamiento_map_x=200;
desplazamiento_map_y=0;
cambio = 0;
}
// color amarillo que existen muchos
if ( cambio == 3 && desplazamiento_map_x > 800 )
{
// cambiamos a otro lugar
// tienda1
lugar = 4;
carga_escenario();
// situamos al prota en el camino del bosque
jugador.posiciona( 376,460 );
desplazamiento_map_x=-170;
desplazamiento_map_y=-100;

cambio = 0;
}
break;
case 4: // tienda1
if ( cambio == 1)
{
// cambiamos a la ciudad
lugar=3;
carga_escenario();
jugador.posiciona( 400,300 );
desplazamiento_map_x=1090;
desplazamiento_map_y=85;
cambio = 0;
sonido_abrirpuerta();
}

default:
break;
}
}

Estos cambios, son para que desde la ciudad se pueda ir a la tienda el nuevo escenario, y desde la tienda se pueda volver a la ciudad.
Todas las posiciones del personaje y desplazamiento del mapa depende del mapa, y solo son vlido para nuestros mapas.
Recuerda que si deseas aadir nuevos mapas, estos datos de posicin del personaje y desplazamiento debe calcularlo uno mismo segn
las imagenes de los escenarios.

En la funcin evento_escenario(), se aade un nuevo case, para controlar los eventos que hay dentro de la tienda.
case 4: // en la tienda

if ( personajes[8].getestado() == 6 && cambio == 0 && !personajes[8].posicion_cerca())


{
personajes[8].cambia_estado(0);
}
if ( personajes[9].getestado() == 6 && cambio == 0 && !personajes[9].posicion_cerca())
{
personajes[9].cambia_estado(0);
}

if ( cambio == 2 && jugador.getx()< 400 )


{
personajes[9].cambia_estado(6);
cambio = 0;
}
if ( cambio == 2 && jugador.getx()> 400 )
{
personajes[8].cambia_estado(6);
cambio = 0;
}

if ( personajes[8].posicion_cerca() )
{
personajes[8].cambia_estado(6);
}

if ( personajes[9].posicion_cerca() )
{
personajes[9].cambia_estado(6);
}

if ( personajes[8].posicion_cerca(9) && personajes[8].alineado_vertical()


&& jugador.accion() && !personajes[8].posicion_cerca(2) )
{
lee_tienda(2);
muestra_tienda = true;
}

if ( personajes[9].posicion_cerca(9) && personajes[9].alineado_vertical()


&& jugador.accion() && !personajes[9].posicion_cerca(2) )
{
lee_tienda(1);
muestra_tienda = true;
}

if ( personajes[8].frente() && jugador.accion() &&


!jugador.hablando() && personajes[8].posicion_cerca())
{
dialogo.cambia_texto(" Y tu que estas mirando!! " );
hablando = 1;
}
if ( personajes[9].frente() && jugador.accion() &&
!jugador.hablando() && personajes[9].posicion_cerca())
{
dialogo.cambia_texto(" Vienes a comprar ? " );
hablando = 1;
}

if ( hablando == 1 && !jugador.accion() )


{
hablando = 2;
jugador.habla();
}

// obliga a esperar minimo 1 segundo


if ( hablando > FRAME_RATE && jugador.accion() ){
hablando = 0;
}

if ( hablando == 0 && !jugador.accion() && jugador.hablando() )


{
jugador.no_habla();
}

break;

Esto de los eventos de los personajes, se puede mejorar pero por el momento lo dejamos as. En este cdigo anterior se controla los dos
personajes que hay en la tienda, si nos colocamos delante de ellos detrs del mostrador se abrir la tienda, en el caso de que nos
acerquemos a ellos nos dirn unas frases.

En la funcin pinta_juego(), al igual que en otras funciones se aade un nuevo case, para la tienda.
case 4: // tienda1
bx = 170;
by = 100;
ancho = 448;
alto = 416;
break;

Tambin se tiene que aadir la llamada a la funcin pinta_tienda(), justo despus de pinta_inventario(), para que de este modo la
tienda lo oculte todo.

En el archivo players.h, en la clase player se aade una nueva variable privada:


int dinero;

Y se aaden nuevas funciones


int getdinero(){ return dinero; };
void masdinero(int n){ dinero = dinero + n; };
void menosdinero(int n){ dinero = dinero - n; };
// devuelve cuantos objetos tienes en el inventario
int num_inventario();
int cuantos_objetos(int id);

La funcin player::cuantos_objetos(), se encarga de recorrer todo el vector inventario y devuelve cuantos objetos hay en el inventario
con el mismo id. Y la funcion player::num_inventario() devuelve la cantidad de objetos que hay en el inventario.
int player::cuantos_objetos(int id)
{
int n=0;
for ( int i=0; i < 12; i++)
{
if ( inventario[i] == id ) n++;
}
return n;
}

int player::num_inventario()
{
int n=0;
for ( int i=0; i < 12; i++)
{
if ( inventario[i] != 0 ) n++;
}
return n;
}

En la funcin player::inicia(), se inicializa la nueva variable precio.


dinero = 50000;

En el archivo npc.h, en la clase npc se aaden nuevas funciones.


bool posicion_cerca(int num=0);
void cambia_estado(int _estado){ estado = _estado; };
int getestado(){ return estado; };
bool frente();
bool alineado_vertical();

Y a continuacin tienen las nuevas funcines:


bool npc::alineado_vertical(){
int jx = jugador.getx() + desplazamiento_map_x;
int jy = jugador.gety() + desplazamiento_map_y;

return ( y+desplazamiento*2 < jy && abs(jx-x) <= desplazamiento*2 );


}

bool npc::frente()
{
int jx = jugador.getx() + desplazamiento_map_x;
int jy = jugador.gety() + desplazamiento_map_y;

int d = jugador.dire();

if ( jx > x )
{
if ( abs ( jy - y ) < desplazamiento*2 )
{
if ( d == 1 )
{
return true;
}else{
return false;
}
}
}

if ( jx < x )
{
if ( abs ( jy - y ) < desplazamiento*2 )
{
if ( d == 2 )
{
return true;
}else{
return false;
}
}
}

if ( jy < y )
{
if ( abs ( jx - x ) < desplazamiento*2 )
{
if ( d == 0 )
{
return true;
}else{
return false;
}
}
}

if ( jy > y )
{
if ( abs ( jx - x ) < desplazamiento*2 )
{
if ( d == 3 )
{
return true;
}else{
return false;
}
}
}

return false;
}

// num distancia considerada cercana en pasos


bool npc::posicion_cerca(int num)
{
int _x = jugador.getx() + desplazamiento_map_x;
int _y = jugador.gety() + desplazamiento_map_y;
int d = 32 + (desplazamiento*(2+num));
int d2 =abs ( _x - x ) + abs ( _y - y );
return d2 <= d && lugar == escena ;
}

Si todo se ha copiado correctamente, solo nos falta aadir las nuevas imagenes, sonidos, etc.

Esta vez hacen faltan tres archivos DAT, el datosjuego, datostienda, y objetos.

Haz clic aqu para descargar el RAR del archivo datosjuego.


Haz clic aqu para descargar el RAR del archivo datosjuego.h
Haz clic aqu para descargar el RAR del archivo datostienda.
Haz clic aqu para descargar el RAR del archivo objetos.
Crear juego RPG en C++ y Allegro 4 (25) Guardar Partida

Aqu esta una nueva entrega del curso crea tu juego RPG en C++ y Allegro. Hoy se har para guardar la partida.

Guardar la partida es guardar todos los valores necesarios, para que despus se pueda carga la partida y continuar por donde lo hemos
dejado.

Por un lado se tiene que guardar todos los datos de nuestro personaje, como son las coordenadas x,y, vida, experiencia, nivel, etc.

Por otro lado todos los datos referente al escenario donde nos encontramos, en este caso se guarda la variable lugar,
desplazamiento_map_x y desplazamiento_map_y. Que son necesarios para situar a nuestro personaje.

Tambin sera necesario guardar todo lo referente a la misin actual, y misiones realizadas. El problema es que aun no se ha
implementado esto. Asi que es una cosa a tener en cuenta en un futuro.

Tambin hay que guardar el valor de todas la variables globales, para poder volver al estado en el que estbamos cuando se cargue la
partida.

Una duda que se me plantea es como se quiere que se guarde la partida. Que el jugador tenga que ir a un punto concreto para guardar
la partida, o que en cualquier momento pueda acceder al men y guardar la partida.

De la primera forma, nos aseguramos de que no este en ningn sitio o estado comprometido. De la segunda forma esto no pasa, debera
de controlarse de que no pueda guardar partida mientras lucha o cuando esta en medio de un dialogo.

Todo lo referente a cargar y guardar partida se pondr en un nuevo archivo llamado partidas.h.

Dicho todo esto pasamos a la implementacin de la funcin


void guarda_partida(){
char contenido[255];

packfile_password("kodaygames2015");
PACKFILE *fichero;

fichero = pack_fopen("partida.sav","w");

Inicializamos las variables que vamos a utilizar, contenido va a almacenar los datos antes de volcarlo al archivo. Se le indica la
contrasea que tendr el archivo mediante el comando packfile_password, de esta manera nadie podr ver lo que contiene, y mucho
menos modificarlo. Se crea una variable del tipo PACKFILE llamada fichero, que se utilizar para manejar el archivo que se va a
guardar.
El comando pack_fopen("partida.sav","w"); se utiliza para abrir el archivo partida.sav en modo escritura, por tanto sobre escribe todo
lo anterior.
// datos del jugador

pack_fputs(itoa(jugador.getx(),contenido,10), fichero);
pack_fputs("\n", fichero);

pack_fputs(itoa(jugador.gety(),contenido,10), fichero);
pack_fputs("\n", fichero);

pack_fputs(itoa(jugador.getvida(),contenido,10), fichero);
pack_fputs("\n", fichero);

pack_fputs(itoa(jugador.getvidamax(),contenido,10), fichero);
pack_fputs("\n", fichero);

pack_fputs(itoa(jugador.getexp(),contenido,10), fichero);
pack_fputs("\n", fichero);

pack_fputs(itoa(jugador.getnivel(),contenido,10), fichero);
pack_fputs("\n", fichero);

pack_fputs(itoa(jugador.getdinero(),contenido,10), fichero);
pack_fputs("\n", fichero);
pack_fputs(itoa(jugador.getcasco(),contenido,10), fichero);
pack_fputs("\n", fichero);

pack_fputs(itoa(jugador.getarmadura(),contenido,10), fichero);
pack_fputs("\n", fichero);

pack_fputs(itoa(jugador.getarma(),contenido,10), fichero);
pack_fputs("\n", fichero);

pack_fputs(itoa(jugador.getanillo(),contenido,10), fichero);
pack_fputs("\n", fichero);

for (int i=0; i < 12; i++)


{
pack_fputs(itoa(jugador.getinventario(i),contenido,10), fichero);
pack_fputs("\n", fichero);
}

Se va guardando en cada linea un dato, primero la posicin del jugador x,y, luego la vida, vidamax, experiencia, nivel, dinero, lo que
lleva equipado casco, armadura, arma y anillo. Y finalmente se guarda todo lo que tenga en el inventario. Despus de cada dato se
almacena un salto de linea "\n", para que de este modo solo haya un dato por linea, para facilitar luego su lectura.
// datos del escenario

pack_fputs(itoa(lugar,contenido,10), fichero);
pack_fputs("\n", fichero);

pack_fputs(itoa(desplazamiento_map_x,contenido,10), fichero);
pack_fputs("\n", fichero);

pack_fputs(itoa(desplazamiento_map_y,contenido,10), fichero);
pack_fputs("\n", fichero);

// npc y enemigos

pack_fputs(itoa(npersonaje,contenido,10), fichero);
pack_fputs("\n", fichero);
for( int i=0; i < npersonaje; i++)
{
pack_fputs(itoa(personajes[i].getimg(),contenido,10), fichero);
pack_fputs("\n", fichero);

pack_fputs(itoa(personajes[i].getx(),contenido,10), fichero);
pack_fputs("\n", fichero);

pack_fputs(itoa(personajes[i].gety(),contenido,10), fichero);
pack_fputs("\n", fichero);

pack_fputs(itoa(personajes[i].getdir(),contenido,10), fichero);
pack_fputs("\n", fichero);

pack_fputs(itoa(personajes[i].getestado(),contenido,10), fichero);
pack_fputs("\n", fichero);

pack_fputs(itoa(personajes[i].getlugar(),contenido,10), fichero);
pack_fputs("\n", fichero);
}

pack_fputs(itoa(nmalos,contenido,10), fichero);
pack_fputs("\n", fichero);

for( int i=0; i < nmalos; i++)


{
pack_fputs(itoa(malos[i].getimg(),contenido,10), fichero);
pack_fputs("\n", fichero);

pack_fputs(itoa(malos[i].getx(),contenido,10), fichero);
pack_fputs("\n", fichero);

pack_fputs(itoa(malos[i].gety(),contenido,10), fichero);
pack_fputs("\n", fichero);

pack_fputs(itoa(malos[i].getdir(),contenido,10), fichero);
pack_fputs("\n", fichero);
pack_fputs(itoa(malos[i].getestado(),contenido,10), fichero);
pack_fputs("\n", fichero);

pack_fputs(itoa(malos[i].getlugar(),contenido,10), fichero);
pack_fputs("\n", fichero);

pack_fputs(itoa(malos[i].getvida(),contenido,10), fichero);
pack_fputs("\n", fichero);

pack_fclose(fichero);
};

Continuamos guardando datos, ahora toca los del escenario. Se almacena en el fichero la variable lugar, desplazamiento_map_x,
desplazamiento_map_y. Despus se tienen que guardar tambien los npc y los enemigos. Por cada uno de los npc se almacena el
numero de imagen, la posicion x,y, direccion, estado y lugar. Al igual que para los enemigos pero aadiendo la vida.

Y finalmente para acabar cerramos el archivo.

Crear juego RPG en C++ y Allegro 4 (26) Cargar Partida

Aqu esta una nueva entrega del curso crea tu juego RPG en C++ y Allegro. En esta entrega se realizar la carga de partida.

Para realizar la carga de partida, se leen los datos que previamente se guardaron con la funcin guarda_partida(). Para la lectura de
los datos se debe hacer en el mismo orden que se puso cuando se guardaron.

En el archivo partidas.h, se crea una nueva funcin llamada carga_partida().


void carga_partida(){
char contenido[255];
packfile_password("kodaygames2015");
PACKFILE *fichero;

fichero = pack_fopen("partida.sav","r");

El principio es practicamente igual, excepto a la hora de abrir el archivo, al utilizar el comando pack_fopen el segundo parmetro "r"
indica que se abre de lectura.
// datos del jugador

pack_fgets( contenido, 255, fichero);


int jx = atoi( contenido );

pack_fgets( contenido, 255, fichero);


int jy = atoi( contenido );

jugador.posiciona(jx,jy);

pack_fgets( contenido, 255, fichero);


jugador.setvida( atoi( contenido ));

pack_fgets( contenido, 255, fichero);


jugador.setvidamax( atoi( contenido ));

pack_fgets( contenido, 255, fichero);


jugador.setexp( atoi( contenido ));

pack_fgets( contenido, 255, fichero);


jugador.setnivel( atoi( contenido ));

pack_fgets( contenido, 255, fichero);


jugador.setdinero( atoi( contenido ));

pack_fgets( contenido, 255, fichero);


jugador.pon_casco( atoi( contenido ));

pack_fgets( contenido, 255, fichero);


jugador.pon_armadura( atoi( contenido ));

pack_fgets( contenido, 255, fichero);


jugador.pon_arma( atoi( contenido ));

pack_fgets( contenido, 255, fichero);


jugador.pon_anillo( atoi( contenido ));

for (int i=0; i < 12; i++)


{
pack_fgets( contenido, 255, fichero);
jugador.pon_inventario( i, atoi( contenido ) );

// datos del escenario

pack_fgets( contenido, 255, fichero);


lugar = atoi( contenido );

pack_fgets( contenido, 255, fichero);


desplazamiento_map_x = atoi( contenido );
pack_fgets( contenido, 255, fichero);
desplazamiento_map_y = atoi( contenido );

// npc y enemigos

pack_fgets( contenido, 255, fichero);


npersonaje = atoi( contenido );

int timg;
int tx;
int ty;
int tdir;
int testado;
int tlugar;
int tvida;

for ( int i = 0; i < npersonaje; i++)


{

pack_fgets( contenido, 255, fichero);


timg = atoi( contenido );

pack_fgets( contenido, 255, fichero);


tx = atoi( contenido );

pack_fgets( contenido, 255, fichero);


ty = atoi( contenido );

pack_fgets( contenido, 255, fichero);


tdir = atoi( contenido );

pack_fgets( contenido, 255, fichero);


testado = atoi( contenido );

pack_fgets( contenido, 255, fichero);


tlugar = atoi( contenido );

personajes[i].crea( timg, tx, ty, tdir, testado, tlugar );

pack_fgets( contenido, 255, fichero);


nmalos = atoi( contenido );

for ( int i = 0; i < nmalos; i++)


{
pack_fgets( contenido, 255, fichero);
timg = atoi( contenido );

pack_fgets( contenido, 255, fichero);


tx = atoi( contenido );

pack_fgets( contenido, 255, fichero);


ty = atoi( contenido );

pack_fgets( contenido, 255, fichero);


tdir = atoi( contenido );

pack_fgets( contenido, 255, fichero);


testado = atoi( contenido );

pack_fgets( contenido, 255, fichero);


tlugar = atoi( contenido );

pack_fgets( contenido, 255, fichero);


tvida = atoi( contenido );

malos[i].crea( timg, tx, ty, tdir, testado, tlugar, tvida );


}

pack_fclose(fichero);
};

Con el comando pack_fgets se va obteniendo lineas y se guardan en la variable contenido. A medida que se va leyendo el archivo se
van asignando los valores a sus respectivas variables. Al finalizar de leer el fichero, se debe de llamar a la funcin carga_escenario().
Crear juego RPG en C++ y Allegro 4 (27) Menu

En esta entrega le aadiremos 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 automtica cuando
salgamos pulsando la tecla ESC del juego.
Programacin
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:

?
1 /*
2
3 Name: menu.h
4 Author: KODAYGAMES
5 Web: http://devcpp-allegro.blogspot.com/
6
7 */
8
9 #define NUMOP 3
10
11class MIMENU {
12 private:
13 int op;
14 int max_op;
15 bool salidamenu;
16 bool pulsa;
17
18 public:
19 MIMENU();
20 void pinta();
21 int teclado();
22 bool salir();
23};
24
25
26MIMENU::MIMENU(){
27 op = 1;
28 max_op = NUMOP;
29 salidamenu = false;
30 pulsa = false;
31};
32
33
34
35void MIMENU::pinta()
36{
37 if ( op == 1 ) blit(menufondo2, buffer, 0, 220, 0, 220, PANTALLA_ANCHO, 70);
38
39 if ( op == 2 ) blit(menufondo2, buffer, 0, 310, 0, 310, PANTALLA_ANCHO, 70);
40
41 if ( op == 3 ) blit(menufondo2, buffer, 0, 400, 0, 400, PANTALLA_ANCHO,
4270);
43};
44
45
46int MIMENU::teclado(){
47
48 if (key[KEY_DOWN] && !pulsa ){ op++; pulsa = true; sonido_boton(); }
49
50 if (key[KEY_UP] && !pulsa) {op--; pulsa=true; sonido_boton(); }
51
52 if ( pulsa && !key[KEY_DOWN] && !key[KEY_UP] ) pulsa=false;
53
54 if (op > max_op) op=1;
55 if (op < 1) op=max_op;
56
57 // comprueba si esta en la opcion de salir
58 if ((key[KEY_ENTER] || key[KEY_ENTER_PAD]) && op == max_op ) salidamenu = true;
59
60 if (key[KEY_ESC]) op=max_op;
61
62 return op;
63};
64
65bool MIMENU::salir(){
66 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 mximo 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 funcin MIMENU::MIMENU(), inicializa las variables.

La funcin MIMENU::pinta(), se encarga de pintar segn el valor de op una de las opciones.

La funcin 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 opcin 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 funcin 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 tendrn las imgenes que se utilizan para el men inicio del
juego.

En el archivo players.h, se aade una nueva funcin llamada pon_choque(), que se encargar de aadir un rectngulo 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 posicin 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 funcin 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 funcin 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 funcin crear y se ha aadido 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 funcin npc::crea(), reciba una imagen bitmap. Ahora recibe un ndice, mediante el cual accede a la imagen
utilizando (BITMAP *)datosjuego[i].dat;

La funcin 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 funcin 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 funcin 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.

?
1 class enemigo : public npc {
2 int vida;
3 int v_actual;
4 bool muerto;
5 int golpeado;
6 int exp;
7 public:
8 void crea( int i, int _x, int _y, int dir, int _estado, int _lugar, int v );
9 void herida( int d );
10 void pinta();
11 bool ha_muerto() { return muerto; };
12 void movimiento();
13 void daexp(int e){ exp = e; };
14 const int getvida() { return vida; };
15 };
16
17 void enemigo::crea( int i, int _x, int _y, int dir, int _estado, int _lugar, int v )
18 {
19 x = _x;
20 y = _y;
21 direccion = dir;
22 animacion = 0;
23 escena = _lugar;
24
25 img=i;
26 BITMAP *_img = (BITMAP *)datosjuego[i].dat;
27 imagen = create_bitmap(_img->w, _img->h);
28
29 blit( _img, imagen, 0,0, 0,0, _img->w, _img->h);
30
31 estado = _estado;
32
33 vida = v;
34 v_actual = vida;
35 muerto = false;
36 golpeado=0;
37 exp = 50;
38 };
39
40 void enemigo::herida( int d )
41 {
42 if ( !muerto )
43 {
44 int num = FRAME_RATE / 2;
45 v_actual-=d;
46 if ( v_actual <= 0 )
47 {
48 muerto = true;
49 int vx = x - desplazamiento_map_x;
50 int vy = y - desplazamiento_map_y;
51 rectfill( cbuffer, vx+2, vy+1, vx+30, vy+31, 0x000000);
52 sonido_muere();
53 jugador.sube_experiencia(exp);
54 // siempre obtiene un objeto aleatorio
55 int q2 = lobj.size();
56 int q = lobj[rand()%q2].id;
57 jugador.obtiene_objeto( q );
58
59 }else{
60 // dao defensivo del enemigo
61 if ( tiempo_total % num == 0 )
62 {
63 if ( rand()%2 == 1 )
64 {
65 sonido_herido();
66 jugador.herido(5+rand()%5);
67 }
68 }
69 }
70
71 }
72 };
73
74
75 void enemigo::pinta()
76 {
77 if ( lugar == escena && !muerto )
78 {
79
80 if ( v_actual == vida )
81 {
82 actualiza();
83 }else{
84 movimiento();
85 }
86
87 int vx = x - desplazamiento_map_x;
88 int vy = y - desplazamiento_map_y;
89
90 rectfill( cbuffer, vx+2, vy+1, vx+30, vy+31, 0xff0000);
91
92 masked_blit(imagen, buffer, animacion*32, direccion*32, vx, vy, 32,32);
93
94 if ( golpeado == 1 )
95 {
96 int xn = 2 + rand()%2;
97 jugador.no_ataca();
98 if ( rand()%10 != 1 )
99 {
100 sonido_espada_da();
101 herida(xn);
102 }else{
103 sonido_espada_choca();
104 }
105 golpeado = 0;
106
107 }
108
109 if ( golpeado == 0 && jugador.atacando() && posicion_cerca()
110 && frente() )
111 {
112 golpeado = 1;
113 }
114
115 if ( !muerto )
116 {
117 int nm = (v_actual * 30 ) / vida;
118 rectfill( buffer, vx+1, vy, vx+nm, vy+5, 0x00ff00);
119 rect( buffer, vx, vy, vx+31, vy+5, 0x000000);
120 }
121 }
122 if ( lugar != escena && v_actual < vida )
123 {
124 int num = FRAME_RATE / 5;
125 if ( tiempo_total % num == 0 )
126 {
127 v_actual++;
128 }
129 }
130}
131
132
133void enemigo::movimiento()
134{
135 ax = x;
136 ay = y;
137 int jx = jugador.getx() + desplazamiento_map_x;
138 int jy = jugador.gety() + desplazamiento_map_y;
139
140 // para indicar que se ejecuta dos veces
141 int num = FRAME_RATE / 6;
142
143 int esta = 0;
144
145 // mira hacia donde este el jugador
146 if ( jx > x )
147 {
148 direccion = 2;
149 }else{
150 direccion = 1;
151 }
152
153 if ( jy > y )
154 {
155 if ( abs ( jx - x ) < desplazamiento*3 )
156 {
157 direccion = 0;
158 }
159 }else{
160 if ( abs ( jx - x ) < desplazamiento*3 )
161 {
162 direccion = 3;
163 }
164 }
165
166 // enemigo te persigue
167
168 if ( tiempo_total % num == 0 )
169 {
170
171 if ( x+32 < jx )
172 {
173 x+=desplazamiento;
174 esta = 1;
175 }
176
177 if ( x > jx+32 )
178 {
179 x-=desplazamiento;
180 esta = 1;
181 }
182 if ( y+32 < jy )
183 {
184 y+=desplazamiento;
185 esta = 1;
186 }
187
188 if ( y > jy+32 )
189 {
190 y-=desplazamiento;
191 esta = 1;
192 }
193
194 }
195
196
197 if ( ax != x || ay != y )
198 {
199 // se ha movido en una de las direcciones
200
201 if ( chocanpc() )
202 {
203 x = ax;
204 y = ay;
205 esta = 0;
206 }
207 if ( esta != 0 )
208 {
209 animacion++;
210 if ( animacion > 2 ) animacion = 0;
211 }
212
213 }
214
215
216 if ( posicion_cerca() )
217 {
218 int num = FRAME_RATE / 3;
219 if ( tiempo_total % num == 0 )
220 {
221 if ( rand()%3 == 1 )
222 {
223 sonido_herido();
224 jugador.herido(2+rand()%2);
225 animacion++;
226 if ( animacion > 2 ) animacion = 0;
227 }
228 }
229 }
230
231}
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 aade 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 funcin inicia_allegro(), se aade la inicializacin de la nueva variable cbuffer, justo despues de inicializar buffer.
cbuffer = create_bitmap(PANTALLA_ANCHO, PANTALLA_ALTO);

El main() cambia por completo, para aadir el menu.

?
1 int main()
2 {
3
4 inicia_allegro();
5
6 carga_inicio();
7
8 musica_menu();
9
10 int op;
11 MIMENU menu;
12
13 do{
14
15 blit(menufondo, buffer, 0, 0, 0, 0, PANTALLA_ANCHO, PANTALLA_ALTO);
16 menu.pinta();
17 op = menu.teclado();
18
19 // se a seleccionado una opcion
20 if (key[KEY_ENTER] || key[KEY_ENTER_PAD])
21 {
22 if ( op == 2 )
23 {
24 // carga partida
25 carga_partida();
26 sonido_sube_nivel();
27 }
28 if ( op == 1 )
29 {
30 jugador.inicia();
31 lugar = 1;
32 desplazamiento_map_x=-160;
33 desplazamiento_map_y=-160;
34 sonido_sube_nivel();
35
36 npersonaje = 10;
37
38 personajes[0].crea( diper001, 1300,700, 1,1,3);
39 personajes[1].crea( diper005, 280, 450, 0,2,3);
40 personajes[2].crea( diper005, 230, 280, 3,2,3);
41 personajes[3].crea( diper003, 960, 310, 2,3,3);
42 personajes[4].crea( diper005, 1120, 450, 0,4,3);
43 personajes[5].crea( diper004, 900, 650, 1,5,3);
44 personajes[6].crea( diper006, 850, 800, 0,0,3);
45 personajes[7].crea( diper001, 530, 280, 1,5,3);
46 personajes[8].crea( diper007, 334, 170, 0,0,4);
47 personajes[9].crea( diper008, 142, 170, 0,0,4);
48
49 nmalos = 3;
50 malos[0].crea( diene001, 380, 280, 3,5,2,100);
51 malos[1].crea( diene001, 400, 720, 0,5,2,100);
52 malos[2].crea( diene001, 380, 240, 0,5,2,100);
53 malos[3].crea( diper005, 440, 720, 3,5,2,100);
54
55 }
56
57 if ( op == 1 || op == 2 )
58 {
59 carga_juego();
60
61 // juego
62 salir = false;
63
64 while ( !salir )
65 {
66
67 // tecla de salida
68 if ( key[KEY_ESC] ) salir = true;
69
70 if ( contador_tiempo_juego )
71 {
72 while ( contador_tiempo_juego )
73 {
74 actualiza_juego();
75 contador_tiempo_juego--;
76 }
77
78 clear_to_color(buffer, 0x00000);
79
80 pinta_juego();
81 pintar_pantalla();
82
83 }else{
84 rest(1);
85 }
86 }
87
88 if ( lugar == 2 ) para_sonido_ambiente();
89 // fin juego
90 guarda_partida();
91 musica_menu();
92 op=2;
93 }
94 }
95
96 pintar_pantalla();
97
98 }while ( !menu.salir() );
99
100
101 destroy_bitmap(fondo);
102 destroy_bitmap(choque);
103 destroy_bitmap(cielo);
104
105 destroy_bitmap(menufondo);
106 destroy_bitmap(menufondo2);
107 destroy_bitmap(cbuffer);
108 destroy_bitmap(buffer);
109
110 return 0;
111}
112END_OF_MAIN();

Se hace que cuando salgas de la partida, se guarde automticamente.

Haz clic aqui para descargar en RAR el archivo main.cpp

Y hasta aqu llega este curso, aun falta un prximo 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.
Crear juego RPG en C++ y Allegro 4 (28) Menu II

Aqu esta una nueva entrega del curso crea tu juego RPG en C++ y Allegro. Continuamos con el menu.

Ya solo nos queda actualizar el archivo mijuego.h.

Se crea una nueva funcin llamada carga_inicio(), que contiene la carga de los ficheros .DAT, la lectura de los objetos y las imagenes
del men.

?
1 void carga_inicio()
2 {
3 packfile_password(evalc);
4
5 datosjuego = load_datafile("datosjuego.dat");
6 if ( !datosjuego ){
7 allegro_message("Error: archivo datosjuego.dat no encontrado\n%s\n", allegro_error);
8 }
9
10 datobjetos = load_datafile("objetos.dat");
11 if ( !datobjetos ){
12 allegro_message("Error: archivo objetos.dat no encontrado\n%s\n", allegro_error);
13 }
14
15 datotiendas = load_datafile("datostienda.dat");
16 if ( !datobjetos ){
17 allegro_message("Error: archivo datostienda.dat no encontrado\n%s\n", allegro_error);
18 }
19
20 lee_objetos();
21
22
23 menufondo = (BITMAP *)datosjuego[dimenu].dat;
24 menufondo2 = (BITMAP *)datosjuego[dimenu2].dat;
25}

Se crea una funcin para actualizar el contenido de la variable cbuffer, que contendr el mapa de choque de lo que se muestra por
pantalla, que se llama carga_mapa_choque().

?
1void carga_mapa_choque()
2{
3 clear_to_color( cbuffer, makecol(0, 0, 0));
4 blit ( choque, cbuffer, desplazamiento_map_x, desplazamiento_map_y,0,0, PANTALLA_ANCHO, PANTALLA_ALTO);
5 jugador.pon_choque();
6}

Se reduce el contenido de la funcin carga_juego(), ya que parte de lo que tena se ha trasladado a la funcin carga_inicio().

?
void carga_juego()
1
{
2
cambio = 0;
3
carga_escenario();
4
5
dialogo.crea("", (FONT *)datosjuego[dftextos].dat, 10, PANTALLA_ALTO-100, PANTALLA_ANCHO-10,
6
PANTALLA_ALTO-10);
7
hablando = 0;
8
9
mision = 1;
10
swraton=-1;
11
12
swinv=0;
13
muestra_tienda = false;
14
swtienda=0;
15
}

Se realizan algunos cambios en la funcin carga_escenario().

?
1 // carga los datos del escenario segun lugar
2 void carga_escenario()
3 {
4 switch ( lugar )
5 {
6 case 1:// casa
7 fondo = (BITMAP *)datosjuego[dicasa].dat;
8 choque = (BITMAP *)datosjuego[dicasachoque].dat;
9 cielo = (BITMAP *)datosjuego[dicasasup].dat;
10
11 carga_mapa_choque();
12 desplaza = false;
13 if ( cambio != 0 ) sonido_abrirpuerta();
14 musica_casa();
15 break;
16
17 case 2:// bosque
18 fondo = (BITMAP *)datosjuego[dibosque].dat;
19 choque = (BITMAP *)datosjuego[dibosquechoque].dat;
20 cielo = (BITMAP *)datosjuego[dibosquesup].dat;
21
22 desplaza=true;
23 carga_mapa_choque();
24 sonido_ambiente();
25 musica_bosque();
26 break;
27
28 case 3:// ciudad
29 fondo = (BITMAP *)datosjuego[dicity1].dat;
30 choque = (BITMAP *)datosjuego[dicity1choque].dat;
31 cielo = (BITMAP *)datosjuego[dicity1sup].dat;
32
33 desplaza=true;
34 carga_mapa_choque();
35 if ( cambio == 0 ) musica_ciudad1();
36 break;
37 case 4:// tienda1
38 fondo = (BITMAP *)datosjuego[ditienda1].dat;
39 choque = (BITMAP *)datosjuego[ditienda1choque].dat;
40 cielo = (BITMAP *)datosjuego[ditienda1sup].dat;
41 carga_mapa_choque();
42 desplaza=false;
43 if ( cambio != 0 ) sonido_abrirpuerta();
44 if ( cambio == 0 ) musica_ciudad1();
45 break;
46 }
47}

En la funcin cambia_escenario(), se suprime algunos sonidos, y se aade la llamada a la funcin carga_mapa_choque().

?
1 void cambia_escenario()
2 {
3
4 switch ( lugar )
5 {
6 case 1: // casa
7 if ( cambio == 1 )
8 {
9 // cambiamos a otro lugar
10 // bosque
11 lugar = 2;
12 carga_escenario();
13 // situamos al prota dentro de la casa
14 jugador.posiciona( 410,370 );
15 desplazamiento_map_x=0;
16 desplazamiento_map_y=160;
17 cambio = 0;
18
19
20 }
21 break;
22 case 2: // bosque
23 if ( cambio == 2 )
24 {
25 // cambiamos a otro lugar
26 // casa
27 lugar = 1;
28 carga_escenario();
29 // situamos al prota cerca de la puerta
30 jugador.posiciona( 290,440 );
31 desplazamiento_map_x=-160;
32 desplazamiento_map_y=-160;
33 sonido_abrirpuerta();
34 para_sonido_ambiente();
35 cambio = 0;
36
37 }
38 if ( cambio == 3 )
39 {
40 // cambiamos a otro lugar
41 // ciudad
42 lugar = 3;
43 carga_escenario();
44 // situamos al prota en el camino
45 jugador.posiciona( 500,540 );
46 desplazamiento_map_x=950;
47 desplazamiento_map_y=508;
48 para_sonido_ambiente();
49 musica_ciudad1();
50 cambio = 0;
51 }
52 break;
53 case 3: // ciudad
54 if ( cambio == 1 )
55 {
56 // cambiamos a otro lugar
57 // bosque
58 lugar = 2;
59 carga_escenario();
60 // situamos al prota en el camino del bosque
61 jugador.posiciona( 650,30 );
62 desplazamiento_map_x=200;
63 desplazamiento_map_y=0;
64 cambio = 0;
65 }
66 // color amarillo que existen muchos
67 if ( cambio == 3 && desplazamiento_map_x > 800 )
68 {
69 // cambiamos a otro lugar
70 // tienda1
71 lugar = 4;
72 carga_escenario();
73 // situamos al prota en el camino del bosque
74 jugador.posiciona( 376,460 );
75 desplazamiento_map_x=-170;
76 desplazamiento_map_y=-100;
77
78 cambio = 0;
79 }
80 break;
81 case 4: // tienda1
82 if ( cambio == 1)
83 {
84 // cambiamos a la ciudad
85 lugar=3;
86 carga_escenario();
87 jugador.posiciona( 400,300 );
88 desplazamiento_map_x=1090;
89 desplazamiento_map_y=85;
90 cambio = 0;
91 sonido_abrirpuerta();
92 }
93
94
95 default:
96 break;
97 }
98 carga_mapa_choque();
99}

Se realizan algunos cambios en la funcin pinta_juego().

?
1 // Se encarga de pintar todo sobre el buffer
2 void pinta_juego()
3 {
4 int ancho, alto;
5 int ax=0;
6 int ay=0;
7 int bx=0;
8 int by=0;
9
10 switch ( lugar )
11 {
12 case 1: // casa
13 bx=160;
14 by=160;
15 ancho = 480;
16 alto = 325;
17 break;
18 case 2: // bosque
19 ax = desplazamiento_map_x;
20 ay = desplazamiento_map_y;
21 ancho = PANTALLA_ANCHO;
22 alto = PANTALLA_ALTO;
23 break;
24 case 3: // ciudad1
25 ax = desplazamiento_map_x;
26 ay = desplazamiento_map_y;
27 ancho = PANTALLA_ANCHO;
28 alto = PANTALLA_ALTO;
29 break;
30 case 4: // tienda1
31 bx = 170;
32 by = 100;
33 ancho = 448;
34 alto = 416;
35 break;
36 default:
37 break;
38 }
39
40
41 blit( fondo, buffer, ax, ay, bx, by, ancho, alto);
42 for ( int z=0; z < npersonaje; z++ )
43 {
44 personajes[z].pinta();
45 }
46
47 for ( int z=0; z < nmalos; z++ )
48 {
49 malos[z].pinta();
50 }
51
52 jugador.pinta();
53
54 masked_blit( cielo, buffer, ax, ay, bx, by, ancho, alto);
55
56 if ( hablando > 1 )
57 {
58 dialogo.pinta(buffer);
59 hablando++;
60 }
61
62 pinta_barra_vida();
63 pinta_lvlup();
64 pinta_inventario();
65
66 pinta_tienda();
67}
Y llegado a este punto ya estara todos los cambios realizados en el archivo mijuego.h.

Haz clic aqui para descargar en RAR el archivo mijuego.h

Para que todo vaya correctamente debe de tener los siguientes archivos:

Archivo datosjuego.dat y datosjuego.h, que contienen imagenes, sonidos, etc. Descargar


Archivos de los objetos y la tienda, que incluye objetos.dat, objetos.txt, datostienda.dat, tiendas.txt, list01.txt y list02.txt. Descargar

Y si todo esta correcto debes tener algo similar a lo que se muestra en el siguiente video.

Crear juego RPG en C++ y Allegro 4 (29) Control de Errores

Aqu esta la ltima entrega del curso crea tu juego RPG en C++ y Allegro, el control de errores. Se debe de minimizar la cantidad de
posibles errores.

Para reducir la cantidad de posibles errores se utiliza la herramienta grabber para unificar todos los archivos multimedia en un archivo
.DAT, de esta manera es mas fcil de comprobar su existencia.
A medida que se ha ido haciendo el juego se han aadido nuevos archivos, por ejemplo, los archivos datostienda.dat y objetos.dat. Si
alguno de estos archivos falta el juego no funciona y da un error.

Este error esta contemplado, es decir, te avisa de que no existe el archivo .dat, pero no se realiza nada mas. Ya que el programa no
funciona en el caso de que falte alguno de los archivos, se debe de hacer que en cuanto falte algn archivo aparte de avisar, que se
salga del programa, evitando as otros posibles errores incontrolables.

El juego consta de los siguientes archivos:


RPG.exe - El ejecutable de la aplicacin
datostienda.dat - Imagenes, sonidos, etc.
objetos.dat - Imagenes de los objetos.
objetos.txt - Informacin de todos los objetos que aparecen en el juego.
datostienda.dat - Imagenes de fondos para las tiendas.
tiendas.txt - tipos de tiendas que hay en el juego.
list01.txt - lista de objetos de una tienda
list02.txt - lista de objetos de una tienda

Esta lista es segn el ejemplo del curso, pero si se han aadido mas mapas, y mas tiendas es posible que se aumente el numero de
archivos que utiliza el juego.

Por cada uno de los archivos, deberamos de comprobar su existencia, y en el caso de no encontrarlo salir del juego.
Que otros tipos de errores se pueden tener ?
Como existen varios archivos de texto (.txt), que se hicieron as para que cada uno pueda aadir sus objetos, tiendas, etc. Se puede dar
el caso que aunque exista el archivo, este archivo no contenga una informacin correcta para el programa. En ese caso cuando el juego
intente acceder a la informacin es muy probable que salte un error.

Este tipo de errores es muy difcil de detectar, por ello es recomendable tener siempre un orden, y un formato a la hora de acceder a la
informacin, para intentar detectar si se a ledo algn dato errneo. De esta manera, poder parar la ejecucin del juego e indicar que
existe algn problema al leer el archivo. Tambin pueden contener informacin errnea si el usuario del juego intenta manipular
dichos archivos, por ello es recomendable que estn protegidos.

Los errores de ejecucin son los ms difciles de detectar, ya que requiere de que alguien testee el juego, y compruebe todas las
posibles opciones.

Evidentemente no se pueden controlar todos los posibles errores, pero cuantos mas tengamos en cuenta esto determinar la estabilidad
de nuestro juego.

Otro error que no se controla es ... que ocurre si la primera vez le damos a cargar partida ? ... El programa no controla si existe el
archivo, y debera comprobar si existe, en el caso de no existir debera ser igual que cuando das a iniciar partida.
Programacin
Se crea una variable que contenga si existe algn error, en nuestro caso se ha llamado la variable sys_error, esta variable como se va a
utilizar en todos las funciones nos interesa que sea global, por tanto su definicin se realiza en global.h.
int sys_error;

Se considera sin error en el caso de que la variable tenga como valor cero.

Se debe aadir en todas las condiciones que comprueban la existencia de los ficheros, de modo de que cuando no existan alguno, la
variable sys_error tenga una valor, y dependiendo de este valor se sabe que tipo de error es.

En este caso, pondr un ejemplo de como hacerlo.

En el archivo mijuego.h, en la funcin carga_inicio().


datosjuego = load_datafile("datosjuego.dat");
if ( !datosjuego ){
allegro_message("Error: archivo datosjuego.dat no encontrado\n%s\n", allegro_error);
sys_error = 7;
}

En este ejemplo, se carga el archivo datosjuego.dat. En el caso de no existir da un mensaje de error: "Error: archivo datosjuego.dat no
encontrado", y adems se le asigna el valor 7 a la variable sys_error que en mi caso es el que indica que el archivo datosjuego.dat no
existe.

De esta forma, mirando el valor de la variable sys_error el programa puede saber si ha habido un error, ya que siempre debe valer cero
en el caso de no tener ningn error.

Una vez detectado el error, se debe evitar que contine el programa. Para ello se aade lo siguiente en el archivo main.cpp en la
funcin main().
// programa principal
int main()
{
sys_error = 0;

inicia_allegro();

carga_inicio();

musica_menu();

int op;
MIMENU menu;

while ( !menu.salir() && sys_error == 0 )


{

De esta forma el bucle principal se detiene en cuanto la variable sys_error sea distinto de cero, evitando as que se generen mas
errores.
if ( sys_error != 0 )
{
allegro_message("Se ha encontrado un Error: %d\n", sys_error);
}
return 0;
}
END_OF_MAIN();

Aadiendo esto ltimo, se mostrar por pantalla una ventana indicando el error que se ha detectado.

En el caso de querer cargar partida cuando no existe ninguna partida, es un error que se puede solucionar, si se da este caso se debe
iniciar partida de forma normal.

Por un lado hay que controlar si existe la partida. En el archivo partidas.h en la funcin carga_partida(), se aade una condicin que
controle si ha habido algn error al cargar la partida.
fichero = pack_fopen("partida.sav","r");
if ( fichero )
{

...

pack_fclose(fichero);
}else{
// no existe partida guardada
sys_error = 6;
}

En el caso de no existir el archivo, sys_error toma el valor 6, que en este ejemplo es el que representa que no existe la partida
guardada.

Luego se debe aadir el control del error, para que se inicie una partida nueva. En el archivo main.cpp, en la funcin main().
if ( op == 2 )
{
// carga partida
carga_partida();
if ( sys_error == 6 )
{
// pasamos a partida nueva
op = 1;
sys_error = 0;
}
}
if ( op == 1 )
{
// partida nueva

De este modo, cuando sys_error vale 6, se asigna a op el valor 1 para indicar que se inicia una partida nueva, y se pone el valor cero a
la variable sys_error para indicar que se ha solucionado el error.
Introduccion Curso RPG
Este curso es una nueva edicin del curso "Crea tu juego RPG". Se trata de un curso que ir paso a paso explicando como crear tu
propio juego. Concretamente un juego al mas puro estilo RPG, utilizando el lenguaje de programacin C++ y la librera Allegro 4.4.

Aprende a programar
tu propio juego RPG.
Para facilitar la creacin del juego y poder centrarnos en la programacin, las imgenes que se utilizan para este curso son sacadas del
programa RPG-Maker. Es por ello, que se utilizar para la imagen del protagonista un "character set" del RPG Maker.
Creando un personaje
Se programar la creacin un personaje principal, que se pueda desplazar con las teclas del cursor.

Para facilitar la creacin de este ejemplo y poder centrarnos en la programacin se utilizar un "Character set" del RPG Maker.

Que es un Character set del RPG Maker ?

Es una imagen, donde esta dibujado un personaje segn las cuatro direcciones: abajo, izquierda, derecha y arriba. Y cada una de ellas
tiene tres variantes para hacer una pequea animacin.
En nuestro ejemplo utilizaremos la siguiente imagen.

Haga Click para Descargar

La librera Allegro 4.4 trabaja con imgenes BMP, el color rosa es el que utiliza Allegro para las transparencias.

Programacin

?
1 #include <allegro.h>
2
3 int main()
4 {
5 allegro_init();
6 install_keyboard();
7
8 set_color_depth(32);
9 set_gfx_mode(GFX_AUTODETECT_WINDOWED, 800, 600, 0, 0);
10
11 BITMAP *buffer = create_bitmap(800, 600);
12 BITMAP *prota = load_bmp("personaje.bmp",NULL);
13
14 bool salir;
15 int x,y;
16
17 // inicializar vbles
18 x = 10;
19 y = 10;
20 salir = false;
21
22
23 while ( !salir )
24 {
25 clear_to_color(buffer, 0xaaaaaa);
26
27 masked_blit(prota, buffer, 0,0, x, y, 32,32);
28
29 // teclas control usuario
30 if ( key[KEY_UP] )
31 {
32 y--;
33 }
34 if ( key[KEY_DOWN] )
35 {
36 y++;
37 }
38 if ( key[KEY_LEFT] )
39 {
40 x--;
41 }
42 if ( key[KEY_RIGHT] )
43 {
44 x++;
45 }
46
47 blit(buffer, screen, 0, 0, 0, 0, 800, 600);
48
49 rest(10);
50
51 // tecla de salida
52 if ( key[KEY_ESC] ) salir = true;
53
54 }
55
56 destroy_bitmap(prota);
57 destroy_bitmap(buffer);
58
59 return 0;
60}
61END_OF_MAIN();

Paso a Paso
#include <allegro.h>
Incluye la libreria Allegro, para poder hacer uso de sus funciones y comandos.

int main(){
Se inicia la funcin principal

allegro_init();
Inicializa los valores de las variables de la libreria Allegro.

install_keyboard();
Instala el controlador de interrupciones del teclado Allegro. Antes de utilizar cualquier rutina de entrada del teclado debe llamarse a
esta funcin.

set_color_depth(32);
Establece el formato de profundidad de los pixels. Los valores posibles son 8,15,16,24 y 32 bits.

set_gfx_mode(GFX_AUTODETECT_WINDOWED, 800, 600, 0, 0);


Establece la resolucin y el tipo de pantalla utilizada. En este caso se indica que ser modo ventana, con unas dimensiones de 800x600
pixel.

BITMAP *buffer = create_bitmap(800, 600);


Se crea una imagen bitmap de 800x600 y se guarda en el variable buffer. Esta imagen almacena todo antes de mostrarse por pantalla.

BITMAP *prota = load_bmp("personaje.bmp",NULL);


Se carga un BMP, llamado personaje.bmp. Y se guarda en la variable prota.

bool salir;
Se crea una variable del tipo booleano llamada salir. Se utiliza para el bucle principal.

int x,y;
Se crea dos variables enteras: x,y. Se utilizan para posicionar la imagen en la pantalla.

// inicializar vbles
x = 10;
y = 10;
salir = false;

Cuando esta precedido de "//" indica que es un comentario.


Las variables son inicializadas a un valor, ya que hasta el momento pueden contener cualquier valor.

while ( !salir )
{
Bucle que se repite mientras se cumpla la condicin. En este caso se repite mientras no sea salir.

clear_to_color(buffer, 0xaaaaaa);
Borra el contenido de la imagen buffer, rellenandolo con un color gris (0xaaaaaa).

masked_blit(prota, buffer, 0,0, x, y, 32,32);


Pinta la imagen prota sobre la imagen buffer, en la posicion x,y dentro de la imagen buffer. Con un tamao de la imagen prota de
32x32.

// teclas control usuario


if ( key[KEY_UP] )
{
y--;
}
if ( key[KEY_DOWN] )
{
y++;
}
if ( key[KEY_LEFT] )
{
x--;
}
if ( key[KEY_RIGHT] )
{
x++;
}
Este conjunto de condiciones se encarga de controlar cuando se pulsa alguna de las cuatro teclas de direccin y en el caso de ser
pulsadas modifica la posicin (x,y).

blit(buffer, screen, 0, 0, 0, 0, 800, 600);


Vuelca el contenido de la imagen buffer sobre screen, que es la pantalla, con un tamao que previamente se a definido, 800x600. Es en
este instante en el que se muestra todo por pantalla.
rest(10);
Hace una pausa de 10 milisegundos.

// tecla de salida
if ( key[KEY_ESC] ) salir = true;
Se define la tecla ESC como tecla para salir, es por ello que se controla si se ha pulsado, en ese caso cambia el valor de la variable
salir.

}
Fin del bucle principal

destroy_bitmap(prota);
destroy_bitmap(buffer);
Elimina las variables prota y buffer, liberando la memoria ocupada por dichas imagenes.

return 0;
}
END_OF_MAIN();

Devuelve 0 la funcion principal. Y finaliza el programa.


Animacin Personaje
Para crear la animacin de nuestro personaje debemos tener varias imgenes del personaje en distintas posiciones.

Esta entrada es la continuacin de "Creando un personaje", por tanto para completar el programa es necesario haber hecho antes el
anterior.

La imagen contiene tres imgenes para cada una de las posibles direcciones. El tamao del personaje es de 32x32.

Empezando de arriba a abajo las direcciones son: abajo, izquierda, derecha y arriba. Esto es importante ya que la imagen se va a tratar
como una matriz de 4x3.

Mediante una variable llamada direccion se controla la direccin del personaje, y puede tener los siguientes valores:
0 : Abajo
1 : Izquierda
2 : Derecha
3 : Arriba

Y para el control de la animacin se crea la variable animacion, que puede tener 3 valores, del cero al 2.

Con estas dos variables se consigue que mediante una nica sentencia de pintar seamos capaz de mostrar cualquier imagen del
personaje. Quedando de la siguiente forma:
?
1 masked_blit(prota, buffer, animacion*32, direccion*32, x, y, 32,32);

Con el comando masked_blit pinta al personaje con el fondo transparente. Pinta de la imagen prota "un trozo" de 32x32, que esta
definida por los dos ltimos parmetros, sobre la imagen buffer, en la posicion x,y.

"El trozo" esta definido segun: animacion*32, direccion*32. Ambas variables se multiplican por 32 ya que es el tamao del personaje,
y de este modo se selecciona la imagen deseada.

Ejemplos:
Ejemplo 1.
animacion = 0; direccion = 2;
Se ha seleccionado el personaje mirando hacia la derecha.

Ejemplo 2.
animacion = 2; direccion = 0;
Se ha seleccionado el personaje mirando hacia abajo.
Se aade a cada condicin que controla el teclado, el uso de la variable direccion, asignandole el valor que le corresponde.

?
1 if ( key[KEY_UP] )
2 {
3 y-=desplazamiento;
4 direccion = 3;
5 }
6 if ( key[KEY_DOWN] )
7 {
8 y+=desplazamiento;
9 direccion = 0;
10}
11if ( key[KEY_LEFT] )
12{
13 x-=desplazamiento;
14 direccion = 1;
15}
16if ( key[KEY_RIGHT] )
17{
18 x+=desplazamiento;
19 direccion = 2;

Y a continuacin para la animacion, para que vaya cambiando su valor pero de forma controlada podemos hacerlo de la siguiente
forma:

?
1animacion++;
2if ( animacion > 2 ) animacion = 0;

De este modo conseguimos que siempre este su valor entre 0 y 2. Recuerda que al principio del programa debers de declarar las dos
variables e inicializarlas a cero.

El primer Escenario (1)


En esta entrega se har un escenario que pueda recorrer nuestro personaje.
Para crear nuestro escenario se utilizarn tres imgenes.

Una imagen del escenario, que se utilizar de fondo y se almacenar en la variable "fondo".

Una segunda imagen que contendr las partes de nuestro escenario que estn por encima del personaje, que se guarda en la variable
"cielo".

Y una tercera imagen para controlar la colisin, de esta manera nuestro personaje no podr atravesar todo por donde quiera. Esta
imagen se guarda en la variable "choque".

A continuacin tenis un ejemplo de las tres imgenes.


1. Choque. 2. Fondo. 3. Cielo

Para la imagen de choque tendr los siguientes requisitos, el fondo de la imagen debe ser de color negro, y las partes que se quiera que
el personaje colisione debe pintarse de rojo.

Para la imagen del cielo todo lo que se quiera hacer transparente debe de pintarse de color rosado, concretamente el color (0xff00ff).

Programacin

?
1 #include <allegro.h>
2 #include "global.h"
3 #include "players.h"
4 #include "mijuego.h"
5
6
7
8 void inicia_allegro()
9 {
10 allegro_init();
11 install_keyboard();
12
13 set_color_depth(32);
14 set_gfx_mode(GFX_AUTODETECT_WINDOWED, PANTALLA_ANCHO, PANTALLA_ALTO, 0, 0);
15
16 buffer = create_bitmap(PANTALLA_ANCHO, PANTALLA_ALTO);
17
18 LOCK_VARIABLE(contador_tiempo_juego);
19 LOCK_FUNCTION(inc_contador_tiempo_juego);
20
21 // Iniciamos el limitador de FPS
22 install_int_ex(inc_contador_tiempo_juego, BPS_TO_TIMER( FRAME_RATE ));
23}
24
25
26
27// programa principal
28int main()
29{
30 inicia_allegro();
31
32 carga_juego();
33
34 salir = false;
35
36
37 while ( !salir )
38 {
39 if ( contador_tiempo_juego )
40 {
41 while ( contador_tiempo_juego )
42 {
43 actualiza_juego();
44
45 contador_tiempo_juego--;
46 }
47
48 clear_to_color(buffer, 0x00000);
49
50 pinta_juego();
51
52 pintar_pantalla();
53
54 }else{
55
56 rest(1);
57 }
58 // tecla de salida
59 if ( key[KEY_ESC] ) salir = true;
60 }
61
62 destroy_bitmap(buffer);
63
64 return 0;
65}
66END_OF_MAIN();

Para este programa se utilizan cuatro archivos, y este cdigo anterior se debe de guardar con el nombre main.cpp.

Este es el cdigo principal, en el se incluyen las libreras que se utilizan como allegro y tres archivos mas: global, player y mijuego.
Estos cdigos sern explicados en las prximas entradas.

Tiene una funcin llamada inicia_allegro(), que se encarga de inicializar el entorno de la librera allegro, define una ventana con el
tamao dado por las variable pantalla_ancho y pantalla_alto.
Inicializa la variable buffer con un bitmap del mismo tamao que la ventana. Y prepara las funciones para el control de los frames por
segundo (FPS).

En el programa principal se repite todo mientras no sea salir. Para salir debes de pulsar la tecla ESC. Mientras se estar actualizando
el juego, borra la pantalla, pinta el juego y lo muestra por pantalla. Todo esto se repetir segn los FPS que se hayan puesto, que para
nuestro ejemplo ser 30 veces por segundo.

Para que no se haga muy largo lo he dividido en varias partes. Contina en la prxima entrega.

GLOBAL.H

El siguiente cdigo corresponde al archivo global.h, que contiene todas las variables y funciones globales de nuestro programa.

?
1 /* GLOBAL.H
2
3 */
4
5 // Ancho y alto de la pantalla
6 const int PANTALLA_ANCHO = 800;
7 const int PANTALLA_ALTO = 600;
8
9 // En este BITMAP dibujaremos todo
10BITMAP *buffer;
11
12// es el espacio en pixel que recorre el jugador al andar
13const int desplazamiento=4;
14
15
16// Copiar el buffer a la pantalla del juego (screen)
17void pintar_pantalla()
18{
19 blit(buffer, screen, 0, 0, 0, 0, PANTALLA_ANCHO, PANTALLA_ALTO);
20}
21
22// controla el bucle principal
23bool salir;
24
25// Variable usada para la velocidad
26volatile unsigned int contador_tiempo_juego = 0;
27
28const int FRAME_RATE = 30;
29
30// Funcin para controlar la velocidad
31void inc_contador_tiempo_juego()
32{
33 contador_tiempo_juego++;
34}
35END_OF_FUNCTION(inc_contador_tiempo_juego)

Se define dos constantes para indicar el tamao de la pantalla, PANTALLA_ANCHO y PANTALLA_ALTO.

Se define la variable buffer del tipo BITMAP, que se utilizar para almacenar las imagenes antes de mostrar por pantalla.

La constante desplazamiento indica el numero de pixel que se desplaza el personaje cada vez que se pulsa alguna tecla de direccin.
La funcin pintar_pantalla, es la que se encarga de volcar el contenido de la variable buffer a la pantalla ( screen ).

Se define la variable salir como un tipo boolean.

Y el resto se crea para el control de los FPS, la variable contador_tiempo_juego cuenta los ticks o veces que se llama la funcin.

PLAYER.H

?
1 // players.h
2
3 // Esta clase se encarga del manejo del jugador
4 class player
5 {
6 BITMAP *prota;
7 int x,y;
8 int direccion;
9 int animacion;
10
11 public:
12 void inicia();
13 void pinta();
14 void teclado();
15 int getx(){ return x; };
16 int gety(){ return y; };
17 void posiciona( int _x, int _y);
18};
19
20
21void player::inicia()
22{
23 prota = load_bmp("personaje.bmp",NULL);
24 // inicializar vbles
25 direccion = 0;
26 animacion = 0;
27 x = 540;
28 y = 280;
29}
30
31
32void player::pinta()
33{
34 masked_blit(prota, buffer, animacion*32, direccion*32, x, y, 32,32);
35}
36
37void player::teclado()
38{
39 int ax = x;
40 int ay = y;
41 // teclas control usuario
42 if ( key[KEY_UP] )
43 {
44 y-=desplazamiento;
45 direccion = 3;
46 }
47 if ( key[KEY_DOWN] )
48 {
49 y+=desplazamiento;
50 direccion = 0;
51 }
52 if ( key[KEY_LEFT] )
53 {
54 x-=desplazamiento;
55 direccion = 1;
56 }
57 if ( key[KEY_RIGHT] )
58 {
59 x+=desplazamiento;
60 direccion = 2;
61 }
62 if ( ax != x || ay != y )
63 {
64 // entra si a cambiado alguna de las variables x,y
65 animacion++;
66 if ( animacion > 2 ) animacion = 0;
67 }
68
69 // limites globales
70 if ( x < 0 ) x = 0;
71 if ( x > PANTALLA_ANCHO ) x = PANTALLA_ANCHO;
72 if ( y < 0 ) y = 0;
73 if ( y > PANTALLA_ALTO ) y = PANTALLA_ALTO;
74}
75
76void player::posiciona( int _x, int _y)
77{
78 x=_x;
79 y=_y;
80}

En este archivo esta definida la clase player, que se encarga de todas las funciones del jugador.
La funcin inicia se encarga de dar valores a las variables del personaje para situarlo dentro de nuestro escenario.

La funcin pinta se encarga de pintar el personaje, sobre el buffer, segn las variables sita al personaje en una posicin concreta.

La funcin teclado se encarga de controlar todas las teclas que manejan al personaje. En este caso se utilizan las teclas de flechas de
cursor.

mijuego.h

En este archivo se pondr lo mas importante que es nuestro juego.

?
1 /*
2 mijuego.h
3
4 */
5
6 BITMAP *fondo;
7 BITMAP *choque;
8 BITMAP *alto;
9
10player jugador;
11
12
13// carga todo lo necesario antes de empezar el juego
14void carga_juego()
15{
16 jugador.inicia();
17 // cargamos imagenes del primer escenario
18 fondo = load_bmp("casa.bmp",NULL);
19 choque = load_bmp("casa-choque.bmp",NULL);
20 alto = load_bmp("casa-sup.bmp",NULL);
21}
22
23
24// actualiza el estado del juego
25void actualiza_juego()
26{
27 int ax,ay;
28 ax = jugador.getx();
29 ay = jugador.gety();
30 jugador.teclado();
31
32 // comprobar si colisiona con el mapa
33 bool choca = false;
34 int px = jugador.getx()-160;
35 int py = jugador.gety()-160+16;
36 for ( int ci=0; ci < 32; ci++)
37 {
38 for (int cj=0; cj < 16; cj++)
39 {
40
41 if ( getpixel( choque, px+ci, py+cj) == 0xff0000 ){
42 choca = true;
43 ci = 32;
44 cj = 16;
45 }
46 if ( getpixel( choque, px+ci, py+cj) == 0x00ff00 ) salir = true;
47 }
48 }
49 if ( choca ){
50 // vuelve al estado anterior
51 jugador.posiciona( ax,ay );
52 }
53
54}
55
56
57
58// Se encarga de pintar todo sobre el buffer
59void pinta_juego()
60{
61 blit( fondo, buffer, 0,0, 160, 160, 480,325);
62 jugador.pinta();
63 masked_blit( alto, buffer, 0,0, 160, 160, 480,325);
64}

Como ya se explic al principio, se declaran las variables fondo, choque, alto para almacenar las imagenes, y se crea una variable
jugador del tipo player.

La funcin carga_juego se encarga de cargar las imgenes que se van a utilizan para nuestro escenario e inicializar al jugador.

La funcin actualiza_juego se encarga de llamar a la funcin jugador.teclado() para actualizar la posicin del jugador y a
continuacin comprueba si colisiona con el mapa. En el caso de colisionar vuelve al estado anterior.

Y aqu acaba todo, si no ha habido ningn problema deberas tener a tu personaje paseando por el primer escenario tal y como se
muestra en el siguiente vdeo.
Las imgenes que se utilizan para este ejemplo las puedes descargar haciendo click aqu o en la seccin de descargas.

Você também pode gostar