miércoles, 28 de enero de 2009

Los tiles y cómo dibujar el Tablero

Por fin vamos a ver una nueva técnica que nos ofrece las librerías PALib.

A la hora de programar juegos de tablero, o a la hora de dibujar nuestros mundos, no es necesario cargar unos fondos enormes. PALib ofrece la posibilidad de poder dibujar tiles.

Los tiles son pequeñas casillas que se pueden dibujar en la pantalla tantas veces como se quiera. Son casillas de 8x8 pixeles, por lo que una pantalla de la DS que tiene 256x192 tendrá 32x24 tiles.

La siguiente imágen nos carga los tiles que vamos a usar en esta primera versión del Tetris:



Si os fijáis bien, se han creado una serie de cuadraditos de 8x8 pixeles que serán las posibles casillas a dibujar. Cómo las fichas del Tetris tienen distintos colores pues hemos hecho casillas de diferentes colores.

Ahora veamos el código poco a poco para ver cómo funciona todo:

En primer lugar:

Cargamos la imagen de los tiles en las dos pantallas mediante estas dos funciones de la API de PALib.
PA_DualLoadBgPal(BACKGROUND_TWO, (void *)tiles_Pal);
PA_DualLoadSimpleBg(BACKGROUND_TWO, tiles_Tiles, Blank, BG_256X256, 0, 1);

La primera carga la paleta de la imagen, y la segunda carga, en las dos pantallas, la imagen como si fueran tiles y lo muestra en pantalla.
¿Pero que muestra? Un array de ceros, Blank, de 32x24 en cada una de las pantallas. Como la primera casilla de la imagen (la casilla cero) de tiles es negra, mostrará una pantalla negra.

Para dibujar por la pantalla los distintos tiles se puede utilizar la función PALib:
PA_SetMapTileEx(screen, BACKGROUND_TWO, x1, y1, ficha, 0, 0, 0);
donde x1,y1 serán las coordenadas del array de 32x24 en que está dividida cada pantalla de la DS. Los tres últimos parámetros ya los veremos más adelante.

Para poder ver todo lo que se ha dicho aquí, probar el siguiente código quitando las dos líneas que cargan los fondos: PA_EasyBgLoad(SCREEN_TOP, BACKGROUND_ONE, fondo_top); y PA_EasyBgLoad(SCREEN_BOTTOM, BACKGROUND_ONE, fondo_bottom);.

Dibujar_casilla:

Esta función dibuja cada casilla del tablero y cada bloque de una ficha del tetris. Como 8x8 pixeles es muy pequeño, vamos a dibujar casillas de 16x16, es decir cuatro veces la casilla de 8x8.
PA_SetMapTileEx(screen, BACKGROUND_THREE, x1, y1, ficha, 0, 0, 0);
PA_SetMapTileEx(screen, BACKGROUND_THREE, x1, y1 + 1, ficha, 0, 1, 0);
PA_SetMapTileEx(screen, BACKGROUND_THREE, x1 + 1, y1, ficha, 1, 0, 0);
PA_SetMapTileEx(screen, BACKGROUND_THREE, x1 + 1, y1 + 1, ficha, 1, 1, 0);

Los parámetros son:
PA_SetMapTileEx(screen, background, x, y, tile, girar_horizontal, girar_vertical, paleta);

Debido a un pequeño problema que ocurre en la pantalla inferior (la posición 0,0 se baja unos 16 pixeles), hay que poner el scroll de esta pantalla a 0 (!?!?!?!?!?) :

PA_EasyBgScrollXY(SCREEN_BOTTOM, BACKGROUND_THREE, 0, 0);

Por último dibujaremos la pantalla de la aplicación. Por ahora he usado estos dos fondos (fondo_top y fondo_bottom)






El código es el siguiente:
#include <PA9.h>

#include "gfx/all_gfx.h"
#include "gfx/all_gfx.c"

#include "devnintendods.h"

#define TABLERO_ANCHO 10
#define TABLERO_ALTO 21

#define FICHA_ANCHO 4
#define FICHA_ALTO 4

#define CASILLA_VACIA 1
#define CASILLA_ROJA 2
#define CASILLA_AZUL 3
#define CASILLA_VERDE 4
#define VELOCIDAD_MINIMA 25
#define BORDE_IZQUIERDO 1

