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

martes, 26 de julio de 2011

GameEngine: Capitulo 25 . FoeManager

Hola a todos,

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

Ya tenemos una estructura bastante solida donde se está asentando un videojuego más o menos serio: podemos movernos, disparar, pero, o vaya!! no tenemos a quien disparar!!. El capitulo de hoy se basa en la implementación básica del FoeManager (Gestor de enemigos).

Al ser una primera implementación que nos interesa que sea sencilla no pondremos ni movimiento de los enemigos ni que disparen, solamente que se estén quietos y reciban balazos (que más se les puede pedir? :D )

Aparte del FoeManager (.cpp y .h) se han modificado ligeramente el BulletManager (para que determine si un enemigo ha recibido un balazo) y el tilemanager para que lea la posición donde ha poner cada uno de los enemigos. El código del proyecto lo podeis pillar de la zona de descargas del megaupload (podeis encontrarlo en http://lordpakus.blogspot.com/2011/07/indice-del-lordpakusblog.html)...

FoeManager.h
/**************************************************************************************************/
// 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.h : 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
/**************************************************************************************************/

#ifndef __FoeManager__
#define __FoeManager__

#define MAX_FOES 100 //Número máximo de enemigos en escena

typedef struct
{
bool active; //Nos indica si el enemigo está activo o no.
int x,y; //Posición
char cad[32]; //Etiqueta de la animación a usar
}Foe;

class FoeManager
{
private:
// Constructor y destructor de la clase
static FoeManager instance;
FoeManager();
~FoeManager();
public:
static FoeManager& singleton();

public:
void Init(); //Initiliaze the bullet manager
void Draw();
void Spawn(int x, int y, char anim[]);
void KillAll();

private:
Foe foe_array[MAX_FOES];
};
#endif



FoeManager.cpp

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

for(i = 0 ; i< MAX_FOES ; i++)
foe_array[i].active = false;
}

void FoeManager::Spawn(int x, int y, char anim[])
{
int i;

for( i = 0 ; i < MAX_FOES; ++i )
{
//Si este enemigo no está activado, lo activamos
if(foe_array[i].active == false )
{
cout << "Hacemos que salga un enemigo : " << x << " " << y << " " << anim << "\n";

//Seteamos los gráficos de la bala
foe_array[i].active = true;
foe_array[i].x = x;
foe_array[i].y = y;
sprintf( foe_array[i].cad , anim );
break;
}
}
}

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

void FoeManager::Draw()
{
int i;

for( i = 0 ; i < MAX_FOES; ++i )
{
//Si la bala está activada
if(foe_array[i].active == true )
{
//Pintamos
LPE.DrawAnim(foe_array[i].x, foe_array[i].y, foe_array[i].cad);

//Colisión con las tiles
foe_array[i].y-=5;
if (LPE.CollideAnimWithMap2D(foe_array[i].x, foe_array[i].y, foe_array[i].cad))
{
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, foe_array[i].cad))
{
foe_array[i].active = false;
}
}
}
}


El video de nuestro personaje matando enemigos lo podeis ver aquí:



Sin más, aquí me despido, espero que os lo hayáis pasado tan bien como yo y que os vayáis preparando para el siguiente capítulo.

LordPakusBlog
Nos vemos.

Entradas populares