En esta entrega haremos uso de lo aprendido con el Wiimote, aplicando el manejo del IR para implementar un punto de mira que nos permitirá desarrollar un sencillo juego de tiro en el que tendremos que deshacernos de todos los personajes que se desplazan por la pantalla. Utilizaremos el juego como excusa para hacer una pequeña introducción a las GX manejando una pantalla en 2D, desarrollando rutinas de sprites y explicando la aplicación de texturas. Intentaré hacer esta parte lo más amena posible.
¿Qué son las GX?. Pues las GX son las librerías que se incluyen en el devkitpro y que nos permiten explotar las posibilidades gráficas de la Wii.
Programar en GX tiene muchas similitudes con programar en OpenGL, por lo que si ya se tienen nociones sobre OpenGL puede ser bastante útil. Pero amigos, programar en GX no es lo mismo que hacerlo en OpenGL. Hay muchas cosas que se hacen automáticamente en OpenGL y que en GX hay que hacerlas de modo explicito.
Empezaremos por explicar cual es la estructura fundamental de cualquier programa que usa GX en Wii. Dicha estructura consta básicamente los siguientes apartados descritos en pseudocódigo:
La verdad es que para quien no esté familiarizado con el lenguaje de la programación gráfica, hay un montón de términos que deberían ser explicados para no acabar abandonándolo todo sin entender nada de nada. Seguidamente haré un pequeño vocabulario con lo que yo he aprendido por mi cuenta, ya que aún me queda muchísimo por aprender en este campo.
FrameBuffers: Se trata de unas zonas de memoria que representan pantallas virtuales, sobre las que podemos modificar el valor de sus píxeles para luego volcarlas sobre la memoria de la tarjeta de video y que está información aparezca en pantalla. Habitualmente trabajaremos con dos Buffers (“Double buffering”) que serán volcados alternativamente sobre la pantalla a cada refresco de la misma. Es decir, escribiremos sobre uno mientras volcamos el otro. Esta es la técnica que suele utilizarse para evitar el típico parpadeo “flicking” que se produce si modificamos directamente el buffer que está visualizándose.
Viewport: Es la vista, dentro del framebuffer, donde dibujaremos.
Ventana de recorte: se utiliza para indicar una subventana en la que serán recortados nuestros dibujos.
Descriptor de vértices: Es el lugar donde se guardan los tipos de atributos de los vértices, como son los tipos de coordenadas de posición, de color y de textura.
Texturas: Son las tramas que se aplican sobre la superficie de los objetos a dibujar y que les dan aspecto realista.
Matrices de vista, proyección y mundial: Estas son las matrices que utiliza el sistema para transformar las coordenadas que nosotros asignamos a nuestro modelo para obtener la representación final.
Matriz de vista o de modelado: Esta matriz se aplica a las coordenadas del objeto para producir las coordenadas de vista
Matriz de proyección: Una vez obtenidas las coordenadas de vista aplicamos la matriz de proyección. Es la matriz que GX aplica para saber las partes del dibujo que se deben recortar.
Matriz Mundial: Es la que finalmente posiciona nuestro objeto en su posición final.
Escena: Llamamos escena al conjunto de objetos a dibujar.
Refresco de pantalla: El proceso de dibujar en la pantalla de nuestro televisor un fotograma de los que forman la salida de vídeo.
Menudo rollo que os he largado…, pero es que si no se tiene alguna noción de estos conceptos, no hay quien entienda la programación de las GX.
A la hora de representar gráficos en Wii podemos hacerlo mediante dos tipos de proyecciones 2D ó 3D. Si utilizamos un sistema de coordenadas en 2D emplearemos parejas de datos (X, Y) para describir los vértices, mientras que para un sistema de coordenadas en 3D utilizaremos 3 datos por vértice (X, Y, Z).
Nosotros emplearemos para nuestro caso un sistema en 2D, por lo que pasaré por alto todo lo que sea específico a los sistemas en 3D.
En GX, todos nuestros dibujos los podemos realizar mediante dos formas básicas: Los Quads (Cuadrados) y Triangles (Triángulos). Aunque internamente los Quads son representados mediante dos triángulos.
Para representar un Quad, hará falta indicar la para cada uno de sus cuatro vértices, su posición, su color y/o las coordenadas de textura. El orden en que definimos los vértices es importante, ya que si los definimos en sentido horario se considerará que la cara del polígono está hacia el observador y en caso contrario de espaldas al observador.
El descriptor de vértices contiene una serie de registros mediante los cuales podemos especificar como vamos a describir cada uno de los vértices.
void GX_ClearVtxDesc();
Borra el descriptor de vértices para permitirnos comenzar desde 0.
void GX_SetVtxDesc(u8 attr,u8 type);void GX_SetVtxDesc(u8 attr,u8 type);
Nos permite asignar los diferentes atributos, el primer parámetro nos permite seleccionar el atributo y el segundo el valor que queremos asignarle.
El parámetro attr tiene los siguientes valores posibles:
GX_VA_POS posición del vértice
GX_VA_NRM normal
GX_VA_CLR0, GX_VA_CLR1 color
GX_VA_TEX0 a GX_VA_TEX7
El parámetro type puedes ser:
GX_NONE desactiva este atributo
GX_DIRECT dato directo
GX_INDEX8 dato indexado en una tabla de hasta 256 elementos
GX_INDEX16 dato indexado en una tabla de hasta 65536 elementos.
A la hora de definir los vértices de un polígono es muy importante definir todos y cada unos de los datos que hemos definido como distintos de GX_NONE y exactamente en el orden indicado.
Es decir, si hemos definido GX_DIRECT para GX_VA_POS y GX_VA_CLR0, deberemos definir la posición del vértice y su color para cada uno de los tres vértices del polígono que queramos dibujar.
GX_SetVtxAttrFmt(u8 vtxfmt,u32 vtxattr,u32 comptype,u32 compsize,u32 frac);GX_SetVtxAttrFmt(u8 vtxfmt,u32 vtxattr,u32 comptype,u32 compsize,u32 frac);
Una vez definido que es lo que vamos a indicar para cada vértice, debemos definir también con la función anterior el tipo de datos que vamos a utilizar para cada uno.
vtxfmt- índice a la lista de formatos de vértices, usualmente GX_VTXFMT0
vtxattr- elemento al que se aplica el formato. Puede ser desde GX_VA_POS a GX_VA_TEX7.
comptype- tipo dependiente de lo que pongamos en vtxattr
GX_POS: GX_POS_XY,GX_POS_XYZ,
GX_NORM: GX_NRM_XYZ, GX_NRM_NBT, GX_NRM_NBT3,
GX_CLR: GX_CLR_RGB, GX_CLR_RGBA,
GX_VA_TEX?: GX_TEX_S, GX_TEX_ST
compsize- tamaño del dato: GX_U8, GX_S8, GX_U16, GX_S16, GX_F32
en el caso de colores : GX_RGB565, GX_RGB8, GX_RGBX8, GX_RGBA4, GX_RGBA6, GX_RGBA8
frac- Normalmente 0.
Empezaremos siempre con GX_Begin y terminaremos con GX_End. Entre medias de estas dos funciones indicaremos una lista de atributos de vértices que definen el polígono y que variaran dependiendo de los atributos que hayamos definido previamente con las funciones que modifican el descriptor de vértices.
El formato de GX_Begin es el siguiente:
void GX_Begin(u8 primitve, u8 vtxfmt, u16 vtxcnt);
primitive: Indicaremos el tipo de primitiva. En nuestro ejemplo utilizaremos solo GX_QUADS.
Vtxfmt: debe corresponder con uno de los formatos que hayamos definido previamente, por ejemplo GX_VTXFMT0.
Vtxcnt: Número de vértices totales. Evidentemente si lo que dibujamos son Quads debe ser múltiplo de 4 y si son Triangles de 3.
Veamos paso a paso las instrucciones de nuestro ejemplo de un programa de tiro al blanco.
Empecemos con la configuración del vídeo. Para separar conceptualmente esta parte la hemos agrupado en una función denominada setup_video() que es llamada desde el main.
Iniciamos la tarjeta de vídeo de la Wii y obtiene el modo de vídeo preferido de la configuración de la Wii
VIDEO_Init(); rmode = VIDEO_GetPreferredMode(NULL);
Reservamos memoria para dos framebuffers
frameBuffer[0] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode)); frameBuffer[1] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
Inicializamos el modo de video, Asignamos el buffer activo y ponemos la pantalla en negro
VIDEO_Configure(rmode); VIDEO_SetNextFramebuffer(frameBuffer[fb]); VIDEO_SetBlack(FALSE);
Hacemos un flush de los registros de vídeo y esperamos un retrace para sincronizarnos con el trazado de la pantalla o dos en el caso de ser un modo entrelazado
VIDEO_Flush(); VIDEO_WaitVSync(); if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync();
Intercambiamos el buffer activo y configuramos la cola fifo teniendo en cuenta que debe estar alineada a 32 bytes y por lo tanto debe ser múltiplo de 32.
fb ^= 1; gp_fifo = memalign(32,DEFAULT_FIFO_SIZE); memset(gp_fifo,0,DEFAULT_FIFO_SIZE); GX_Init(gp_fifo,DEFAULT_FIFO_SIZE); fb ^= 1; gp_fifo = memalign(32,DEFAULT_FIFO_SIZE); memset(gp_fifo,0,DEFAULT_FIFO_SIZE); GX_Init(gp_fifo,DEFAULT_FIFO_SIZE);
Asigna los valores de color y "valor Z" durante las operaciones de borrado de pantalla
GX_SetCopyClear(background, 0x00ffffff);
Asignamos las coordenadas de pantalla, calculamos y asignamos la relación de escala vertical y asignamos la ventana de recorte.
GX_SetViewport(0,0,rmode->fbWidth,rmode->efbHeight,0,1); yscale = GX_GetYScaleFactor(rmode->efbHeight,rmode->xfbHeight); xfbHeight = GX_SetDispCopyYScale(yscale); GX_SetScissor(0,0,rmode->fbWidth,rmode->efbHeight);
Asignamos los parámetros de copia desde el buffer interno al externo, la anchura y altura del display en pixels.
GX_SetDispCopySrc(0,0,rmode->fbWidth,rmode->efbHeight); GX_SetDispCopyDst(rmode->fbWidth,xfbHeight); GX_SetCopyFilter(rmode->aa,rmode->sample_pattern,GX_TRUE,rmode->vfilter); GX_SetFieldMode(rmode->field_rendering,((rmode->viHeight==2*rmode->xfbHeight)?GX_ENABLE:GX_DISABLE));
Seleccionamos el formato de los pixeles en el buffer interno
if (rmode->aa) GX_SetPixelFmt(GX_PF_RGB565_Z16, GX_ZC_LINEAR); else GX_SetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR);
Activamos o desactivamos la eliminación de caras ocultas. Los planos cuyos vértices tengan un orden horario se consideran visibles mientras que tengan orden antihorario se consideran ocultos. Con GX_CULL_NONE no se elimina ninguna primitiva. Esto solo es útil para el caso 3D.
GX_SetCullMode(GX_CULL_NONE); Copiamos el buffer interno al externo y asignamos la corrección Gamma a los pixeles durante la copia. GX_CopyDisp(frameBuffer[fb],GX_TRUE); GX_SetDispCopyGamma(GX_GM_1_0);
Configuramos el descriptor de vértices debiendo utilizar dos primitivas por vértice:
En nuestro caso las coordenadas de posición serán coordenadas 2D y también las coordenadas de textura, utilizando un solo canal de textura y de color simultáneos.
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_F32, 0);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
GX_SetNumChans(1);
GX_SetNumTexGens(1);
Usamos el modo "Replace" a la hora de aplicar las texturas y seleccionamos el canal 0 de textura y de color.
GX_SetTevOp(GX_TEVSTAGE0, GX_REPLACE); GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
Especificamos como son aplicadas las coordenadas de textura 0:
GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
Creamos una matriz ortográfica de proyecccion 4x4, indicando 0 como distancia al plano más cercano y 300 al plano más alejado. De esta manera damos sensación de 2D.
guOrtho(perspective,0,479,0,639,0,300);
Como último paso cargamos en memoria la matriz de proyección creada.
GX_LoadProjectionMtx(perspective, GX_ORTHOGRAPHIC);
Una vez descritos los pasos que hemos seguido en la función de configuración del video vamos a pasar a la carga de texturas:
Para la carga de texturas usaremos la librería TPL.
Antes de pasar a explicar las funciones utilizadas vamos a explicar algunos detalles:
Los archivos de textura los debemos colocar en la carpeta Textures que previamente ha sido correctamente configurada en el archivo makefile. Para nuestros propósitos utilizaremos archivos .png que nos permiten definir el canal alfa para definir que partes queremos pintar y cuales deben ser transparentes.
En este archivo .png incluiremos todas las texturas que usaremos posteriormente y después únicamente tendremos que seleccionar las coordenadas de la fracción de textura a utilizar para cada sprite. El archivo para nuestro juego quedaría como sigue.
En dicho archivo, hemos incluido tanto los personajes como la explosión y el punto de mira.
Una vez terminado el archivo de texturas deberemos definir el archivo .scf en el que le indicaremos que imagen vamos a enlazar, que nombre de variable usaremos y que formato de aplicación de la textura que vamos a utilizar.
Los formatos posibles son:
GX_TF_I4 0x0
GX_TF_I8 0x1
GX_TF_IA4 0x2
GX_TF_IA8 0x3
GX_TF_RGB565 0x4
GX_TF_RGB5A3 0x5
GX_TF_RGBA8 0x6
También se pueden usar texturas comprimidas usando colfmt=14. Nosotros seleccionamos el formato RGBA8 ya que nos permite la utilización del canal Alfa para los sprites.
La línea nos queda como sigue:
<filepath="ballsprites.png" id="ballsprites" colfmt=6 />
Una vez tenemos ambos archivos en la carpeta textures (o sea el png con todas las texturas y el scf con la información necesaria para el compilador) pasamos a las instrucciones propiamente dichas que agrupamos en una función a la que llamamos cargar_texturas() utilizando la siguiente secuencia
GX_InvalidateTexAll();
TPL_OpenTPLFromMemory(&spriteTPL, (void *)textures_tpl,textures_tpl_size); TPL_GetTexture(&spriteTPL,ballsprites,&texObj);
Con lo que dejamos las texturas listas para ser usadas.
Lo siguiente que tenemos que hacer en nuestro programa es inicializar el wiimote, para ello hemos agrupado todas las funciones de inicialización en una función llamada setup_wiimote(void) que es llamada desde el main.
En dicha función debemos incluir lo siguiente:
Inicializamos todo los wiimotes activos.
WPAD_Init();
Buscamos el primer pad activo, para lo cual llamamos a una función expresamente creada al efecto. El contenido de dicha función ya lo hemos utilizado en la entrega anterior y no creo que necesite explicación:
pad = getFirstActivePad();
Seguidamente debemos asignar la resolucion que nos devuelve el IR y el modo de operación de los pads, activando los acelerómetros y el IR.
WPAD_SetVRes(pad, 640, 480); WPAD_SetDataFormat(pad, WPAD_FMT_BTNS_ACC_IR);
Acto seguido vamos a asignar un tiempo de 5 minutos para que se apague el wiimote en caso de inactividad. Esto no es imprescindible pero ayuda a ahorrar batería.
WPAD_SetIdleTimeout(300);
Para ello creamos una función llamada setup_sprites(void) que situa aleatoriamente los sprites en pantalla y les asigna una velocidad aleatoria. También seleccionamos aleatoriamente uno de los personajes.
La función queda como sigue:
int i; srand(time(NULL)); for(i = 0; i < NUM_SPRITES; i++) { // situa aleatoriamente la posición y velocidad de los sprites sprites[i].x = rand() % (640 - 32 ) << 8; sprites[i].y = rand() % (480 - 32 ) << 8 ; sprites[i].dx = (rand() & 0xFF) + 0x100; sprites[i].dy = (rand() & 0xFF) + 0x100; sprites[i].image = rand() & 3; if(rand() & 1) sprites[i].dx = -sprites[i].dx; if(rand() & 1) sprites[i].dy = -sprites[i].dy; }
En el bucle principal del programa utilizaremos la estructura básica que hemos descrito en la sección de las GX añadiendo todo el control necesario de los mandos y la gestión de los sprites.
Vamos paso a paso describiendo las funciones llamadas desde dicho bucle:
Leemos los datos del wiimote y comprobamos si pulsamos home para volver al launcher.
WPAD_ScanPads(); wmote_data=WPAD_Data(pad); u32 pressed = WPAD_ButtonsDown(pad); if (WPAD_ButtonsDown(pad) & WPAD_BUTTON_HOME) exit(0);
Si pulsamos el botón A generamos un sonido de disparo y comprobamos si le hemos acertado a alguno de los personajes. Es decir, si sus coordenadas coinciden con las del IR.
if (pressed && WPAD_BUTTON_A) { disparo(); comprueba_disparo(wmote_data->ir.x, wmote_data->ir.y); }
Seguidamente, a cada vuelta de bucle, limpiamos la memoria de vértices y de texturas y volvemos a cargar las texturas y los descriptores de vertices:
GX_InvVtxCache(); GX_InvalidateTexAll(); GX_LoadTexObj(&texObj, GX_TEXMAP0); GX_ClearVtxDesc(); GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);
Después inicializamos el modelo de matriz para la vista 2D, viendo una vista plana y perpendicular al observador.
guMtxIdentity(GXmodelView2D); guMtxTransApply (GXmodelView2D, GXmodelView2D, 0.0F, 0.0F, -5.0F); GX_LoadPosMtxImm(GXmodelView2D,GX_PNMTX0);
Generamos el movimiento de los sprites y comprobamos para cada uno de los sprites que tipo de imagen debemos mostrar. Es decir, el personaje, la explosión en el caso de que acabemos de acertarle o nada en caso de que esté muerto.
for(i = 0; i < NUM_SPRITES; i++) { sprites[i].x += sprites[i].dx; sprites[i].y += sprites[i].dy; // Chqueamos las colisiones con los bordes de la pantalla if(sprites[i].x < (1<<8) || sprites[i].x > ((640-32) << 8)) sprites[i].dx = -sprites[i].dx; if(sprites[i].y < (1<<8) || sprites[i].y > ((480-32) << 8)) sprites[i].dy = -sprites[i].dy; drawSpriteTex( sprites[i].x >> 8, sprites[i].y >> 8, WIDTH_SPRITE, HEIGHT_SPRITE, sprites[i].image); if (!wiimote_sonando & (sprites[i].image == 4)) sprites[i].image = 5; // muerto }
Para dibujar los sprites llamamos a la función drawSpriteTex que dibuja el sprite.
Dicha función dibuja un Quad con la textura correspondiente, definiendo cada uno de los vértices con la pareja de instrucciones GX_Position2f32 y GX_TexCoord2f32 que indican respectivamente la posición del vértice en el viewport y las coordenadas de textura en la textura cargada. Esto tiene que estar en consonancia con lo que hemos definido en el descriptor de vertices.
En ambos casos hablamos de coordenadas X,Y tal y como hemos definido en los atributos de vértice.
//--------------------------------------------------------------------------------- void drawSpriteTex( int x, int y, int width, int height, int image ) { //--------------------------------------------------------------------------------- int texIndex = image * 8; // Comprobamos si está vivo if (image <= 4) { GX_Begin(GX_QUADS, GX_VTXFMT0, 4); // Draw A Quad GX_Position2f32(x, y); // Top Left GX_TexCoord2f32(texCoords[texIndex],texCoords[texIndex+1]); texIndex+=2; GX_Position2f32(x+width-1, y); // Top Right GX_TexCoord2f32(texCoords[texIndex],texCoords[texIndex+1]); texIndex+=2; GX_Position2f32(x+width-1,y+height-1); // Bottom Right GX_TexCoord2f32(texCoords[texIndex],texCoords[texIndex+1]); texIndex+=2; GX_Position2f32(x,y+height-1); // Bottom Left GX_TexCoord2f32(texCoords[texIndex],texCoords[texIndex+1]); GX_End(); // Done Drawing The Quad } }
Una vez dibujado el sprite pasamos a dibujar la mirilla. En este caso la técnica es la misma que en el caso del sprite solo que la dibujaremos en la posición del IR y con la porción de la textura correspondiente a la mirilla.
void dibujar_mirilla(int x, int y){ GX_Begin(GX_QUADS, GX_VTXFMT0, 4); GX_Position2f32(x, y); // Top Left GX_TexCoord2f32(0.5f,0.0f); GX_Position2f32(x+width_mirilla-1, y); // Top Right GX_TexCoord2f32(1.0f,0.0f); GX_Position2f32(x+width_mirilla-1,y+height_mirilla-1); // Bottom Right GX_TexCoord2f32(1.0f,0.5f); GX_Position2f32(x,y+height_mirilla-1); // Bottom Left GX_TexCoord2f32(0.5f,0.5f); GX_End(); // Done Drawing The Quad }
Una vez dibujado todo lo que tenemos que dibujar pasamos al control del vibrador del mando:
twiimote1=get_ms_clock(); // activa la vibración del wiimote si hay algún sonido activo if (wiimote_sonando) { WPAD_Rumble(pad, 1); if ((twiimote1-twiimote0) >= ti_golpe) wiimote_sonando = 0; } else { WPAD_Rumble(pad, 0); }
Como último paso antes de finalizar el bucle, añadimos algunas instrucciones que indican el modo de aplicación del color, vuelcan el buffer sobre la tarjeta de video e intercambian el buffer activo.
GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE); GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); GX_SetAlphaUpdate(GX_TRUE); GX_SetColorUpdate(GX_TRUE); GX_CopyDisp(frameBuffer[fb],GX_TRUE); VIDEO_SetNextFramebuffer(frameBuffer[fb]); if(first_frame) { VIDEO_SetBlack(FALSE); first_frame = 0; } VIDEO_Flush(); VIDEO_WaitVSync(); fb ^= 1; // flip framebuffer
Y al fin doy por terminado este largo y tedioso tutorial (debido a las GX).
Aquí os dejo los fuentes:
Descargar Fuentes Profundizando mandos de Wii - Parte 3
Para la siguiente entrega, estoy preparando un ejemplo en el que se muestra el manejo de la Wii Balance Board. Estoy todavía explorando las funciones pero espero no tener problemas en poder exponeros una aplicación en este sentido.
Comentarios
MP
ya te envie el mp por favor responde
Muy interesante
Realmente impresionante tus conocimientos
gracias por compartirlos
Muchas gracias amigo. Espero
Muchas gracias amigo.
Espero que os animéis a probarlos y a preguntarme lo que no entendáis.
Me encantaría pensar que cada vez somos más los que aprendemos juntos.
Curso aplicado de GRRLIB - Parte 1 - Parte 2 - Parte 3 - Parte 4 - Parte 5 - Parte 6 - Parte 7 - Parte 8 - Parte 9 - Parte 10 - Parte 11
Profundizando en los mandos de la Wii - Parte 1 - Parte 2 - Parte 3 - Parte 4 (Balanceboard) - Parte 5 (Miis)
Homebrew - WiiTriis - LifemiiWii
EPA
BRAVO BRAVO amigo vas no ERES un gran coder has echo algun programilla.
yo tembien empeze a programam con las SDL pero.... chungo.
Si sabes algo sobre ellas me pasarias tu msn por Mp.
GRACIAS.
The Allies
"Ayudante del Taller de Firmas y Avatares de Wii Scenebeta" =) & Orgulloso de ello =)
Pues la verdad es que no las
Pues la verdad es que no las he utilizado, pero imagino que podría defenderme si me preguntas algo concreto.
Preferiblemente prefiero que las preguntas las hagáis en el foro, puesto que así pueden servir a más gente.
Si por algún motivo quieres hacer la pregunta en privado mandame un mensaje privado y veré a ver si puedo ayudarte.
Si vas bien de inglés, de las SDL puedes utilizar este tutorial:
http://wiibrew.org/wiki/SDL_Wii/tutorial
De todas formas, hazme la pregunta que sea que si está en mi mano te ayudaré.
Curso aplicado de GRRLIB - Parte 1 - Parte 2 - Parte 3 - Parte 4 - Parte 5 - Parte 6 - Parte 7 - Parte 8 - Parte 9 - Parte 10 - Parte 11
Profundizando en los mandos de la Wii - Parte 1 - Parte 2 - Parte 3 - Parte 4 (Balanceboard) - Parte 5 (Miis)
Homebrew - WiiTriis - LifemiiWii