viernes, 11 de junio de 2010

Instalar DevkitPro en Linux - Ubuntu (2)

En este entrada del blog ya se hizo referencia a la forma de instalar devkitpro en linux - ubuntu.

A fecha de hoy la instalación ha cambiado y hemos modificado esa entrada para actualizarla.


Saludos

jueves, 27 de mayo de 2010

Una breve explicación

Ya siento no haber escrito en estos cuatro meses. Siempre me he estado quejando del poco tiempo que tenía, y ahora como podéis ver, ya ni siquiera actualizo el blog.


La verdad es que he emprendido una aventura empresarial y ya no tengo nada de tiempo. Desde hace un par de años llevaba colaborando con un amigo en una serie de proyectos informáticos y este año ya nos hemos decidido a dar el salto.

He dejado mi trabajo y nos estamos poniendo por nuestra cuenta. Como comprenderéis estoy bastante liado.

En cuanto tenga algo de tiempo seguiré con el blog, ya que tengo alguna que otra idea y me gustaría poder desarrollarla en el blog.

Saludos a todos y muchas gracias

viernes, 22 de enero de 2010

Tetris para la Wii

Como ya comentamos en una entrada anterior vamos a ver como pasar el Tetris de la DS a la Wii.

Lo primero de todo es bajar el Tetris para la DS, y ponernos a cambiar las llamadas a la librería PAlib por las de la Wii. Para la Wii hay varias librerías disponibles, en nuestro caso hemos usado las librerías libwiisprite, para la parte gráfica, fat, para le acceso al disco y wpad para manejar el wiimote.

Por ejemplo el #include se debe cambiar por #include , #include y #include .

Por ejemplo las llamadas de tipo PA_Init, PA_InitVBL() y PA_InitText por gwd.InitVideo, donde se ha definido gwd de tipo GameWindow, fatInitDefault y WPAD_Init();.

Otra cosa, las librería del Wii que he usado están en C++, por lo que hay que pasar de tetris.c a tetris.cpp. He utilizado la nomenclatura de objetos con . en vez de con ->. Esto es debido a que todos los ejemplo venían con . y como he hecho Copy/Paste pues así es como ha quedado.

Este es el código para cargar el fondo:

Image img_fondo;
 if (img_fondo.LoadImage("fondo.png") != IMG_LOAD_ERROR_NONE) exit(0);
  Sprite fondo;
  fondo.SetImage(&img_fondo);
  fondo.SetPosition(0, 0);

La explicación del código sería muy fácil: primero definimos la variable img_fondo de tipo Imagen y cargamos el fondo en este objeto, con la función LoadImage. Se crea un objeto de tipo Sprite y se le asocia la imagen. Finalmente lo colocamos en la posición 0, 0 y Ya tenemos el fondo.

Ahora toca cargar los tilset para las fichas y el tablero. Esto, si se ha seguido la programación del tetris de la DS, no sería necesario explicarlo mucho. Sólo comentaremos que un tileset es una zona gráfica que se puede rellenar con pequeñas imágenes para crear la imagen completa, a modo de ladrillos.

Para crear una zona de tilesets (el tablero del tetris y la fichas siguiente) usaremos
el objeto TiledLayer:

TiledLayer tablero_layer(TABLERO_ANCHO, TABLERO_ALTO, 8);
TiledLayer ficha_siguiente_layer(FICHA_ANCHO, FICHA_ALTO, 8);

Image tiles;
if (tiles.LoadImage("tiles16.png") != IMG_LOAD_ERROR_NONE) exit(0);
tablero_layer.SetStaticTileset(&tiles, 16, 16);
tablero_layer.SetPosition(160, 50);

ficha_siguiente_layer.SetStaticTileset(&tiles, 16, 16);
ficha_siguiente_layer.SetPosition(350, 50);

La explicación sería muy parecida a la de cargar el fondo. Se han creado dos objetos de tipo TiledLayer y a ambos se les asocia una imagen como StaticTileset. Esta será la que contendrá los ladrillos. Finalmente posicionamos los dos tilesets en la pantalla.

Ahora vamos a cargar todo en pantalla. Para esto se usa un LayerManager, al cual hay que asignarle los tres componentes que queremos que muestre:

