domingo, 31 de marzo de 2013

PakEngine. Capitulo 18. Interficie C++. Clase Sprite.

Artículo perteneciente a la sección del PakEngine

Hola a todos,

No hace mucho que escribí un articulo que trataba sobre la pereza de reescribir código y sus efectos malignos en el código. Y como no podía ser de otra manera debo aplicar lo que digo.

El caso es que hice un error de diseño bastante grande presuponiendo que la interficie de C y C++ podrían ser idénticas cambiando solamente la nomenclatura, pero esto , obviamente, no es posible. Si lo hiciera, la complejidad ciclomática se me dispararía y acabaría por abandonar el proyecto, así que las cosas quedan de la siguiente manera:
Interficie C: Se podrán realizar pintados normales y con rotación. No se podrá hacer más por que sino la complejidad aumentaría de forma exponencial.
Interficie C++: Se trabajará directamente sobre objetos de tipo sprite que permitirán multitud de modificaciones sobre el pintado. Esto es así por que la estructura usada en C++ permite crecer el número de funcionalidades sin aumentar prácticamente la complejidad.

El código del drawobject (el objeto que se encarga de realizar el pintado final ) queda tal que así:

DrawObject::DrawObject(int tx, int ty, int tz, int tid, float tangle,float tescale, bool tflipx, bool tflipy)
{
x = tx;
y = ty;
z = tz;
id = tid;
angle = tangle;
escale = tescale;
flipx = tflipx;
flipy = tflipy;
}

void DrawObject::Draw(void)
{
int tex,w,h;
int center_x,center_y;
float xini,yini,xend,yend;

tex = SPRITELIST_GetTexture(id);
w = SPRITELIST_GetWidth(id);
h = SPRITELIST_GetHeight(id);

w *= escale;
h *= escale;

w/= 100.0;
h/= 100.0;

glAlphaFunc(GL_GREATER,0.1f);
glEnable(GL_ALPHA_TEST);

glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, tex);
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );

center_x = x + w/2 ;
center_y = y + h/2 ;

if(flipx)
{
xini = 1.0f;
xend = 0.0f;
}
else
{
xini = 0.0f;
xend = 1.0f;
}

if(flipy)
{
yini = 1.0f;
yend = 0.0f;
}
else
{
yini = 0.0f;
yend = 1.0f;
}

glPushMatrix();

glTranslatef( (GLfloat) center_x, (GLfloat) center_y, 0 );
glRotatef( angle , 0, 0, 1 );
glTranslatef( - (GLfloat) center_x, - (GLfloat) center_y, 0 );

glBegin(GL_QUADS);
glTexCoord2f( xend, yend); glVertex2i( x + w , y + h );
glTexCoord2f( xini, yend); glVertex2i( x     , y + h );
glTexCoord2f( xini, yini); glVertex2i( x     , y  );
glTexCoord2f( xend, yini); glVertex2i( x + w    , y );
glEnd();

glPopMatrix();

};


Y la interficie de la clase sprite la podéis encontrar en el svn dentro del pakengine.h (para que sea accesible desde el juego). No está acabada pero si que tiene lo más importante.

class DLLEXPORT Sprite
{
private:
int graf;
int x,y,z;
float angle;
float escale;
bool flipx,flipy;
bool visible;

public:
Sprite(void);
~Sprite(void);

//Seters
void SetGraf(int id);
void SetGraf(char *cad);

void Move(int incx, int incy);
void Move(int incx, int incy, int incz);

void MoveTo(int x, int y);
void MoveTo(int x, int y, int z);

void Rotate(float inc_angle);
void RotateTo(float angle);

void Escale(float inc_escale);
void EscaleTo(float escale);

void FlipX(void);
void FlipY(void);
void FlipXY(void);

void RestoreToDefaults(void);

void Push(void);
void Pop(void);

void SetVisible(bool visible);

//Geters
int GetX(void);
int GetY(void);
int GetZ(void);

float GetAngle(void);
float GetEscale(void);

bool IsFlippedinX(void);
bool IsFlippedinY(void);

bool IsVisible(void);

//Others
void Draw(void);
};




Aquí teneís un ejemplo de código de como usar la nueva clase de pintado:

