jueves, 28 de julio de 2011

GameEngine: Capitulo 26 . Mejorando el FoeManager

Hola a todos,

Bienvenidos a un nuevo capitulo de como crear tu propio game engine...

El otro dia implementamos un gestor muy muy sencillo de enemigos, hoy, pese a que no lo acabaremos (los enemigos no dispararán todavía por ejemplo), le implementaremos nuevas funcionalidades.

De entre estas funcionalidades cabe destacar que todo está en forma de pseudo scripts : las animaciones de los enemigos, su tipo de inteligencia artificial (por ahora solo tendremos un patrullero), y sus parametros de funcionamiento tales como su tiempo de reacción.

Para conseguir esto se han tocado varios archivos pero principalmente se ha modificado el FoeManager...

A continuación podreis ver el código del FoeManager.cpp (el .h no varia demasiado):

/**************************************************************************************************/
// Código creado por F.Bordas (LordPakus) como ejemplo de creación de un game engine
// para el blog LordPakus (http://lordpakus.blogspot.com/).
// Prohibida la distribución fuera de este blog sin el permiso expreso del autor
/**************************************************************************************************/

/**************************************************************************************************/
// FoeManager.cpp : Código de la parte de enemigos del game engine
// Caracteristicas especiales: Esta clase implementa un singleton, es decir, solo podrá existir un objeto de esta clase en todo el proyecto
/**************************************************************************************************/
#include "FoeManager.h"
#include "MyGL.h"
#include "Core.h"
#include "BulletManager.h"
#include <iostream> //Usada para imprimir por consola

using namespace std;

#include <math.h> //Funciones de matematicas.

#define PI 3.14159265

//Instancia única del foe manager
FoeManager FoeManager::instance;

FoeManager::FoeManager()
{
}

FoeManager::~FoeManager()
{
}

//Devolvemos el puntero al singleton
FoeManager& FoeManager::singleton()
{
return instance;
}

void FoeManager::Init(int* argc, char* argv[])
{
char cad[256]; //Cadena donde guardaremos cada linea que leamos
char id[256]; //Cadena donde guardaremos el identificador de cada recurso
char ruta[256]; //Cadena donde guardaremos la ruta de cada recurso
int num_id; //número que nos asigna la función de carga del recurso.... un -1 significa que no lo ha podido cargar
FILE *fp;

cout << "Inicializamos el foe manager\n";
//Limpiamos todos los enemigos
KillAll();

num_type_foe = 0; //Inicializamos a 0 el número de tipos de enemigos cargados.

filemanager.Init("Data\\LoadFoe.txt");

//Todo este código tendrá que ir al filemanager, pero por el momento lo dejamos aquí
//Leemos cada linea del archivo
while( filemanager.GetLine(cad) )
{
for( int i = 0 ; i < strlen(cad); ++i)
{
if ( cad[i] == '=' )
{
cad[i]='\0';
sprintf(id,"%s",cad);
sprintf(ruta,"%s",&cad[i+1]);
i = 257;
}
}

cout << "Intentamos cargar tipo de enemigo con ruta:" << ruta << "\n";
fp = fopen(ruta,"r");

if (fp == NULL)
continue;

sprintf(type_foe_array[num_type_foe].id,"%s",id);
//tile con la que se activa
fscanf(fp,"%s\n",cad); //leemos primera linea
type_foe_array[num_type_foe].tile = cad[0];

//Etiqueta de la animación a usar mientras espera
fscanf(fp,"%s\n",cad); //leemos linea
cout << cad << "\n";
sprintf(type_foe_array[num_type_foe].stand,"%s",cad);

//Etiqueta de la animación a usar mientras corre
fscanf(fp,"%s\n",cad); //leemos linea
cout << cad << "\n";
sprintf(type_foe_array[num_type_foe].run,"%s",cad);
//Tipo de IA
fscanf(fp,"%s\n",cad); //leemos linea
cout << cad << "\n";
sprintf(type_foe_array[num_type_foe].type,"%s",cad);
//Vida Maxima
fscanf(fp,"%d%s\n",&(type_foe_array[num_type_foe].life),cad); //leemos linea

//Munición Maxima
fscanf(fp,"%d%s\n",&(type_foe_array[num_type_foe].ammo),cad); //leemos linea

//Milisegundos entre disparo y disparo
fscanf(fp,"%d%s\n",&(type_foe_array[num_type_foe].reaction),cad); //leemos linea

fclose(fp);
cout << type_foe_array[num_type_foe].id << " " << type_foe_array[num_type_foe].tile << " " << type_foe_array[num_type_foe].run << " "
<< type_foe_array[num_type_foe].stand << " " << type_foe_array[num_type_foe].type << " " << type_foe_array[num_type_foe].life << " "
<< type_foe_array[num_type_foe].ammo << " " << type_foe_array[num_type_foe].reaction << "\n";

cout << "Tipo de enemigo cargado con identificadores:" << id << " " << num_type_foe << "\n";

num_type_foe++;
}
}

void FoeManager::DeInit()
{
cout << "Desinicializamos el FoeManager\n";
}

void FoeManager::Spawn(int x, int y, char id)
{
int i,j;

for( i = 0 ; i < MAX_FOES; ++i )
{
//Si este enemigo no está activado, lo activamos
if(foe_array[i].active == false )
{
for(j = 0 ; j < num_type_foe ; j++ )
{
if (id == type_foe_array[j].tile )
{
cout << "Hacemos que salga un enemigo : " << x << " " << y << " " << id << "\n";
foe_array[i].active = true;
foe_array[i].x = x;
foe_array[i].y = y;
foe_array[i].tile = type_foe_array[j].tile;
//sprintf( foe_array[i].type_foe , "%s" , type_foe_array[j].type );
foe_array[i].life = type_foe_array[j].life;
foe_array[i].ammo = type_foe_array[j].ammo;
break;
}
}
break;
}
}
}