LayerManager tablero_manager(3);
 tablero_manager.Append(&tablero_layer);
 tablero_manager.Append(&ficha_siguiente_layer);
 tablero_manager.Append(&fondo);
El orden es importante, el fondo debe ir al final.

En el próximo post veremos como utilizar el mando el wiimote como un mando clásico.

Saludos y feliz año.

P.D. Este es el primer y único desarrollo que he hecho para la Wii, así que no puedo decir si como se ha hecho es la mejor forma posible. Cargar un fondo en un objeto de tipo Sprite no parece muy correcto, pero es que no he encontrado otra forma de hacerlo. Me gustaría estudiar más a fondo la librería libogc pero, como siempre, la falta de tiempo...

viernes, 18 de diciembre de 2009

MegaMan ya dispara

Para ver a nuestro nuevo héroe disparando he creado un nuevo proyecto.

El código que he escrito lo he metido dentro de la librería devnintendods, pero no tiene que ser difícil ponerlo en otro proyecto. Por supuesto el juego está en 2D.

Se ha creado un vector en la clase ScreenManager que se denomina m_dynamicSprite. Este es el vector donde se irán añadiendo los distintos elementos dinámicos del juego. En este caso son disparos, pero podrían ser otras cosas, por ejemplo los enemigos, o las casas que se van construyendo en un juego tipo Age of Empire. Los objetos que se asocian a este array deben implementar el interface DyamicSprite. De esta forma todos tendrán la función Next.

Desde el bucle del juego, en GameManager se llama a la función Next de ScreenManager. Se hacen dos llamadas, una para la pantalla de arriba y otra para la pantalla de abajo:

void GameManager::Run() {
 bool exit = 1;
 while (exit) {
  if (m_KeypadManager) {
   if (Pad.Newpress.Up) {
    exit = m_KeypadManager->NewpressUp();
   } else if (Pad.Newpress.Left) {
    exit = m_KeypadManager->NewpressLeft();
   } else if (Pad.Newpress.Right) {
    exit = m_KeypadManager->NewpressRight();
   } else if (Pad.Newpress.Down) {
    exit = m_KeypadManager->NewpressDown();
   } else if (Pad.Held.Up) {
    exit = m_KeypadManager->HeldUp();
   } else if (Pad.Held.Left) {
    exit = m_KeypadManager->HeldLeft();
   } else if (Pad.Held.Right) {
    exit = m_KeypadManager->HeldRight();
   } else if (Pad.Held.Down) {
    exit = m_KeypadManager->HeldDown();
   } else {
    exit = m_KeypadManager->NoKey();
   }
  }
  m_TopScreenManager->Next();
  m_BottomScreenManager->Next();
  exit = exit && m_KeypadManager->BeforeVBL();
  PA_WaitForVBL();
 }
}

ScreenManager es la que recorre el array y llama a todas las funciones Next, independientemente del tipo de objeto que sea. En nuestro caso es siempre un objeto de tipo Disparo.

void ScreenManager::Next() {
 unsigned int i;
 for (i = 0; i < m_dynamicSprites->size(); i++) {
  DynamicSprite *dynamicSprite = m_dynamicSprites->at(i);
  dynamicSprite->Next(this);
 }
}

void ScreenManager::EraseDynamicSprite(DynamicSprite *dynamicSprite) {
 unsigned int i;
 for (i = 0; i < m_dynamicSprites->size(); i++) {
  DynamicSprite *dynamic = m_dynamicSprites->at(i);
  if (dynamic == dynamicSprite) {
   m_dynamicSprites->erase(m_dynamicSprites->begin() + i);
   delete dynamicSprite;
  }
 }
}

Al pulsar la tecla de disparar (he puesto la flecha para arriba porque en mi emulador no funciona la A) se crea un disparo y se añade al array. La función Next del disparo es la que se encargará de mover la bala en la dirección correcta y cuando llegue al final de la pantalla destruir el propio objeto.

bool Disparo::Next(void *screenManager) {
 if (m_direccion == DIRECCION_IZQUIERDA) {
  int x = this->MoveLeft(VELOCIDAD);
  if (x < 0) this->Stop(screenManager);
 } else if (m_direccion == DIRECCION_DERECHA) {
  int x = this->MoveRight(VELOCIDAD);
  if (x > 256) this->Stop(screenManager);
 }
 return 1;
}