int main(void)
{
Sprite *fondo;
Sprite *nave[10] = {0};
int i;

PakEngine p("Pakteroids",1024,750);

fondo = new Sprite();
fondo->SetGraf("Resources\\estrellas.jpg");
fondo->MoveTo(0,0,100);
fondo->SetVisible(true);

for( i = 0 ; i < 10 ; ++i)
{
nave[i] = new Sprite();
nave[i]->SetGraf("Resources\\nave.png");
nave[i]->SetVisible(true);
}

nave[0]->MoveTo(100,100,20);

nave[1]->MoveTo(200,200,30);
nave[1]->EscaleTo(0.0);

nave[2]->MoveTo(300,500,40);
nave[2]->EscaleTo(0.0);

nave[3]->MoveTo(100,200,25);
nave[3]->EscaleTo(45.0f);

nave[4]->MoveTo(200,550,25);
nave[4]->EscaleTo(-45.0f);

nave[5]->MoveTo(400,100,50);
nave[5]->FlipX();

nave[6]->MoveTo(500,650,50);
nave[6]->FlipY();

nave[7]->MoveTo(600,100,50);
nave[7]->FlipXY();

nave[8]->MoveTo(700,500,60);
nave[8]->FlipY();

nave[9]->MoveTo(800,600,70);
nave[9]->FlipXY();
nave[9]->EscaleTo(300.0f);

while(p.Render())
{
fondo->Draw();

for( i = 0 ; i < 10 ; ++i)
{
if(nave[i])
nave[i]->Draw();
}

nave[0]->Rotate(0.1f);

nave[1]->Escale(1.0f);

nave[2]->Escale(0.5f);
nave[2]->Rotate(-0.5f);

nave[8]->Rotate(2.0f);

nave[9]->Escale(-1.0f);
}
}



Y aquí os dejo el video de muestra de como funciona la clase Sprite


Espero que os haya gustado y os animéis a hacer vuestras primeras pruebas.

Nos vemos.

LordPakusBlog

viernes, 29 de marzo de 2013

La importancia de reescribir el código.

Artículo perteneciente a la sección de calidad de código
Artículo íntimamente relacionado con como mejorar la calidad del código

Hola a todos,

Bajo mi punto de vista hay dos factores importantes que pueden destruir nuestros proyectos de software:

- Duplicación de código: La duplicación de código es según los padres de TDD la principal causa de mala calidad de los códigos actuales. Duplicación de código tanto es ctrl+c, ctrl+v como funciones repetidas mal parametrizadas,  parámetros de funciones innecesarios,etc.... Todos estos problemas los arregla TDD y más concretamente la refactorización.

- Pereza/miedo a reescribir trozos de código erróneos o poco mantenibles: A todos nos ha pasado que cuando hemos acabado un proyecto o una parte importante de él, nos hemos dicho : "o vaya!! esto lo podría haber hecho mucho mejor.... de hecho, debería arreglarlo ahora mismo por que sino todo lo que se construya encima va a sufrir bastante.... pero.... me da miedo/pereza, lo dejo como está que así funciona...".  Y así nos va.  Aunque parezca una perdida de tiempo,  reescribir un trozo de código dándole un enfoque más mantenible o más robusto ante los cambios es extremadamente necesario.

Para dar un ejemplo más concreto. En el PakEngine estaba creando una interficie para C y otra idéntica para C++ para que se pudiese utilizar de cualquier manera, ambas devolvían siempre tipos básicos (ints,chars, etc...) de forma que la interficie era sencilla e intuitiva. Ahora, 17 capítulos después de haber tomado esa decisión. me he dado cuenta que cualquier cosa que haga a partir de ahora me hará crecer de forma exponencial el número de funciones de la interficie.

La explicación es muy sencilla. Imaginemos una interficie de pintado de gráficos  como la que tenemos en el motor, a la que le iremos incorporando nuevas funcionalidades:
- Al principio tenemos solamente una función  de pintado: Draw. Pinta un gráfico en determinadas coordenadas.

- Queremos incorporar las rotaciones de gráficos, así que tendremos dos funciones: Draw y DrawRotate

- Queremos incorporar el escalado de imagenes, así que tendremos 4 funciones :
1. Draw
2. DrawRotate
3. DrawEscale
4. DrawRotateEscale

- Queremos incorporar flip en x y flip en y, así que las funciones que tendremos al final serán:
1. Draw
2. DrawRotate
3. DrawEscale
4. DrawFlipX
5. DrawFlipY
6. DrawRotateEscale
7. DrawRotateFlipX
8. DrawRotateFlipY
9. DrawRotateFlipXY
10. DrawEscaleFlipX
11. DrawEscaleFlipY
12. DrawEscaleFlipXY
13. DrawRotateEscaleFlipX
14. DrawRotateEscaleFlipY
15. DrawRotateEscaleFlipXY
16. DrawFlipXY