//La fila 0 es la de más arriba y la 16 la de la base. La columna 0 es la de la izquierda.
u8 tablero[TABLERO_ANCHO][TABLERO_ALTO]; 

typedef struct {
    int x, y;
    u8 radio;
    u8 ficha[FICHA_ANCHO][FICHA_ALTO];
} tipo_ficha;

tipo_ficha ficha;

void inicializar_tablero();
void dibujar_tablero();
void dibujar_casilla(int x, int y, u8 ficha);

int main(int argc, char ** argv) {
    PA_Init();
    PA_InitVBL();
    PA_InitText(SCREEN_TOP, BACKGROUND_TWO);

    PA_DualLoadBgPal(BACKGROUND_THREE, (void *)tiles_Pal);
    PA_DualLoadSimpleBg(BACKGROUND_THREE, tiles_Tiles, Blank, BG_256X256, 0, 1);
    PA_EasyBgScrollXY(SCREEN_BOTTOM, BACKGROUND_THREE, 0, 0);

    PA_EasyBgLoad(SCREEN_TOP, BACKGROUND_ONE, fondo_top);
    PA_EasyBgLoad(SCREEN_BOTTOM, BACKGROUND_ONE, fondo_bottom);

    inicializar_tablero();
    dibujar_tablero();

    while  (1) {
        PA_WaitForVBL();
    }
}

void inicializar_tablero() {
    u8 i, j;

    for(i = 0; i < TABLERO_ANCHO; i++) 
        for(j = 0; j < TABLERO_ALTO; j++) 
            tablero[i][j] = CASILLA_VACIA;
}

void dibujar_tablero() {
    u8 i, j;

    for(i = 0; i < TABLERO_ANCHO; i++) 
        for(j = 0; j < TABLERO_ALTO; j++)
            dibujar_casilla(i, j, tablero[i][j]);
}

void dibujar_casilla(int x, int y, u8 ficha) {
    u8 x1 = x * 2;
    u8 y1 = y;
    u8 screen = SCREEN_TOP;
    if (y1 >= 12) {
        screen = SCREEN_BOTTOM;
        y1 = y1 - 12;
    }
    y1 = y1 * 2;
    x1 += BORDE_IZQUIERDO;
    PA_SetMapTileEx(screen, BACKGROUND_THREE, x1, y1, ficha, 0, 0, 0);
    PA_SetMapTileEx(screen, BACKGROUND_THREE, x1, y1 + 1, ficha, 0, 1, 0);
    PA_SetMapTileEx(screen, BACKGROUND_THREE, x1 + 1, y1, ficha, 1, 0, 0);
    PA_SetMapTileEx(screen, BACKGROUND_THREE, x1 + 1, y1 + 1, ficha, 1, 1, 0);
}


La programación con 'tiles' nos permitirá crear el juego de plataformas. Si nos da tiempo volveremos sobre el juego de Pokemon para poder hacer mundos más grandes, sin ocupar demasiado espacio en memoria.

Saludos

viernes, 23 de enero de 2009

Empezamos a crear el Tetris

Con los post anteriores veíamos cosas de la programación pero nada tangible. Así que en este post empezaremos a crear el juego.

Lo primero el fondo del juego. Por ahora no vamos a hacer nada de puntuación y ficha siguiente, así que por ahora el fondo serán estas dos imágenes:


fondo_top y

fondo_bottom

Las imágenes deben pasar por el PAGX, como se indica aquí.

PALib nos da estas dos funciones para cargar dichos fondos:
PA_EasyBgLoad(SCREEN_TOP, BACKGROUND_ONE, fondo_top);
PA_EasyBgLoad(SCREEN_BOTTOM, BACKGROUND_ONE, fondo_bottom);

El código sería:
#include <PA9.h>

#include "gfx/all_gfx.h"
#include "gfx/all_gfx.c"

#include "devnintendods.h"

#define TABLERO_ANCHO 10
#define TABLERO_ALTO 20

#define FICHA_ANCHO 4
#define FICHA_ALTO 4

u8 tablero[TABLERO_ANCHO][TABLERO_ALTO]; 

typedef struct {
    u8 x, y, radio;
    u8 ficha[FICHA_ANCHO][FICHA_ALTO];
} tipo_ficha;

tipo_ficha ficha;

u8 ficha_timer = 0;