void Disparo::Stop(void *screenManager) {
 ScreenManager *sm = (ScreenManager *)screenManager;
 sm->EraseDynamicSprite(this);
}

Y esto es todo. Si disparáis más de 100 veces deja de disparar. He probado y todos los destructores de las clases se ejecutan y la función PA_DeleteSprite también, pero como se ha comentado en algún post anterior, este tema parece que no va bien.


En el próximo post veremos como disparar un sólo proyectil. El proyecto se puede descargar de aquí.

Saludos

viernes, 11 de diciembre de 2009

Cogiendo las monedas

Por fin, Mario puede coger sus monedas y ya empieza esto a parecerse más al New Super Mario Bross de la Nintendo DS.

Está quedando chulo, parece algo:



Se ha tenido que cambiar alguna cosilla de las librerías, pero nada demasiado raro.

Para coger las monedas lo primero es saber donde están y cuanto abultan. Es por esto que se ha tenido que introducir las variables width y height dentro de la clase Sprite. De paso hemos aprovechado para limpiar un poco los constructores y dejarlo todo más fino.

Dentro de la clase Sprite se han modificado e introducido las siguientes funciones:

Sprite(unsigned int screen, unsigned int background, unsigned int size, int x, int y, int width, int height);

Sprite(unsigned int screen, unsigned int background, unsigned int shape, unsigned int size, void *palette, void *sprite, int x, int y, int width, int height, int hflip);

void SetWidth(int width);
void SetHeight(int height);
void SetWidthHeight(int width, int height);

int GetWidth();
int GetHeight();

bool InSprite(int x, int y);

Esta última función, InSprite mira que el punto x,y esté dentro del Sprite. En nuestro caso será dentro de la moneda y se ha codificado de la siguiente manera:

bool Sprite::InSprite(int x, int y) {
return (x >= m_x && x <= m_x + m_width && y >= m_y && y <= m_y + m_height);
}

No está fino del todo, pero ya lo iremos mejorando.
También hemos codificado el destructor de la clase: 

Sprite::~Sprite() {
PA_StopSpriteAnim(m_screen, m_index_sprite);
PA_DeleteSprite(m_screen, m_index_sprite);

}

De esta forma al coger la moneda destruimos el sprite. Ahora la clase ScreenManager. En las funciones para crear Sprites y sprites de fondo se ha introducido el width y el height:
Sprite *CreateSprite(unsigned int background, unsigned int shape, unsigned int sizes, void *palette, void *sprite, int x, int y, int width, int height, int hflip = 0);

Sprite *CreateBackgroundSprite(unsigned int background, unsigned int shape, unsigned int sizes, void *palette, void *sprite, int x, int y, int width, int height, int hflip = 0);
Y hemos introducido más funciones para trabajar con los Sprites de fondo:
int ScreenManager::GetBackgroundSpriteIndex(int x, int y) {
unsigned int i;
for (i = 0; i < m_backgroundSprites->size(); i++) {
Sprite *sprite = m_backgroundSprites->at(i);
if (sprite->InSprite(x, y)) return i;
}
return -1;
}

Sprite *ScreenManager::GetBackgroundSprite(unsigned int i) {
return m_backgroundSprites->at(i);
}

void ScreenManager::EraseBackgroundSprite(unsigned int i) {
Sprite *sprite = m_backgroundSprites->at(i);
delete sprite;
m_backgroundSprites->erase(m_backgroundSprites->begin() + i);
}
La primera obtiene el índice de un Sprite de fondo a partir de unas coordenadas. Devolverá el primer sprite de fondo que contenga esas coordenadas. La segunda devuelve el Sprite de fondo a partir de un índice. La tercera borra un Sprite de fondo a partir de un índice. Esta última destruye el Sprite. En cuanto a código PAlib hemos usado por primera vez, creo, la función que sirve para borrar un Sprite: PA_DeleteSprite(m_screen, m_index_sprite); Se le pasa la pantalla donde está el sprite asignado y el indice que se le asoció al crearlo (no confundir con el índice de los Sprites de fondo). Para adaptar todas estas nuevas funciones a nuestro propósito, que es coger monedas al pasar sobre ellas, he introducido las siguientes líneas al final de la función BeforeVBL de la clase NSMB.cpp:
int i = topScreen->GetBackgroundSpriteIndex(mario->GetX() + 8, mario->GetY() + 8);
if (i > -1) { //hemos pillado moneda
topScreen->EraseBackgroundSprite(i);
puntuacion_monedas++;
PA_OutputText(SCREEN_BOTTOM, 1, 1, "monedas = %d      ", puntuacion_monedas); // para debugear
}