1 función,2 funciones,4 funciones.... 16 funciones!!! Como entenderéis esta estructura de código no es mantenible por ningún lugar.

Algún lector avispado podría decir que todas estas características podrían unirse en una sola función con muchos parámetros del aspecto de la siguiente:
Draw (int graf, int x, int y, int z, float rotate_angle, float escale_factor, bool flipx,  bool flipy).

El problema de esta "solución" es que se aumentaría muchísimo la complejidad del sistema para pintados sencillos  e implicaría cambios continuos en el código de los juegos a cada mejora del motor (cada característica nueva implicaría cambiar todas las llamadas de pintado).

La única solución lógica (aunque de pereza hacerlo :D) es aceptar que no se escogió una buena solución y proponer una interficie que se base en objetos, y que estos objetos contengan gráficos que acepten modificaciones. De esta manera la complejidad queda reducida y se permite seguir aumentando el número de características sin problemas.

En breve colgaré un capítulo en la sección del pakengine que os dejará mucho más claro este concepto.

Espero que os haya gustado y servido

Nos vemos,
LordPakusBlog

jueves, 28 de marzo de 2013

PakEngine. Capitulo 17. Alphas.


Artículo perteneciente a la sección del PakEngine

Hola a todos,

El capítulo de hoy trata sobre los alphas. Los alphas no son más que la gestión de transparencias durante el pintado de imágenes.

Básicamente hay dos tipos:

Alpha Blending: También se le llama semitransparencia. Es cuando hacemos que el pintado de un gráfico deje entrever lo que había debajo. Es un efecto que está bastante bien, pero por ahora no tengo planteado implementarlo en el motor. (si a alguien se le ocurre un buen motivo para que si, que lo diga)

Alpha Test: También llamado máscara de recorte. O pinta, o no pinta, pero no hay semitransparencias. Este es el alpha que hemos implementado y se usa para tener un recorte de los gráficos en movimiento. Es decir, si pintamos una nave dando vueltas, queremos que el jugador solo vea la nave, no todo el recuadro que la envuelve.

Para implementar el alpha test en el motor es tan tonto como copiar las siguientes lineas en la función de pintado (DrawObject::Draw)

Si queréis más información sobre los alphas podéis mirar el siguiente link

Y ya para finalizar, os dejo un video que os mostrará la implementación en el pakengine de las alphas y las rotaciones... espero que os guste



Nos vemos

LordPakusBlog

miércoles, 27 de marzo de 2013

PakEngine. Capitulo 16. Rotaciones

Artículo perteneciente a la sección del PakEngine

Hola a todos,

El capitulo de hoy va sobre la rotación de gráficos.

La forma más fácil de aplicar rotaciones en un motor gráfico como el nuestro que se basa en opengl es realizar rotaciones a nivel de opengl. El pequeño problema que hay es que las rotaciones se hacen de la pantalla completa así que tendremos que hacer pequeños trucos para que no sirva para nuestros motor.
1. Deberemos guardar la matriz de transformación como la tenemos.
2. Deberemos desplazar el gráfico a rotar al origen de coordenadas.
3. Rotaremos toda la pantalla.
4. Desplazaremos el gráfico a su posición final.
5. Restauraremos la matriz de transformación de opengl.

Esto en código es tan sencillo como esto:


void DrawObject::Draw(void)
{
int tex,w,h;
int center_x,center_y;

tex = SPRITELIST_GetTexture(id);
w = SPRITELIST_GetWidth(id);
h = SPRITELIST_GetHeight(id);

glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, tex);
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );

if(angle == 0)
{
glBegin(GL_QUADS);
glTexCoord2f( 1.0f, 1.0f); glVertex2i( x + w , y + h );
glTexCoord2f( 0.0f, 1.0f); glVertex2i( x     , y + h );
glTexCoord2f( 0.0f, 0.0f); glVertex2i( x     , y  );
glTexCoord2f( 1.0f, 0.0f); glVertex2i( x + w    , y );
glEnd();
}
else
{
center_x = x + w/2 ;
center_y = y + h/2 ;
glPushMatrix();
               glTranslatef( center_x, center_y, 0 );
               glRotatef( angle , 0, 0, 1 );
                glTranslatef( -center_x, -center_y, 0 );
                glBegin(GL_QUADS);
glTexCoord2f( 1.0f, 1.0f); glVertex2i( x + w , y + h );
glTexCoord2f( 0.0f, 1.0f); glVertex2i( x     , y + h );
glTexCoord2f( 0.0f, 0.0f); glVertex2i( x     , y  );
glTexCoord2f( 1.0f, 0.0f); glVertex2i( x + w    , y );
glEnd();
glPopMatrix();

}
};