int main(int argc, char ** argv) {
    PA_Init();
    PA_InitVBL();

    PA_EasyBgLoad(SCREEN_TOP, BACKGROUND_ONE, fondo_top);
    PA_EasyBgLoad(SCREEN_BOTTOM, BACKGROUND_ONE, fondo_bottom);

    while  (1) {
        PA_WaitForVBL();
    }
    return 0;
}


Para los que no han seguido el tutorial desde el principio devnintendods.h lo podeís encontrar aquí, y se debe guardar en la carpeta source.

Bueno, esto va tomando forma. En el próximo post veremos como programar el pintado del tablero en pantalla.

Saludos

domingo, 18 de enero de 2009

Más teoría sobre el Tetris

Seguimos con la programación del Tetris para la nintendo DS.

Tenemos que continuar programando funciones sobre los arrays que definimos en la anterior entrada del blog. Por ejemplo:

void crear_ficha_l(u8 color) {
ficha.radio = 3;
ficha.ficha[0][0] = color;
ficha.ficha[1][0] = CASILLA_VACIA;
ficha.ficha[2][0] = CASILLA_VACIA;
ficha.ficha[3][0] = CASILLA_VACIA;
ficha.ficha[0][1] = color;
ficha.ficha[1][1] = CASILLA_VACIA;
ficha.ficha[2][1] = CASILLA_VACIA;
ficha.ficha[3][1] = CASILLA_VACIA;
ficha.ficha[0][2] = color;
ficha.ficha[1][2] = color;
ficha.ficha[2][2] = CASILLA_VACIA;
ficha.ficha[3][2] = CASILLA_VACIA;
ficha.ficha[0][3] = CASILLA_VACIA;
ficha.ficha[1][3] = CASILLA_VACIA;
ficha.ficha[2][3] = CASILLA_VACIA;
ficha.ficha[3][3] = CASILLA_VACIA;
}
y así todas las fichas!!

Pensando en la forma como hemos solucionado el tema de las fichas, se podría hacer de otra manera.
Esta se basaría en que todas las fichas tienen 4 piezas, con lo que con un array de cuatro elementos sería suficiente, y cada elemento del array guardaría la posición (x, y) donde va esa pieza.

Por ejemplo:
  1. La pieza L sería (0, 0), (0, 1), (0, 2), (1, 2)
  2. La pieza J sería (1, 0), (1, 1), (0, 2), (1, 2)
  3. La pieza o (el cuadrado) sería (0, 0), (0, 1), (1, 0), (1, 1)
A la hora de rotar se podrían guardar las distintas posiciones o rotar sobre la casilla 1,1. Tendríamos que guardar en otra variable el color de las piezas, pero me parece un poco rollo.

Más funciones, la de colisión:
u8 existe_colision(u8 x, u8 y) {
u8 i, j;

for(i = 0; i < j =" 0;">= TABLERO_ANCHO) return 1;
if (y + j >= TABLERO_ALTO) return 1;
if (tablero[x + i][y + j] != CASILLA_VACIA) return 1;
}
return 0; //false
}

Esta la vamos a explicar con más detenimiento.

