En nuestro ejemplo anterior, el personaje se desplaza por un escenario que es mostrado por pantalla. Si este escenario tiene un tamaño mayor que el de la pantalla solo se estará mostrando una parte de este, concretamente la parte donde este situado nuestro personaje.
Cuando el personaje empieza a recorrer el escenario, puede llegar en algún momento en que llegue a un rincón que no se muestra por pantalla, para evitar esto se desplaza la imagen del escenario de manera que se muestre por pantalla por donde va nuestro personaje.
A este desplazamiento de imagen se le llama Scroll.
Para entender un poco mas esto, es como si la pantalla fuera una cámara que persigue a nuestro personaje.
En este ejemplo el control de dicha cámara será controlado desde la clase player.
class player
{
// posicion del personaje con respecto imagen
int x, y;
// posicion del personaje con respecto pantalla
int px, py;
// posicion de la camara que sigue al personaje
int camarax, camaray;
int dir;
int paso;
int tam;
int tiempoPaso;
int tiempoCont;
ALLEGRO_BITMAP* img;
public:
int getCamX() { return camarax; }
int getCamY() { return camaray; }
void inicia()
{
img = al_load_bitmap("datos/per32s.png");
tiempoPaso = int(sistema.FPS / mueve);
tiempoCont = 0;
x = 260;
y = 260;
px = x;
py = y;
camarax = 0;
camaray = 0;
dir = 0;
paso = 0;
tam = 32;
}
bool colisiona()
{
escena.bloquea();
bool valor = false;
for (int i = 1; i < tam-1; i++)
{
for (int j = tam/2; j < tam; j++)
{
int vx = x + i;
int vy = y + j;
if (escena.esRojo(vx, vy))
{
valor = true;
}
}
}
escena.desbloquea();
return valor;
}
void actualiza(ALLEGRO_EVENT evento)
{
ALLEGRO_KEYBOARD_STATE teclado;
// estado anterior
int ax, ay;
ax = x;
ay = y;
al_get_keyboard_state(&teclado);
if (al_key_down(&teclado, ALLEGRO_KEY_UP))
{
y -= desplaza;
dir = 3;
tiempoCont++;
}
if (al_key_down(&teclado, ALLEGRO_KEY_DOWN))
{
y += desplaza;
dir = 0;
tiempoCont++;
}
if (al_key_down(&teclado, ALLEGRO_KEY_LEFT))
{
x -= desplaza;
dir = 1;
tiempoCont++;
}
if (al_key_down(&teclado, ALLEGRO_KEY_RIGHT))
{
x += desplaza;
dir = 2;
tiempoCont++;
}
if ((x != ax || y != ay) && colisiona())
{
x = ax;
y = ay;
}
// limitadores
if (x < 0) x = 0;
if (x > escenax - tam) x = escenax - tam;
if (y < 0) y = 0;
if (y > escenay - tam) y = escenay - tam;
if (tiempoCont > tiempoPaso)
{
paso++;
tiempoCont = 0;
}
if (paso > 2) paso = 0;
if (x < pantallax / 2) camarax = 0;
if (y < pantallay / 2) camaray = 0;
if (x >= pantallax / 2 && x <= escenax - (pantallax / 2)) camarax = -x + (pantallax / 2);
if (y >= pantallay / 2 && y <= escenay - (pantallay / 2)) camaray = -y + (pantallay / 2);
if (x > escenax - (pantallax / 2)) camarax = pantallax - escenax;
if (y > escenay - (pantallay / 2)) camaray = pantallay - escenay;
px = x + camarax;
py = y + camaray;
}
void pinta()
{
al_draw_bitmap_region(img, paso * tam, dir * tam, tam, tam, px, py, 0);
}
} jugador;
Al principio de la clase se definen las variables privadas de la clase. Después de public tenemos todos los métodos públicos de la clase.Se tienen getCamX(), getCamY() para obtener la posición de la cámara que sigue al personaje.
El método inicia, sirve para inicializar todas las variables de la clase.
El método colisiona, se encarga de comprobar si choca el personaje con algún objeto del escenario. Contiene dos bucles que se encargan de recorrer todo el espacio del cuerpo del personaje.
Dentro se va comprobando pixel a pixel, si el pixel de la imagen de choque es rojo, en ese caso la variable valor es true, es decir, que ha colisionado.
Para un correcto funcionamiento se añade las funciones escena.bloquea(), y escena.desbloquea(). Que es necesario para usar repetidamente la función escena.esRojo() que comprueba el pixel seleccionado.
El método actualiza, se encarga de comprobar las teclas que manejan a nuestro personaje, y con la nueva posición de x,y se obtiene la nueva posición de la pantalla.
El método pinta, como su nombre indica pinta el personaje.
void dibuja()
{
al_clear_to_color(sistema.fondo);
escena.pinta(jugador.getCamX(), jugador.getCamY());
jugador.pinta();
escena.pinta(jugador.getCamX(), jugador.getCamY(), true);
// al_draw_textf(font, al_map_rgb(60, 60, 60), 50, 330, 0, "%d, %d", px,py);
al_draw_text(font, al_map_rgb(0, 0, 0), 50, 380, 0, "KodayRPG 2020");
// muestra por pantalla
al_flip_display();
}
Dibuja(), pinta la escena, al jugador, y el texto y lo muestra por pantalla.
void juego()
{
font = al_load_ttf_font("datos/neuropol.ttf", 64, 0);
ALLEGRO_EVENT evento;
bool repetir = true;
bool dibujar = true;
jugador.inicia();
escena.carga();
while (repetir)
{
// Pinta si es dibuja y esta vacia la lista de eventos
if (dibujar && al_event_queue_is_empty(sistema.Mis_eventos))
{
dibuja();
dibujar = false;
}
// esperamos a que ocurra un evento
al_wait_for_event(sistema.Mis_eventos, &evento);
// se ha cerrado la ventana
if (evento.type == ALLEGRO_EVENT_DISPLAY_CLOSE)
{
repetir = false;
}
// se ha pulsado ESC
if (evento.type == ALLEGRO_EVENT_KEY_DOWN)
{
if (evento.keyboard.keycode == ALLEGRO_KEY_ESCAPE)
{
repetir = false;
}
}
// pasa un tiempo determinado
if (evento.type == ALLEGRO_EVENT_TIMER)
{
dibujar = true;
jugador.actualiza(evento);
}
}
al_destroy_font(font);
}
juego() contiene el bucle principal que se repite mientras que repetir sea verdadero, este valor cambia cuando se pulsa la tecla ESC o se hace click en la X de la ventana.
int main(void)
{
// inicializamos las librerías utilizadas
al_init();
al_init_primitives_addon();
al_init_font_addon();
al_init_ttf_addon();
al_init_image_addon();
al_install_keyboard();
ALLEGRO_DISPLAY* display = al_create_display(800, 450);
ALLEGRO_TIMER* timer = NULL;
al_set_window_title(display, "KodayRPG");
sistema.img = al_load_bitmap("datos/escenario.png");
sistema.fondo = al_map_rgb(255, 255, 255);
sistema.FPS = 60;
timer = al_create_timer(1.0 / sistema.FPS);
// creo lista de eventos
sistema.Mis_eventos = al_create_event_queue();
// asigno eventos a la lista de eventos
al_register_event_source(sistema.Mis_eventos, al_get_keyboard_event_source());
al_register_event_source(sistema.Mis_eventos, al_get_display_event_source(display));
al_register_event_source(sistema.Mis_eventos, al_get_timer_event_source(timer));
al_start_timer(timer);
juego();
al_destroy_display(display);
}
Aquí tienen el código completo para descargar, con las imágenes incluidas.
Descargar
Nota: Para los que descargaron el archivo antes del 19 de nov, el ejemplo viene con un error. El nombre de la imagen que se utiliza es incorrecto, debes cambiarlo a per32s.png. Si no se hace este cambio da un error de ejecución al no encontrar la imagen.
bro,me sale un error, es este:
ResponderEliminar[Warning] anonymous type with no linkage used to declare variable ' sistema' with linkage
espero me puedas responder, gracias
Esa advertencia supongo que se debe a que se a declarado una variable con un tipo anónimo y te da problemas. Prueba a darle un nombre por ejemplo datos quedando de la siguiente forma:
Eliminarstruct datos{
int FPS;
ALLEGRO_EVENT_QUEUE* Mis_eventos;
ALLEGRO_COLOR fondo;
ALLEGRO_BITMAP* img;
} sistema;
Saludos.