Todo el código está subido al svn.

Espero que os sirva y os haya gustado.

Nos vemos

LordPakusBlog

El principio de la ventana rota

Artículo perteneciente a la sección de calidad del código

Hola a todos,

Hace un tiempo, leyendo un libro de TDD leí  un principio bastante divertido. El principio de la ventana rota.

Este principio se basa en que si unos crios de un balonazo se cargan un cristal, no pasa nada, se cambia el cristal y todo arreglado.
El problema viene cuando si nosotros, por desidida o falta de recursos o tiempo no cambiamos el cristal, ese cristal roto va a contagiar al resto de vecinos.

Cuando al vecino se le rompa un cristal de un golpe de aire, no lo va arreglar, por que total, "el del segundo no lo ha arreglado, por que debería hacerlo yo?"... esta cadena iria sucediendo hasta que al final la gente rompiese queriendo los cristales de las ventanas para no desentonar con el resto de la vecindad.

Aunque este principio es una clara exageración en el mundo real, resulta que coincide exactamente con lo que pasa en el mundo de la programación. La frase "total, para como está el código no hace falta hacerlo mejor" seguro que la habeís oido (e incluso dicho) en más de una ocasión.

Todo esto viene por que ahora, con el desarrollo del pakengine me ha pasado una cosa parecida. Voy falto de tiempo, pero quiero tener regularidad en las entregas de los artículos y en las subidas al svn, así que aplico menos testeo TDD, se me van rompiendo las cosas, pero como no son del tema en concreto que estoy publicando las apunto para un futuro...., pero vaya, el futuro al final llega con toda una bolsa de pequeños fallos por arreglar. Si decido no arreglarlos, se comienzan a crear fallos estructurales en el código y se condicionan malas decisiones de diseño....si los arreglo tengo que ir más lento en las entregas....  supongo que ya sabeís cual es la respuesta no???

La única manera de conseguir una buena calidad del código es parar cuando detectamos errores, sacar tiempo y recursos de donde sea y arreglarlo todo bien hecho y sin parches. Sino, las malas decisiones nos van a seguir durante todo el proyecto hasta matarlo (si quereis ejemplo mirad mis motores anteriores :D)

En fin, espero no haber sido demasiado catastrofista y que estos consejos os puedan servir en vuestros proyectos.

Nos vemos
LordPakusBlog

domingo, 24 de marzo de 2013

PakEngine. Capitulo 15. Documentación

Artículo perteneciente a la sección del PakEngine

Hola a todos,

La documentación es una cosa necesaria pero que raramente apetece hacer.

Cuando hablamos de documentación no me refiero a la explicación del código desarrollado (para eso ya está el propio código y los comentarios) sino a un resumen sencillo de como se trabaja con ese código desde fuera. Es decir, de que funciones dispone un programador al usar nuestro código.

La documentación ha de ser sencilla y clara (y veraz) permitiéndonos en menos 5 minutos saber si esa librería va a cumplir con nuestras necesidades o no.

Si no tenemos documentación, da igual cuan buena sea nuestra librería  será complicado usarla, así que no se usará. De la misma manera una documentación demasiado exhaustiva, centrándose en los detalles también es contraproducente ya que dificulta el aprendizaje inicial de nuestro producto, haciendo que la gente tampoco use nuestra librería.

Sin más , os dejo con la documentación del pakengine en el siguiente link.

La documentación está en castellano, catalán e inglés, aunque estos dos últimos los tengo traducidos a medias, si queréis acceder a la versión castellana la tenéis ya al 100%.

Espero que os guste y os sirva.

Nos vemos

LordPakusBlog

viernes, 22 de marzo de 2013

PakEngine. Capitulo 14. Interface C++

Artículo perteneciente a la sección del PakEngine

Hola a todos,

Ya hay bastantes personas que me han enviado mails con posibles mejoras del motor y con nuevas funcionalidades que estaría muy bien implementar.

La primera cosa de la que me he dado cuenta es ,que si realmente este motor va tirando adelante y vamos implementando lo que me estáis pidiendo, la interficie (el conjunto de funciones del PakEngine.h) será muy grande e inmanejable.

