Duda OPENGL: actualizar posicion de personajes

Bueno, el caso es que tengo resuelto el problema, pero no se si de la manera mas correcta o no.

Imaginemos una barra que tiene que ir de derecha a izquiera y de izquierda a derecha de la pantalla sin parar de moverse, rebotando en las paredes. La cosa es, cada cuando actualizo la posicion de esta barra?

La primera idea fue ponerlo en la funcion de dibujar, pero entonces dependiendo de en que PC iria a diferente velocidad, no?
Entonces se me ocurrio lo que al final fue la solucion que finalmente he aplicado: poner un timer, glutTimerFunc(20,Timer,0) y dentro de Timer actualizar la posicion.

Es correcto eso? O hay otra manera mejor de hacer las cosas? 20ms son muchos o pocos para lo que pretendo?

Gracias.
La clave está en mover la barra un número de pixels determinado por los milisegundos que han pasado desde el último movimiento de la barra (es decir, desde el último frame) y mover esta en cada frame, evidentemente no dentro de la función de render pero si justo antes en el bucle del juego. Es decir:

pos.x = pos.x + vel.x * milisegundos / 1000;

Eso teniendo en cuenta que la variable vel tiene la velocidad expresada en unidades/segundo.

De esta manera te da igual al número de frames por segundo a la que se ejecute tu aplicación porque la barra siempre se moverá a la misma velocidad. Ahora, de la manera que lo haces tu también funciona, aunque personalmente nunca me gustó glut a no ser que sea para prototipos o cosas así.
Es que tampoco se si la idea esta bien estructurada, porque lo unico que hago es inicializar cosas y luego lanzar el glutMainLoop() y ya esta (es decir que se vaya dibujando), actualizo con el timer y despues de actualizar le doy a un glutPostRedisplay(que redibuje vamos) pero eso tambien lo hace en glutDisplayFunc(display) y en glutIdleFunc(display);

Mucha universidad pero OpenGL las clases han sido... id nehe y aprended, ya nos veremos en febrero. Por suerte, esta aprobada, pero el tema me gusta y cabrea un poco eso.

Si no te importa, como lo harias tu de manera similar?
Lo que no pillo es lo del "bucle del juego" sin meterlo dentro del timer.
La estructura de practicamente todos los juegos es la siguiente:

inicialización();
while(seguir)
{
logica();
render();
}

A eso me refiero con el bucle del juego, al bucle que al final es el que marca los fps de la aplicación. Glut hace esto mediante la función glutMainLoop() , en la que detecta los eventos del sistema y va llamando a las diferentes funciones callback que has definido anteriormente pasandole a glut un puntero a dichas funciones (por ejemplo la función de dibujado con glutDisplayFunc(), o cuando detecta un evento de teclado la que definas con la glutKeyboardFunc() creo que se llama). Lo que suele hacer la gente cuando trabaja con glut es definir la misma función para render(glutDisplayFunc()) que para cuando glut no hace nada (glutIdleFunc() creo que es) y meter ahí primero la lógica del juego y depues el render. De esa manera creo que te aseguras que se va a ejecutar una vez cada frame, aunque no me hagas mucho caso porque solo he utilizado esto para hacer demos gráficas con una carga mínima de lógica. Igual los que programan juegos con glut lo hacen de otra manera.

De todos modos para entender esto mejor deberías intentar hacer un juego sin Glut (algo sencillo, una ventana en SDL con una imagen estática vale) para que veas que es lo que hace glut por detras, ya que no es mas que un bucle que recoge eventos y llama a las funciones que tu le dices que tiene que llamar para cada evento.

Por cierto, glutPostRedisplay() no redibuja, lo que hace es decir a glut que llame cuanto antes a la función que le mandas en glutDisplayFunc() para que refresque la pantalla.
kbks escribió:La estructura de practicamente todos los juegos es la siguiente:

inicialización();
while(seguir)
{
logica();
render();
}

A eso me refiero con el bucle del juego, al bucle que al final es el que marca los fps de la aplicación. Glut hace esto mediante la función glutMainLoop() , en la que detecta los eventos del sistema y va llamando a las diferentes funciones callback que has definido anteriormente pasandole a glut un puntero a dichas funciones (por ejemplo la función de dibujado con glutDisplayFunc(), o cuando detecta un evento de teclado la que definas con la glutKeyboardFunc() creo que se llama). Lo que suele hacer la gente cuando trabaja con glut es definir la misma función para render(glutDisplayFunc()) que para cuando glut no hace nada (glutIdleFunc() creo que es) y meter ahí primero la lógica del juego y depues el render. De esa manera creo que te aseguras que se va a ejecutar una vez cada frame, aunque no me hagas mucho caso porque solo he utilizado esto para hacer demos gráficas con una carga mínima de lógica. Igual los que programan juegos con glut lo hacen de otra manera.

