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
Espero que lo hayais pasado bien y que aprendais. Si teneis dudas o sugerencias no dudeis en plantearlas.
Nos vemos