La solución a esto es hacer una interificie C++ que nos permita tener un poco más organizadas las funciones. Además el autocompletar de la mayoria de IDE nos permitirá acceder más fácilmente al contenido.

El código de la interficie es el siguiente:



class GraphInterface
{
public:
GraphInterface(){};
~GraphInterface(){};

public:
int Load(char *cad) { return PAKENGINE_LoadGraph(cad); };
void Draw(int x, int y, int z, int id) { PAKENGINE_DrawGraf(x,y,z,id); };
};

class TextInterface
{
public:
TextInterface(){};
~TextInterface(){};

public:
void Draw(int x, int y, int z, char *cad ) { PAKENGINE_DrawText( x, y,  z, cad ); };
};

class SoundInterface
{
public:
SoundInterface(){};
~SoundInterface(){};

public:
int Load(char *cad) { return PAKENGINE_LoadSound(cad); };
void Play(int sound) { PAKENGINE_Play(sound); };
};

class PhysicsInterface
{
public:
PhysicsInterface(){};
~PhysicsInterface(){};

public:
char Collision(int x1,int y1 ,int graf1,   int x2, int y2, int graf2)   { return PAKENGINE_Collision( x1, y1 , graf1,    x2,  y2,  graf2); };
char Collision(int x1,int y1 ,int graf1,   int x2, int y2, int graf2 , char *mode) { return PAKENGINE_Collision( x1, y1 , graf1,    x2,  y2,  graf2,  mode) ;};

char NotInside(int x1,int y1 ,int graf1,   int x2, int y2, int graf2)   { return PAKENGINE_NotInside( x1, y1 , graf1,    x2,  y2,  graf2); };
char NotInside(int x1,int y1 ,int graf1,   int x2, int y2, int graf2 , char *mode) { return PAKENGINE_NotInside( x1, y1 , graf1,    x2,  y2,  graf2,  mode) ;};
};

class InputInterface
{
public:
InputInterface(){};
~InputInterface(){};

char KeyIsPressed(char *cad) {return PAKENGINE_KeyIsPressed(cad);};
};

class PakEngine
{
public:

PakEngine(char *cad,int w,int h)
{
PAKENGINE_Init(cad,w,h);
};

~PakEngine()
{
PAKENGINE_DeInit();
};

int Render(void) {   return PAKENGINE_Render();   };
GraphInterface Graph;
TextInterface Text;
SoundInterface Sound;
PhysicsInterface Physics;
InputInterface Input;
};



Y en el editor de código queda de la siguiente forma (siendo p un objeto PakEngine):

PakEngine p("PakPong",640,480);

Aquellos de vosotros que queráis seguir programando en C puro no os preocupéis ya que la interficie C la seguiré manteniendo.

Espero que os guste.

Nos vemos,

LordPakusBlog

<< capitulo anterior

lunes, 18 de marzo de 2013

PakEngine. Capitulo 13. Primera "release"

Artículo perteneciente a la sección del PakEngine

Hola a todos.

No se si os habéis dado cuenta pero el motor, aunque modesto, ya tiene todo lo que hace falta tener para hacer nuestros primeros videojuegos.

He colgado en la zona de descargas de google code un rar con todo lo necesario para desarrollar mediante el PakEngine. Me iría muy bien que lo probarais y que me dijerais si queda claro o no el funcionamiento del motor:  https://code.google.com/p/pakengine-svn/downloads/list

Ahora me iré dedicando a mejorar el motor , darle nuevas funcionalidades y hacer que el código sea cada vez más mantenible. Me haría falta que lo fuerais probando para decirme que más cosas le faltarían al motor. Se aceptan propuestas.

Nos vemos
LordPakusBlog

<< capitulo anterior                                                                                          siguiente capitulo>>

PakEngine. Capitulo 12. Incluyendo sonido


Artículo perteneciente a la sección del PakEngine

Hola a todos,

En otros motores que he desarrollado siempre he usado OpenAL y alut  para los temas de audio, pero nunca me ha acabado de gustar. Solo acepta wav (y no con todas las configuraciones) y hacer que funcione nunca es fácil.

En cambio, ahora, buscando por internet he encontrado una libreria un poco anticuada pero que tiene todo lo que necesito: audiere  . Carga ogg, mp3,wav, mod y otros formatos y es extremadamente sencilla de usar e integrar con el resto del sistema.

El código del gestor de audio lo he distribuido en los ficheros SoundList.cpp y .h

SoundList.h