void FoeManager::KillAll()
{
int i;
//Matamos a todos los enemigos
for( i = 0 ; i < MAX_FOES; ++i )
{
foe_array[i].active = false;
foe_array[i].time = 0;
}
}


void FoeManager::IA(int i , int id , char *anim)
{
if( (clock() - foe_array[i].time) < type_foe_array[id].reaction)
{
//Si no ha pasado el tiempo de reacción, el bicho se quedará bastante igual
sprintf(anim, "%s" , foe_array[i].last_anim);
return;
}

foe_array[i].time = clock();

//Aplicamos la IA
if (!strcmp( type_foe_array[id].type , "PATROL" ) )
{
//cout << "aplicamos IA patrol\n";
//El 10% del tiempo estará parado
if( (rand()%100) < 20)
{
//cout << "Paramos\n";
sprintf(anim, "%s" , type_foe_array[id].stand );
}
else
{
//cout << "Nos movemos\n";
sprintf(anim, "%s" , type_foe_array[id].run );

if (foe_array[i].right)
{
//cout << "Derecha\n";
foe_array[i].x += 5;
if (LPE.CollideAnimWithMap2D(foe_array[i].x, foe_array[i].y, anim))
{
foe_array[i].x-=6;
foe_array[i].y+=5;
}
}
else
{
//cout << "Izquierda\n";
foe_array[i].x -= 5;
if (LPE.CollideAnimWithMap2D(foe_array[i].x, foe_array[i].y, anim))
{
foe_array[i].x+=6;
foe_array[i].y+=5;
}
}
}

if( (rand()%100) == 1)
foe_array[i].right = !foe_array[i].right;
}

sprintf(foe_array[i].last_anim , "%s" , anim );
}


void FoeManager::Draw()
{
int i,j,id;
char anim[32];

for( i = 0 ; i < MAX_FOES; ++i )
{
//Si el enemigo está activado
if(foe_array[i].active == true )
{
//Buscamos que tipo es
for( j = 0 ; j < num_type_foe ; ++j)
{
if(foe_array[i].tile == type_foe_array[j].tile)
{
id = j;
break;
}
}
sprintf(anim, "%s" , type_foe_array[id].stand );

IA(i,id,anim);

//cout << "Pintamos enemigo con animación : " << id << " " << foe_array[i].type_foe << " " << anim << "\n";
if ( !foe_array[i].right)
LPE.EnableFlag("INVX");

//Pintamos
LPE.DrawAnim(foe_array[i].x, foe_array[i].y, anim);

//Colisión con las tiles
foe_array[i].y-=5;
if (LPE.CollideAnimWithMap2D(foe_array[i].x, foe_array[i].y, anim))
{
foe_array[i].y+=5;
}
//En pruebas, si recibe un disparo, se muere.
if (BulletManager::singleton().Shooted(foe_array[i].x, foe_array[i].y, anim))
{
foe_array[i].active = false;
}

LPE.DisableFlag("INVX");
}
}
}


A nivel de pseudo scripts cabe destacar como nuevo el de LoadFoe.txt
foe1=Data\\Foes\\foe1.txt
foe2=Data\\Foes\\foe2.txt
foe3=Data\\Foes\\foe3.txt

Ejemplo del primer enemigo:
A //Tile que hace que "salte"
STAND_F1 //Animación de espera
RUN_F1 //Animación de correr
PATROL //Tipo de IA
100 //Vida
100 //munición
250 //tiempo entre reacción y reacción en milisegundos


Cual es el resultado??' En el video podeis ver como queda uno de los niveles:



Aparte de todo esto a fin de ahorrar espacio he comprimido en un solo archivo todas carpetas desde el capitulo 10 hasta el actual(26), lo teneis en la siguiente ruta: http://www.megaupload.com/?d=LDHJAHQW

LordPakusBlog
Espero que lo hayais pasado bien y que aprendais. Si teneis dudas o sugerencias no dudeis en plantearlas.

Nos vemos

3 comentarios :

  1. Buenas tardes.
    Me gustaría saber si podemos comunicarnos mediante msn o skype, u otra herramienta de mensajería instantánea. Te comento a ver si puedes ayudarme...
    Tengo que hacer un trabajo para el instituto, y necesito hacerle una entrevista a alguien de tu nivel, alguien que tenga claro que es la programación.
    Así que si, me puedes ayudar pues nada yo también tengo un blog, creo que hay me podrás encontrar o si respondes a este mensaje. Me paso, y te envio mi msn por privado o algo.
    Gracias Pako!
    Y un gran trabajo lo explicado anteriormente.
    Saludos!

    ResponderEliminar
  2. Si no es muy larga la entrevista, en 5 minutos puedeo estar en el gtalk... lordpakusblog@gmail.com (no tengo mucho tiempo pero por ayudar no cuesta nada)

    Nos vemos

    ResponderEliminar
  3. Bueno pues, ya hemos contactado y en breve me hará la entrevista (miedo me da :D ).

    El resto podeis ver que no es demasiado dificil contactar conmigo, si os interesa, ya lo sabeis....

    ResponderEliminar

Entradas populares