El código lo podéis encontrar en esta dirección.

Esto es todo por ahora.

Saludos

viernes, 4 de diciembre de 2009

Cargamos las monedas (II)

Hemos encontrado un problema a la hora de cargar las monedas. La función de clonar tiene la peculiaridad de que clona incluso el comportamiento del Sprite, en este caso la animación.

Eso nos venía muy bien hasta ahora, porque todas la monedas se ponían a girar alegremente. Pero ahora, al coger las monedas, las borramos y las paramos (si no se hace esto aparecen efectos raros en la pantalla).

Al parar la primera moneda (la de más arriba), de la que hemos clonado todas, se paraban todas. Así que hemos modificado la función de clonar añadiendo nuevos parámetros (opcionales):

//en ScreenManager.h
void LoadBackgroundSprites(Sprite *sprite, int *map, int map_columns, int map_rows, unsigned int firstframe = 0, unsigned int lastframe = 0, unsigned int speed = 0);

//en ScreenManager.cpp
void ScreenManager::LoadBackgroundSprites(Sprite *sprite, int *map, int map_columns, int map_rows, unsigned int firstframe, unsigned int lastframe, unsigned int speed) {
 unsigned int i;
 unsigned int length = map_columns * map_rows;
 bool primero = true;
 for (i = 0; i < length; i++)
  if (map[i] > 0) {
   int y = i / map_columns;
   int x = i - (y * map_columns);
   if (primero) {
    sprite->SetXY(x * 16, y * 16);
    primero = false;
   } else {
    Sprite *newSprite = CloneBackgroundSprite(sprite, x * 16, y * 16);
    if (speed > 0) {
     newSprite->SetAnim(firstframe, lastframe, speed);
     newSprite->StartAnim();
    }
   }
  }
}

Lo nuevo de la función está al final:

Si la velocidad de la animación es mayor de cero entonces preparamos el sprite para animarlo y comenzamos la animación. De esta forma cada sprite es animado de forma independiente y no depende del primer sprite.

Ya se pode descargar el editor de mapas.

Saludos

P.D: Es importante parar el Sprite a la hora de borrarlo, por lo que estas dos líneas deben ir juntas, si no darán problemas el resto de sprites. En el destructor de la clase Sprite hemos escrito el siguiente código:
Sprite::~Sprite() {
 PA_StopSpriteAnim(m_screen, m_index_sprite);
 PA_DeleteSprite(m_screen, m_index_sprite);
}

y se invoca de esta forma:
delete sprite;

Borra el objeto sprite, y de esta forma se ejecuta el destructor de la clase Sprite.

Aún así el tema no va bien. Si creas y destruyes muchos Sprites al final el sistema no permite crear más o aunque los cree no se ven correctamente, parece un problema de PAlib.

Saludos

miércoles, 2 de diciembre de 2009

Un juego tipo Shooter

O lo que es lo mismo uno de disparar.

Hace un par de semanas respondimos a un comentario de como hacer un juego de disparos. Hemos preparado una nueva versión de la librería devnintendods para que permita a nuestros héroes disparar.

En su momento se respondió con una explicación técnica de como es posible hacerlo. Esta era más o menos la explicación que se dio:  se crea un array o vector y ahí se van acumulando todos los disparos que se vayan efectuando. El motor del juego recorrería ese array en cada ciclo del bucle del juego y moverá los disparos a su siguiente posición.

Hemos diferenciado dos tipos de juegos, los que puede hacer un disparo cada vez (no se puede hacer otro hasta que no acabe el anterior) y otro con disparos ilimitados.

Hemos preparado un nuevo desarrollo basado en el popular personaje de Megaman y nos hemos puesto a pegar tiros como locos por la pantalla.



Las próximas semanas iremos publicando el código y la explicación de como se ha hecho.

Saludos

P.D. En cuanto a la programación en C++ es interesante ver como se ha preparado un Interface. Para los que trabajen en Java parece algo trivial pero a la hora de trabajar en C++ no me ha parecido tan sencillo. Lo veremos en los próximos post.