#ifndef __SOUNDLIST__
#define __SOUNDLIST__

//Máximo número de sonidos
#define MAX_SOUNDS 100

void SOUNDLIST_Init(void);
int SOUNDLIST_Insert(char cad[]);
void SOUNDLIST_Play(int sound);

#endif




SoundList.cpp
#include "SoundList.h"
#include <iostream>

#include "audiere.h"
using namespace audiere;

AudioDevicePtr* device;
OutputStreamPtr* soundlist[MAX_SOUNDS];
int index_sound = 0 ;

void SOUNDLIST_Init(void)
{
device = new AudioDevicePtr(OpenDevice());
}

int SOUNDLIST_Insert(char cad[])
{
soundlist[index_sound++] = new OutputStreamPtr(OpenSound(*device, cad, false));
return (index_sound-1);
}

void SOUNDLIST_Play(int sound)
{
(*soundlist[sound])->play();
}

Como podéis observar el código es extremadamente sencillo, aunque es cierto que aún le tengo que dar bastantes funcionalidades a la parte de audio, pero esto ya lo iremos viendo con el tiempo.

El código del proyecto lo podéis bajar del svn.

Y con la parte de audio finalizada creo que en breve podré hacer una primera release alpha de la libreria dll para que desarrolléis vuestros propios juegos. Os mantendré informados.

Nos vemos

LordPakusBlog

<<capitulo anterior                                                                                          siguiente capitulo >>

jueves, 14 de marzo de 2013

PakEngine. Capitulo 11.Incluyendo textos

Artículo perteneciente a la sección del PakEngine

Hola a todos,

En el capítulo anterior refactorizamos el render para prepararlo para nuevos elementos de pintado. Y en este capítulo vamos a implementar el pintado de textos con z's.

La idea es muy sencilla. Igual que para pintar un gráfico creabamos un objeto de tipo DrawObject que heredaba de  RenderOject, ahora para pintar texto crearemos un objeto de tipo TextObject que también heredará de RenderObject.



#ifndef __TextObject__
#define __TextObject__

#include "RenderObject.h"

class TextObject : public RenderObject
{
private:
char cad[80];

public:
TextObject(int tx,int ty, int tz, char *tcad);
~TextObject(){};
void Draw(void);
};

#endif


TextObject::TextObject(int tx, int ty, int tz, char *tcad)
{
x = tx;
y = ty;
z = tz;
strcpy(cad,tcad);
}

void TextObject::Draw(void)
{
glQuickText::printfAt(x,y,0.0f ,1.0f , cad );
};


Y así quedan las llamadas que podrá hacer el usuario:

DLLEXPORT void PAKENGINE_DrawGraf(int x, int y, int z, int id )
{
RENDERLIST_Insert(new DrawObject(x,y,z,id));
}

DLLEXPORT void PAKENGINE_DrawText(int x, int y, int z, char *cad )
{
RENDERLIST_Insert(new TextObject(x,y,z,cad));
}


El código lo podéis bajar ya del svn

Espero que os haya gustado

Nos vemos

LordPakusBlog

<< capitulo anterior                                                                                          siguiente capitulo>>

miércoles, 13 de marzo de 2013

PakEngine. Capitulo 10. Refactorizando el Render

Artículo perteneciente a la sección del PakEngine

Hola a todos,

Hace poco estaba metido con el tema de ponerle el pintado de textos con z al motor, pero, "oh sorpresa!!!" la complejidad ciclomática se me disparaba y los test TDD me petaban sin parar. Así que he decidido hacer un miniparón y pensar bien la solución, para refactorizar el código y que sea más fácil introducir nuevas características de pintado.

La solución a la que he llegado es la siguiente:
- El render gestiona  un vector de punteros a objetos de la clase RenderObject
- La clase RenderObject solo contiene la x,y,z del objeto a renderizar (ya sea un gráfico,texto, animación,etc..) y una función virtual llamada Draw.
- Se implemena una nueva clase llamada DrawObject que hereda de RenderObject e implementa la funcionalidad de Draw, permitiendo introducir nuevas características sin aumentar la complejidad del sistema.

Está todo el código subido al svn.


class RenderObject
{
public:
int x, y ,z;

RenderObject(){};
~RenderObject(){};
virtual void Draw() = 0;
int GetZ(void){ return z; };
};



class DrawObject : public RenderObject
{
private:
int id;

public:
DrawObject(int tx,int ty, int tz, int tid);
~DrawObject(){};
void Draw(void);
};




