rotozoomSurfaceXY que ocurre?

Tratamos sobre el manejo de APIs frecuentemente utilizadas en el desarrollo de videojuegos, como SDL, pygame o SFML.

rotozoomSurfaceXY que ocurre?

Notapor astut » Sab Abr 21, 2007 1:13 pm

Bueno uso esta funcion, pero cada vez que la utilizo en mi programa la imagen se aleja cada vez mas de su centro y ademas se "deforma", utilizo el siguiente metodo:
Código: Seleccionar todo
void CImagen::CfgAngulo(int angulo,int imagen)
{
repositorio[imagen]= rotozoomSurfaceXY(repositorio[imagen],angulo,1,-1,0);
}

¿Como deberia utilizarlo? esque los parametros no me quedan claros, solo los 2 primeros, gracias
astut
 
Mensajes: 7
Registrado: Vie Mar 16, 2007 9:20 am

Notapor rcaf » Sab Abr 21, 2007 7:42 pm

En realidad no debería deformarse ya que el factor de escalamiento en el eje x e y siempre es el mismo, solo estás cambiando el ángulo.

Pero ojo que deberías crear una superficie temporal donde guardes la superficie modificada (ya rotada), luego copiar a la pantalla la superficie temporal, y por último eliminar la memoria de la superficie temporal.

El prototipo de la función rotozoomSurfaceXY es:

Código: Seleccionar todo
SDL_Surface * rotozoomSurfaceXY (SDL_Surface *src, double angle, double zoomx, double zoomy, int smooth);


- Devuelve una superficie ya modificada (rotada y/o escalada).

- El primero parámetro src, es la superficie original a modificar.

- El segundo parámetro es el ángulo con que rotarás la superficie y medido en grados (0-360)

- El tercer y cuarto parámetro es el factor de escalamiento que aplicarás a la superficie, en el eje x e y respectivamente. Un valor de 1.0 corresponde al tamaño original.

- Y el último parámetro corresponde a un flag que indica si se dibujará o no con anti-aliasing.

Yo utilizo la siguiente función para dibujar superficies escaladas y/o rotadas en la pantalla:

Código: Seleccionar todo
void DrawSurface(SDL_Surface *src, int x, int y, float zoomx, float zoomy, float angle)
{
    // Superficie temporal con las modificaciones correspondientes
    SDL_Surface *tmp;

    // Posición donde dibujaremos la superficie en pantalla
    SDL_Rect pos={x, y, 0, 0};

    // Creamos superficie rotada y/o escalada sin anti-aliasing
    tmp=rotozoomSurfaceXY(src, angle, zoomx, zoomy, 0);

    // Dibujamos en pantalla la nueva superficie
    // Nota: screen es la variable que nos devolvió
    // la función SDL_SetVideoMode
    SDL_BlitSurface(tmp, NULL, screen, &pos);

    // Eliminamos de la memoria la superficie temporal
    SDL_FreeSurface(tmp);
}


Saludos!
Roberto Albornoz Figueroa
Mi Blog
Wiki
Avatar de Usuario
rcaf
 
Mensajes: 71
Registrado: Jue Ago 10, 2006 4:35 am
Ubicación: Concepción, Octava Región. Chile

Notapor rcaf » Sab Abr 21, 2007 8:15 pm

Me falto comentar un detalle de la función anterior.

Si estás dibujando y rotando una imagen, esta se rotará por defecto con respecto al punto (0,0) de la imagen, lo que provocará que la rotación no se vea bien.

La solución es suponer que rotamos con respecto al centro de la imagen, lo que significará que la posición (x, y) no hará referencia al punto (0,0) sino al centro (cx, cy) de la imagen, por lo tanto antes de dibujar la imagen en pantalla deberás restarle a cada eje, la mitad de la diferencia de las dimensiones de la imagen resultante y la imagen inicial, me explico:

src es la superficie original
tmp la superficie resultante (rotada o escalada)

Código: Seleccionar todo
pos.x -= (tmp->w - src->w) / 2;
pos.y -= (tmp->h - src->h) / 2;


Por lo tanto el código anterior con la nueva modificación quedaría de la siguiente forma:

Código: Seleccionar todo
void DrawSurface(SDL_Surface *src, int x, int y, float zoomx, float zoomy, float angle)
{
    // Superficie temporal con las modificaciones correspondientes
    SDL_Surface *tmp;

    // Posición donde dibujaremos la superficie en pantalla
    SDL_Rect pos={x, y, 0, 0};

    // Creamos superficie rotada y/o escalada sin anti-aliasing
    tmp=rotozoomSurfaceXY(src, angle, zoomx, zoomy, 0);

    // Arreglo posición de la imagen ya que la rotación
    // es con respecto al centro de la imagen
    pos.x -= (tmp->w - src->w) / 2;
    pos.y -= (tmp->h - src->h) / 2;

    // Dibujamos en pantalla la nueva superficie
    // Nota: screen es la variable que nos devolvió
    // la función SDL_SetVideoMode
    SDL_BlitSurface(tmp, NULL, screen, &pos);

    // Eliminamos de la memoria la superficie temporal
    SDL_FreeSurface(tmp);
}
Roberto Albornoz Figueroa
Mi Blog
Wiki
Avatar de Usuario
rcaf
 