De todos modos para entender esto mejor deberías intentar hacer un juego sin Glut (algo sencillo, una ventana en SDL con una imagen estática vale) para que veas que es lo que hace glut por detras, ya que no es mas que un bucle que recoge eventos y llama a las funciones que tu le dices que tiene que llamar para cada evento.

Por cierto, glutPostRedisplay() no redibuja, lo que hace es decir a glut que llame cuanto antes a la función que le mandas en glutDisplayFunc() para que refresque la pantalla.


Vale, es que aqui la logica esta toda dentro de la "funcion de dibujar" digamos.
Con lo de glutPostRedisplay() ya me referia a eso, quiza me explique mal, y en glutDisplayFunc() y en glutIdleFunc() tambien tengo puesto lo mismo, asi que eso es correcto.
Entonces al final poniendolo todo en la funcion que le paso a estas dos funciones ya deberia de funcionar, pero eso dependeria de los FPS no? Entonces tendria que controlar el tiempo que ha pasado entre uno y otro y hacer lo de la velocidad... no?

Gracias entonces, esta bien como lo tengo pero se podria hacer mas facil.
No te lies tanto, cada vez que hagas un frame vuela lo que te convenga( prueba y error)
La forma de actualizar la lógica en juegos (posiciones, etc etc) normalmente es en base al tiempo transcurrido entre frames.
Échale un ojo a hilo_cual-es-la-forma-correcta-de-calcular-los-fps_1161871
Así siempre te irá tido a la misma velocidad independientemente de donde corra la aplicación.
Un timer que salte cada X tiempo no suele ser la opción más adecuada, porque notarás tirones al no corresponderse la velocidad de actualización (esos 20ms que pones) con la de pintado (idealmente 1000/60 ms)
Saludos :)
te comento mi forma de sincronizar los FPS (frames por segundo)

lo ideal es separar el render de la logica de la aplicacion, por lo cual tendras dos rutinas: una que se llama update_logica() y otra que se llame render() esto te permitira incluso hacer que las actualizaciones de logica sean mas a menudo que el render (por ejemplo, en un juego de simulacion de coches, actualizas la logica cada 10msec, pero dibujas cada 60msec, lo cual te permite tener un control mucho mayor de las fisicas).

te pongo un algoritmo basico, que actualiza las fisicas a la misma velocidad de los fps:
#define FPS 60
#define NEXTFRAMELAPSE 1000/FPS  //cuantos milisegundos entre frames?
#define MAXFRAMESKIP 20 //estoy dispuesto a saltarme 20 frames si no me da tiempo, pero ni uno mas

next_frame=get_current_milisec()+NEXTFRAMELAPSE; //no recuerdo con exactitud la funcion exacta, pero creo que se entiende
frame_skip=0;

while(1)
{
  if(get_current_milisec()>next_frame) //o sea, nos toca trabajar?
  {
    next_frame=next_frame+NEXTFRAMELAPSE; //para saber cuando nos tocara currar de nuevo
    update_logica(); //la logica la tenemos que actualizar por cojones
    if (get_current_milisec()>next_frame && frame_skip<MAXFRAMESKIP)
    {
      //si entramos aqui, es que no nos da tiempo a dibujar porque ya deberiamos estar en el otro frame
      //y ademas, aun estamos dentro de los frames permitidos que nos podemos 'comer'
      frame_skip++;
    }
    else
    {
      //si entramos aqui, o bien aun estamos en tiempo de dibujar, o bien tenemos que plantar un frame por cojones
      render();
      frame_skip=0;
    }
  }
}


este algoritmo se puede mejorar para que la fisica sea 'refrescada' mas veces que los fps a mostrar.

tambien se puede hacer un algortimo que intente sacar el numero maximo de 'renders' por segundo y que no este limitado a unos FPS prefijados, pero en tal caso, deberas 'arquitecturar' tu rutina de 'update_logica()' para que pasandole el tiempo que ha pasado desde el frame anterior al actual, actualice la logica en consonancia.
Está utilizando glut, por lo que no puede modificar directamente el bucle principal. Tiene que hacerlo definiendo funciones callback y eso es lo que pregunta, porque en glut no puedes definir una función exclusicamente para la lógica.

Lo que yo haría es una función (por ejemplo loop()) que sea la que pasas a glutDisplayFunc() y a glutIdleFunc() y que esta función solamente tega dos llamadas a otras dos funciones: primero a updateLogica() y despues a render().

Respecto al movimiento, para hacerlo independiende a los fps:

pos.x = pos.x + vel.x * milisegundos / 1000;

siendo milisegundos los milisegundos que duró el frame anterior y expresando la velocidad en puntos/segundo. Para calcular lo que tarda un frame simplemente tienes que mirar el milisegundo en el que empieza (en loop justo antes de llamar a updateLogica()) y en el que termina (justo despues de llamar a render()).

Repito que no he usado mucho glut, que esta es la manera en la que yo lo haría, pero que seguro que hay maneras mucho mejores para hacerlo.
8 respuestas