DrawObject::DrawObject(int tx, int ty, int tz, int tid)
{
x = tx;
y = ty;
z = tz;
id = tid;
}

void DrawObject::Draw(void)
{
int tex,w,h;

tex = SPRITELIST_GetTexture(id);
w = SPRITELIST_GetWidth(id);
h = SPRITELIST_GetHeight(id);

glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, tex);
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );

glBegin(GL_QUADS);
glTexCoord2f( 1.0f, 1.0f); glVertex2i( x + w , y + h );
glTexCoord2f( 0.0f, 1.0f); glVertex2i( x     , y + h );
glTexCoord2f( 0.0f, 0.0f); glVertex2i( x     , y  );
glTexCoord2f( 1.0f, 0.0f); glVertex2i( x + w    , y );
glEnd();
};

El código para introducir un nuevo gráfico queda tal que asi:

void SetElement(int index, int x, int y, int z, int id)
{
DrawList[index] = new DrawObject(x,y,z,id);
}

Y el render se reduce a..


void RENDER_Run(void)
{
RenderObject *elem = 0;

//Inspeccionamos todos los elementos a pintar
for( RENDERLIST_GetFirstElement(&elem) ; elem ; RENDERLIST_GetNextElement(&elem) )
elem->Draw();
[...]


Si os fijais, he usado C++ por primera vez en el proyecto . El motivo no es otro que por que hasta ahora no  me había encontrado con la necesidad de usarlo. Usar la herencia de clases y la sobrecarga de funciones virtuales me permite disminuir drásticamente la complejidad de la operación de render (si lo hiciera con C puro debería usar un switch que podría crecer de forma indefinida)

Gracias a esto, lo siguiente que haré será implementar el pintado de textos con z creando una nueva clase que herede de RenderObject y que tenga la función de Draw como a ella le interese, pero esto ya es tema del siguiente capítulo...

Nos vemos,
LordPakusBlog

<< capitulo anterior                                                                                         siguiente capitulo >>

sábado, 9 de marzo de 2013

PakEngine. Capitulo 0. Como empezar con el PakEngine

Artículo perteneciente a la sección del PakEngine

Hola a todos,

Como ya he recibido mails de varia gente que no sabe como descargarse el proyecto y empezar a probarlo he desarrollado este tutorial sencillito. Espero que os sea de utilidad.

Antes que nada, os tendréis que bajar un cliente de SVN. SVN no es más que un protocolo cliente-servidor muy usado para tener control de versiones. Es decir, nosotros trabajamos en nuestro ordenador y cuando ya tenemos algo probado y funcional lo subimos al servidor para que el resto se lo pueda bajar. Hay muchos clientes, yo os recomiendo el SVN Tortoise (dadle al link para ir a la zona de descargas de tortoise).

De mientras que se descarga el cliente de SVN buscaremos un editor/compilador de C/C++. Usad el que queráis. El que os recomiendo si queréis dar doble click sobre el proyecto y tenerlo todo montado es el VC++. Podéis encontrar una versión gratuita en el siguiente link (seleccionad VC++ 2010 Express).

Finalmente os deberéis bajar el proyecto del svn. Si no os aclaráis con las instrucciones de google code o no sabéis demasiado como funciona el svn, bajaos el siguiente bat de aqui y ejecutadlo donde queráis de vuestro ordenador. Lo único que hace el bat es ejecutar la siguiente linea:
svn checkout http://pakengine-svn.googlecode.com/svn/trunk/ PakEngine(LordPakus)

O lo que es lo mismo, pedirle que al servidor que le descarge las carpetas y archivos del proyecto PakEngine dentro de la Carpeta PakEngine(LordPakus).

Una vez lo hayáis ejecutado , si abrís la carpeta que os genera os deberían aparecer tres carpetas dentro que son la de los tres proyectos que conforman el motor:


PakEngine: Proyecto que generar la dll del motor
PakGame: Juego de prueba para testear a nivel de juego la dll
PakTest: Tests unitarios que nos aseguran que el código es correcto y funcional. ( TDD)

A medida que yo vaya subiendo cosas al servidor la copia que tenéis vosotros se quedará desfasada. Esto lo veréis por que en vez de un tick verde, veréis una exclamación roja encima de las carpetas en las que haya cambios.

La solución es fácil  le dais botón derecho encima de la carpeta conflictiva->TortoiseSVN->Update to Revision. Y en breve debería volverse a poner todo verde. Si no se pusiera verde de nuevo  (que esto a veces pasa :D), borra la carpeta y volved a ejecutar el bat del inicio, ya que ese bat siempre os bajará lo más actualizado.




Espero que os haya gustado y os haya servido para comenzar a ver como va el código del proyecto.

Nos vemos

LordPakusBlog

                                                                                                                 Siguiente capitulo >>

martes, 5 de marzo de 2013

PakEngine. Capítulo 9. Fisicas 2D : Colisiones e inclusiones.

Artículo perteneciente a la sección del PakEngine

Hola a todos,

De resultas del juego del pong me he visto "obligado" a desarrollar las colisiones y las inclusiones dentro del PakEngine.

La gracia de todo esto es que lo he desarrollado con los ojos cerrados usando TDD y los resultados son increíbles  El código que finalmente ha surgido no se me habría ocurrido nunca plantearlo de esa manera y además está testeado, así que en cualquier momento se puede mejorar sin temor a romper nada.

Si queréis ver el código lo tenéis en el repositorio:  Physics.cpp , PakEngine.cpp, PakPong.cpp

Os dejo el vídeo para que veáis que realmente funcionan correctamente las físicas 2D


Ya para acabar, tenemos que decir qué nos falta para que realmente podamos hablar de tener un primer juego acabado:
- Fuentes: Como mínimo bitmapeadas. Que podamos escribir la puntuación de cada jugador.
- Sonidos: Ni que sea poder reproducir 2 o 3 sonidos en mono para los rebotes de la bola.
- Que más nos faltaría? Se aceptan sugerencias.


Espero que os haya gustado,

Nos vemos

LordPakusBlog

<< capitulo anterior                                                                                         siguiente capitulo >>

domingo, 3 de marzo de 2013

PakEngine. Capitulo 8. Creando nuestro primer juego


Hola a todos,

Ya hemos empezado nuestro primer juego con el PakEngine!!. Un pong para variar :D.



El código del juego es el siguiente:

#include "..\PakEngine\PakEngine.h"
#include <stdlib.h>

int main(void)
{
  int bola,pala,campo;
  int x = 100,y= 100, xene = 400, yene= 200, xbola = 300, ybola = 300, xvel = 1, yvel = 1;

 //Creamos una ventana de tamaño determinado.
 PAKENGINE_Init("PakGame",640,480);

 bola  = PAKENGINE_LoadGraph("Resources\\bola.png");
 pala  = PAKENGINE_LoadGraph("Resources\\pala.png");
 campo = PAKENGINE_LoadGraph("Resources\\campo.png");

 //Loop de engine
 while(PAKENGINE_Render())
 {
    if(PAKENGINE_KeyIsPressed("up") && ( y > 10 ) )
y--;

if(PAKENGINE_KeyIsPressed("down") && ( y < 410 ) )
y++;


if(PAKENGINE_KeyIsPressed("left") && ( x > 10 ) )
x--;

if(PAKENGINE_KeyIsPressed("right") && (x < 310) )
x++;

xbola += xvel;
ybola += yvel;

if (xbola >= 620)
{
xbola = 619;
xvel = - (rand()%3 + 1);
yvel += (rand()%3 -1);
}

if (xbola <= 20)
{
xbola = 21;
xvel = (rand()%3 + 1);
yvel += (rand()%3 -1);
}

if (ybola <= 20)
{
ybola = 21;
yvel = (rand()%3 + 1);
xvel += (rand()%3 -1);
}

if (ybola >= 460)
{
ybola = 459;
yvel = - (rand()%3 + 1);
xvel += (rand()%3 -1);
}

if(xbola > xene)
{
if(xene < 630)
xene++;
}
else
{
if(xene > 320)
xene--;
}
if(ybola > yene)
{
if( yene < 410 )
yene++;
}
else
{
if ( yene > 10 )
yene--;
}

PAKENGINE_DrawGraf(0,0,100,campo);
PAKENGINE_DrawGraf(x,y,50,pala);
PAKENGINE_DrawGraf(xene,yene,50,pala);
PAKENGINE_DrawGraf(xbola,ybola,20,bola);
 }

 //Desinicializamos el motor
 PAKENGINE_DeInit();
 return 0;
}

Como podeis observar hay muchas repeticiones de código que se pueden implementar dentro del motor y algunas cosillas como la detección de colisiones que faltan por hacer, pero vamos por el buen camino.

En breve os colgaré mas avances.

Nos vemos

LordPakusBlog


<< capitulo anterior                                                                                         siguiente capitulo >>

Entradas populares