Mensajes: 71
Registrado: Jue Ago 10, 2006 4:35 am
Ubicación: Concepción, Octava Región. Chile

Notapor astut » Sab Abr 21, 2007 9:38 pm

Gracias, aunque prefiero usar una funcion aparte de la de dibujado para mayor organizacion (CfgAngulo), bueno, ahora pretendo que el angulo de la imagen apunte hacia la posicion del raton:
Código: Seleccionar todo
if (evento.type== SDL_MOUSEMOTION)
    avion.CfgAngulo(evento.motion.x,1);

Vale, pero es la coordenada x... de que forma ago que apunte hacia el raton???, gracias!! ^^
astut
 
Mensajes: 7
Registrado: Vie Mar 16, 2007 9:20 am

Notapor hugoruscitti » Dom Abr 22, 2007 3:37 am

Saludos,

Pienso que el problema de la 'deformación' que sufren los gráficos al ser
modificados es a causa de como están compuestas las imágenes. Como indica uno de
los tutoriales de RCAF[1], cada imagen está compuesta de diferentes pixeles y
una transformación como la rotación 'reordena' dichos pixeles en nuevas
posiciones y esto generalmente no es reversible.

En tu función 'CfgAngulo' la transformación de rotación se realiza todo el
tiempo sobre la misma imagen, por lo tanto cada transformación deteriora aún
mas la imagen original, mira este ejemplo:

Imagen

en el primer caso se muestra el deterioro que sufre una superficie cuando se
'transforma' muchas veces sobre sí misma, como lo hace tu método. En cambio, el
segundo caso muestra como quedan las rotaciones cuando
se calculan a partir de la imagen original, como lo realiza la función de RCAF
(DrawSurface).

Como sugerencia te recomiendo que utilices la función que desarrolló RCAF o
tengas en cuenta este detalle para modificar el funcionamiento del método
'CfgAngulo', la idea del cambio sería 'calcular la rotación desde el cuadro
original sin alterarlo'.


bueno, ahora pretendo que el angulo de la imagen apunte hacia la posicion del
raton


En este caso, tendrías que calcular el ángulo que determinan el centro de la
imagen (x, y) y la posición del punto (mouse_x, mouse_y). Para ello podrías
utilizan la función trigonométrica 'arco-tangente'. Sería algo así:

Código: Seleccionar todo
#include <math.h>
#define PI 3.1415927

void dibujar_superficie_mirando_el_puntero(SDL_Surface * src, SDL_Surface * screen)
{
    int x = 200;        /* centro de la imagen a imprimir */
    int y = 200;        /* centro de la imagen a imprimir */
    int mouse_x, mouse_y;
    int distancia_x, distancia_y;
    float angulo;

    SDL_GetMouseState(&x, &y);

    distancia_x = abs(mouse_x - x);
    distancia_y = abs(mouse_y - y);

    angulo = (atan2(-distancia_y, distancia_x) * 180) / PI;

    DrawSurface(src, x, y, 1.0, 1.0, angulo);
}


Si este código funciona... debería imprimir la superficie que le envíes como
parámetro 'rotada' en la dirección a donde apunta el mouse. Habría que probarlo,
imagino que funcionará. La imagen original debería señalar hacia la derecha
(angulo 0).

Si añades una función como esta a tu programa recuerda indicarle al
compilador el parámetro '-lm' en el momento que quiera enlazar el programa para
generar un programa ejecutable. Así se vincula la biblioteca matemática que
implementa la función 'atan2' a tu programa, de otra forma falla.

Ah, también sería bueno que consultes el artículo 'usando seno y coseno'[2] donde
se que explica como utilizar estas funciones trigonométricas.

Buena suerte.

Referencias:

1 - http://www.losersjuegos.com.ar/referenc ... asicos.php
2 - http://www.losersjuegos.com.ar/referenc ... coseno.php
Avatar de Usuario
hugoruscitti
Site Admin
 
Mensajes: 1242
Registrado: Dom Jul 30, 2006 3:57 am
Ubicación: Buenos Aires, Argentina

Notapor rcaf » Dom Abr 22, 2007 5:44 am

El código que publicó Hugo está bien, excepto algunos detalles que noté:

Primero, la llamada a la función GetMouseState se encuentra con las variables incorrectas, debe ser:

Código: Seleccionar todo
SDL_GetMouseState(&mouse_x, &mouse_y);


Segundo, el cálculo del ángulo entre los dos vectores se debe realizar sin el valor absoluto, ya que al usar abs() estamos limitando el movimiento de la imagen solo a un cuadrante, por lo tanto el cálculo debería ser de la siguiente forma:

Código: Seleccionar todo
distancia_x = mouse_x - cx;
distancia_y = mouse_y - cy;

angulo = (atan2(distancia_y, distancia_x) * 180) / PI;


Por último una acotación, podrías agregar una condición para que cambie el ángulo solo cuando el puntero del mouse se encuentre fuera del área de la imagen, es decir:

Código: Seleccionar todo
distancia_x = mouse_x - cx;
distancia_y = mouse_y - cy;

// En este caso si utilizamos valor absoluto
if(abs(distancia_x) > ancho_imagen || abs(distancia_y) > alto_imagen)
{
  angulo = (atan2(distancia_y, distancia_x) * 180) / PI;
}


Resumiendo, el código general en el ciclo de eventos debería quedar así:

Código: Seleccionar todo
if (evento.type == SDL_MOUSEMOTION)
{
  // Obtenemos posición actual del mouse
  int mouse_x=evento.motion.x;
  int mouse_y=evento.motion.y;

  // (cx, cy) coordenadas del centro de la imagen
  int distancia_x = mouse_x - cx;
  int distancia_y = mouse_y - cy;

  // Si el puntero del mouse está fuera del área de la imagen
  // obtenemos el ángulo entre los dos vectores
  // ancho_imagen, alto_imagen dimensiones de la imagen
  if(abs(distancia_x) > ancho_imagen || abs(distancia_y) > alto_imagen)
  {
      angulo = (atan2(distancia_y, distancia_x) * 180) / PI;
  }
}


o utilizando mouse polling:

Código: Seleccionar todo
// Guardará la posición del mouse
int mouse_x, mouse_y;

// Obtenemos posición actual del mouse
SDL_GetMouseState(&mouse_x, &mouse_y);

// (cx, cy) coordenadas del centro de la imagen
int distancia_x = mouse_x - cx;
int distancia_y = mouse_y - cy;

// Si el puntero del mouse está fuera del área de la imagen
// obtenemos el ángulo entre los dos vectores
// ancho_imagen, alto_imagen dimensiones de la imagen
if(abs(distancia_x) > ancho_imagen || abs(distancia_y) > alto_imagen)
{
    angulo = (atan2(distancia_y, distancia_x) * 180) / PI;
}


Saludos!
Roberto Albornoz Figueroa
Mi Blog
Wiki
Avatar de Usuario
rcaf
 
Mensajes: 71
Registrado: Jue Ago 10, 2006 4:35 am
Ubicación: Concepción, Octava Región. Chile

Notapor astut » Dom Abr 22, 2007 10:36 am

Muchas gracias, ¿y como hacer que la imagen valla hacia las coordenadas del raton?
astut
 
Mensajes: 7
Registrado: Vie Mar 16, 2007 9:20 am

Notapor rcaf » Dom Abr 22, 2007 6:26 pm

astut escribió:Muchas gracias, ¿y como hacer que la imagen valla hacia las coordenadas del raton?


¿Cómo un proyectil dirigido? y ¿Qué se mueva de forma automática?

Si es así, solo debes mover la imagen en cada ciclo hacia el objetivo que serán las coordenadas del mouse.

Ya tenemos calculado el ángulo entre los dos vectores (la imagen y el puntero del mouse), por lo tanto en el ciclo del juego deberías realizar algo como esto:

Código: Seleccionar todo

// Guardará la posición del mouse
int mouse_x, mouse_y;

// Obtenemos posición actual del mouse
SDL_GetMouseState(&mouse_x, &mouse_y);

// (cx, cy) coordenadas del centro de la imagen
int distancia_x = mouse_x - cx;
int distancia_y = mouse_y - cy;

// Si el puntero del mouse está fuera del área de la imagen
// obtenemos el ángulo entre los dos vectores
// ancho_imagen, alto_imagen dimensiones de la imagen
if(abs(distancia_x) > ancho_imagen || abs(distancia_y) > alto_imagen)
{
    angulo = (atan2(distancia_y, distancia_x) * 180) / PI;
    mover_imagen=1; // la imagen se debe mover
}
else mover_imagen=0; // la imagen se detiene

// La imagen solo se mueve cuando el puntero del mouse está fuera
// del área de la imagen
if(mover_imagen)
{
   if (speed <= 10) speed += 0.3;
}
// El movimiento de la imagen se detiene cuando llega a su objetivo
// es decir, el puntero del mouse se encuentra dentro del área de
// la imagen
else
{
   speed=0;   
}

// Calcula la coordenada x e y del vector velocidad
vel.x = speed * cos(angulo*PI/180);
vel.y = speed * sin(angulo*PI/180);

// Movemos la imagen
pos.x += vel.x;
pos.y += vel.y;


En el código anterior suponemos que tenemos una variable speed que contiene la longitud del vector velocidad, una estructura llamada vel con dos componentes x e y para almacenar el vector velocidad de la imagen, y por último otra estructura pos con las componentes x e y para almacenar el vector posición de la imagen.

Saludos!
Roberto Albornoz Figueroa
Mi Blog
Wiki
Avatar de Usuario
rcaf
 
Mensajes: 71
Registrado: Jue Ago 10, 2006 4:35 am
Ubicación: Concepción, Octava Región. Chile


Volver a Sobre las bibliotecas multimedia

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 1 invitado

cron