La función mira si la ficha 'ficha' en las posiciones 'x' e 'y', pasadas como parámetro, está sobre un obstáculo. Se mira que no se salga por los bordes izquierdo o derecho ( if (x + i <>= TABLERO_ANCHO) return 1;), que no llegue al fondo ( if (y + j >= TABLERO_ALTO) return 1;) o que no encuentre alguna casilla del tablero no vacía (if (tablero[x + i][y + j] != CASILLA_VACIA) return 1;), por supuesto todo esto se hará si la casilla de la ficha (i, j) no está vacía ( if (ficha.ficha[i][j] != CASILLA_VACIA) {)

Saludos

martes, 13 de enero de 2009

Tetris para la Nintendo DS

Buenas

Después del primer tutorial de este blog, un juego de Pokemon vamos a programar otro juego más conocido, el Tetris.

Seguiremos utilizando las librerías PALib, aunque vamos a utilizar otros conceptos distintos a los hasta ahora vistos, que luego nos servirán para crear más juegos. Si el primer programa se utilizaron los Sprites a tope, en este vamos a utilizar los Tiles.

Entre los comentarios que aparecen en el blog, los cuales agradezco muchísimo, había uno donde se me animaba a hacer un Super Mario Bros. Lo haremos, primero el Tetris, y con los conceptos que vamos a introducir con él, podremos construir este juego de plataformas. He encontrado un tutorial donde explican un juego de plataformas, pero creo que podemos hacerlo algo más profesional.

Un par de comentarios me indicaban que el tutorial de Pokemon ha quedado bien como tutorial y algo cojo como juego. Pues bien, estoy pensando una serie de modificaciones para hacerlo, también, más 'profesional'.

Por mi parte, intentaré ser más constante con las publicaciones. Me gusta tener el juego más o menos acabado antes de empezar con los tutoriales, y eso me lleva tiempo.

Pero cada cosa a su tiempo, primero el Tetris, que vereís es sencillísimo.

Como siempre, lo primero es copiar el template de PALib (C:\devkitPro\PAlibTemplate) a la ubicación que deseemos, en mi caso C:\devDS\TetrisDS, y ya podemos empezar.

En primer lugar las estructuras que vamos a usar.

El tetris es un juego de tablero, por lo que haremos un array con las casillas del tablero, es decir una matriz de 10x20, 10 columnas por 20 filas.

#define TABLERO_ANCHO 10
#define TABLERO_ALTO 20

u8 tablero[TABLERO_ANCHO][TABLERO_ALTO];

Y las fichas del tetris son fichas de 4x4 casillas. La verdad es que todas las fichas son de 3x3 menos dos: el cuadrado que es de 2x2 y la ficha larga que es de 4x1 (o 1x4).
En resumen, todas entrarán en una estructura de 4x4, pero a la hora de hacerlas girar, cada una deberá hacerlo dentro de su tamaño, es decir, necesitaremos un campo que denominaremos radio.

#define FICHA_ANCHO 4
#define FICHA_ALTO 4

typedef struct {
  u8 x, y, radio;
  u8 ficha[FICHA_ANCHO][FICHA_ALTO];
} tipo_ficha;

tipo_ficha ficha;

Cómo podéis observar, también he metido las coordenadas del tablero por donde andará la ficha (x, y).

Bueno, por ahora no hemos visto mucho de las PALib. Esto es por que el Tetris tiene mucho trabajo de arrays, y hasta que acabemos con todas estas funciones no podremos empezar ha programar el resto del juego.

Por ahora vamos a presentar alguna función más, por ejemplo la de crear algún tipo de ficha:

#define CASILLA_VACIA 1

void crear_ficha_z(u8 color) {
  ficha.radio = 3;
  ficha.ficha[0][0] = color;
  ficha.ficha[1][0] = color;
  ficha.ficha[2][0] = CASILLA_VACIA;
  ficha.ficha[3][0] = CASILLA_VACIA;
  ficha.ficha[0][1] = CASILLA_VACIA;
  ficha.ficha[1][1] = color;
  ficha.ficha[2][1] = color
  ficha.ficha[3][1] = CASILLA_VACIA;
  ficha.ficha[0][2] = CASILLA_VACIA;
  ficha.ficha[1][2] = CASILLA_VACIA;
  ficha.ficha[2][2] = CASILLA_VACIA;
  ficha.ficha[3][2] = CASILLA_VACIA;
  ficha.ficha[0][3] = CASILLA_VACIA;
  ficha.ficha[1][3] = CASILLA_VACIA;
  ficha.ficha[2][3] = CASILLA_VACIA;
  ficha.ficha[3][3] = CASILLA_VACIA;
}

Seguiremos. El juego es muy sencillo, pero tiene un montón de funciones de manejo de arrays.

Saludos

lunes, 5 de enero de 2009

Fundido de pantalla

Vamos a realizar un último efecto gráfico en el juego, un Fundido de pantalla. En este caso es simplemente el oscurecimiento de la pantalla. Esto se consigue con la función PA_SetBrightness.

De esta forma cargaremos un par de portadas al juego y lo dejaremos acabado.

Las portadas serán estas:
la portada:
y la subportada:

Crearemos una función que las cargue y las descargue. Usaremos el Stylus para pulsar sobre la pantalla y poder así empezar el juego.

Deberemos añadir a nuestro código la definición de la función y la función cargarPortadas.

#include <PA9.h>

#include "gfx/all_gfx.h"
#include "gfx/all_gfx.c"

#include "devnintendods.h"

#define BACKGROUND_X 80
#define BACKGROUND_Y 132

#define HEROE_PALLETE 0
#define HEROE_SPRITE 0
#define HEROE_REFLEJO_SPRITE 1
#define HEROE_X 120
#define HEROE_Y 88
#define HEROE_VELOCIDAD 5

#define ANIMACION_PAUSA 0
#define ANIMACION_ABAJO 1
#define ANIMACION_ARRIBA 2
#define ANIMACION_IZQUIERDA 3
#define ANIMACION_DERECHA 4

#define ESPACIO_LIBRE 0

u8 obtenerBaldosa(s32 x, s32 y);
void cargarPortadas();

int main(int argc, char ** argv) {
    PA_Init();
    PA_InitVBL();

    cargarPortadas();

    PA_EasyBgLoad(SCREEN_TOP, BACKGROUND_TWO, fondo);
    PA_EasyBgLoad(SCREEN_TOP, BACKGROUND_THREE, agua);
    PA_EasyBgLoad(SCREEN_TOP, BACKGROUND_ONE, sobre_fondo);
    
    PA_LoadSpritePal(SCREEN_TOP, HEROE_PALLETE,(void*)heroe_Pal);
    PA_CreateSprite(SCREEN_TOP, HEROE_SPRITE, (void*)heroe_Sprite, OBJ_SIZE_16X32, COLOR_MODE_256, HEROE_PALLETE, HEROE_X, HEROE_Y);
    PA_SetSpritePrio(SCREEN_TOP, HEROE_SPRITE, BACKGROUND_TWO);

    PA_CreateSprite(SCREEN_TOP, HEROE_REFLEJO_SPRITE, (void*)heroe_Sprite, OBJ_SIZE_16X32, COLOR_MODE_256, HEROE_PALLETE, HEROE_X, HEROE_Y + 8);
    PA_SetSpritePrio(SCREEN_TOP, HEROE_REFLEJO_SPRITE, BACKGROUND_THREE);
    PA_SetSpriteVflip(SCREEN_TOP, HEROE_REFLEJO_SPRITE, 1);
    PA_SetSpriteMode(SCREEN_TOP, HEROE_REFLEJO_SPRITE, 1);
    PA_EnableSpecialFx(SCREEN_TOP, SFX_ALPHA, 0, SFX_BG0 | SFX_BG1 | SFX_BG2 | SFX_BG3 | SFX_BD);
    PA_SetSFXAlpha(SCREEN_TOP, 12, 15);

    s32 fondoX = BACKGROUND_X;
    s32 fondoY = BACKGROUND_Y;
    s32 estadoAnimacion = ANIMACION_PAUSA;

    PA_InitParallaxX(SCREEN_TOP, 0, 256, 256, 0);
    PA_InitParallaxY(SCREEN_TOP, 0, 256, 256, 0);

    PA_ParallaxScrollXY(SCREEN_TOP, fondoX, fondoY);
    PA_WaitForVBL();
    //PA_InitText(SCREEN_BOTTOM, BACKGROUND_ZERO); // para debugear!!
    while  (1) {
        if (Pad.Held.Up) {
            if (estadoAnimacion != ANIMACION_ARRIBA) {
                estadoAnimacion = ANIMACION_ARRIBA;
                PA_StartSpriteAnim(SCREEN_TOP, HEROE_SPRITE, 3, 5, HEROE_VELOCIDAD);
                PA_SetSpriteHflip(SCREEN_TOP, HEROE_SPRITE, 0);

                PA_StartSpriteAnim(SCREEN_TOP, HEROE_REFLEJO_SPRITE, 3, 5, HEROE_VELOCIDAD);
                PA_SetSpriteHflip(SCREEN_TOP, HEROE_REFLEJO_SPRITE, 0);
            }
        } else if (Pad.Held.Down) {
            if (estadoAnimacion != ANIMACION_ABAJO) {
                estadoAnimacion = ANIMACION_ABAJO;
                PA_StartSpriteAnim(SCREEN_TOP, HEROE_SPRITE, 0, 2, HEROE_VELOCIDAD);
                PA_SetSpriteHflip(SCREEN_TOP, HEROE_SPRITE, 0);
                
                PA_StartSpriteAnim(SCREEN_TOP, HEROE_REFLEJO_SPRITE, 0, 2, HEROE_VELOCIDAD);
                PA_SetSpriteHflip(SCREEN_TOP, HEROE_REFLEJO_SPRITE, 0);
            }
        } else if (Pad.Held.Left) {
            if (estadoAnimacion != ANIMACION_IZQUIERDA) {
                estadoAnimacion = ANIMACION_IZQUIERDA;
                PA_StartSpriteAnim(SCREEN_TOP, HEROE_SPRITE, 6, 8, HEROE_VELOCIDAD);
                PA_SetSpriteHflip(SCREEN_TOP, HEROE_SPRITE, 1);

                PA_StartSpriteAnim(SCREEN_TOP, HEROE_REFLEJO_SPRITE, 6, 8, HEROE_VELOCIDAD);
                PA_SetSpriteHflip(SCREEN_TOP, HEROE_REFLEJO_SPRITE, 1);
            }
        } else if (Pad.Held.Right) {
            if (estadoAnimacion != ANIMACION_DERECHA) {
                estadoAnimacion = ANIMACION_DERECHA;
                PA_StartSpriteAnim(SCREEN_TOP, HEROE_SPRITE, 6, 8, HEROE_VELOCIDAD);
                PA_SetSpriteHflip(SCREEN_TOP, HEROE_SPRITE, 0);

                PA_StartSpriteAnim(SCREEN_TOP, HEROE_REFLEJO_SPRITE, 6, 8, HEROE_VELOCIDAD);
                PA_SetSpriteHflip(SCREEN_TOP, HEROE_REFLEJO_SPRITE, 0);
            }
        } else {
            estadoAnimacion = ANIMACION_PAUSA;
            PA_StopSpriteAnim(SCREEN_TOP, HEROE_SPRITE);
            PA_StopSpriteAnim(SCREEN_TOP, HEROE_REFLEJO_SPRITE);
        }
        if (estadoAnimacion != ANIMACION_PAUSA) {
            s32 x = fondoX + Pad.Held.Right - Pad.Held.Left;
            s32 y = fondoY + Pad.Held.Down - Pad.Held.Up;
            u8 baldosa = obtenerBaldosa(x + HEROE_X + 8, y + HEROE_Y + 16);
            baldosa |= obtenerBaldosa(x + HEROE_X, y + HEROE_Y + 16);
            baldosa |= obtenerBaldosa(x + HEROE_X + 8, y + HEROE_Y + 8);
            baldosa |= obtenerBaldosa(x + HEROE_X, y + HEROE_Y + 8);
            //PA_OutputText(SCREEN_BOTTOM, 0, 6, "baldosa: %d      ", baldosa); // para debugear!!
            if (baldosa == ESPACIO_LIBRE) {
                fondoX = x;
                fondoY = y;
                PA_ParallaxScrollXY(SCREEN_TOP, fondoX, fondoY);
            } else {
                estadoAnimacion = ANIMACION_PAUSA;
            }
        }
        PA_WaitForVBL();
    }
    return 0;
}

u8 obtenerBaldosa(s32 x, s32 y) {
    return mapa_Map[((y>>3) * 92) + (x>>3)];
}

void cargarPortadas() {
    PA_EasyBgLoad(SCREEN_TOP, BACKGROUND_ZERO, portada);
    PA_EasyBgLoad(SCREEN_BOTTOM, BACKGROUND_ZERO, subportada);
    bool salir = false;
    while  (!salir) {
        salir = Stylus.Held;
        PA_WaitForVBL();
    }
    u8 i = 0;
    for (i = 0; i < 32; i++) {
        PA_SetBrightness(SCREEN_TOP, -i);
        PA_SetBrightness(SCREEN_BOTTOM, -i);
        PA_WaitForVBL();
    }
    PA_DeleteBg(SCREEN_BOTTOM, BACKGROUND_ZERO);
    PA_DeleteBg(SCREEN_TOP, BACKGROUND_ZERO);
    PA_SetBrightness(SCREEN_TOP, 0);
    PA_SetBrightness(SCREEN_BOTTOM, 0);
    PA_EasyBgLoad(SCREEN_BOTTOM, BACKGROUND_ZERO, portada);
    return;
}


Este juego por ahora lo dejamos acabado.

Saludos y Feliz año.

Enlaces patrocinados: