Graphics en Canvas

kraneok

Hola gente!
Estoy programando un pequeño videojuego en 2D con Java, utilizando Canvas.
Tengo ya bastantes conceptos implementados, pero, tengo un gran problema que ni en clase me han sabido responder, los profesores no llegan a esos niveles.

Resulta que, no consigo quitar el parpadeo al refrescar las imágenes.

La cosa está tal que así.

El Canvas en realidad se pinta una sola vez ( actualmente ) y ya está.
Los GameObject son los que se pintan, es decir, cada GameObject se pinta así mismo, así pensaba que se reduciría el parpadeo o lo eliminaría completamente, pero nada de nada.

He leído infinidad de foros, de tutoriales y de todo tipo de textos para intentar comprender este problema, pero siempre termino en el mismo lugar y no lo consigo.

Por otra parte, si utilizo el doble buffer ( que ya está siendo utilizado por el Canvas, quiero decir, Canvas ya utilizar doble buffer automáticamente ), aún así, si yo reescribo otro doblebuffer con BufferedImage, puedo reducir el parpadeo, si, pero en cuanto son varios los gameobject a actualizar, ya parpadea de nuevo.

Seguramente me esté equivocando totalmente en la codificación de la aplicación

¿ Alguna ayuda ?
Gracias y saludos.

bLero

¿Utilizas un hilo diferente al de la aplicación para pintar?

No debería parpadear así.

1 respuesta
kraneok

#2 Cada GameObject iba a ser un hilo, pero no lo es, controlo sus vidas yo mismo mientras el juego esté funcionando o el GameObject muera o le toque desaparecer.

1 respuesta
bLero

#3

Necesitas tener un hilo (GameLoop) que se ejecute todo el rato y haga 2 cosas:

1º - actualice la lógica del juego
2º - pinte el buffer en el canvas

Además deberías usar deltatime, ya que sino tu juego será más rápido en una máquina más potente que en otra menos potente.

Te dejo un pequeño código para android que utilicé en su día. Sería casi lo mismo para Java SE.

public class AndroidFastRenderView extends SurfaceView implements Runnable {
    AndroidGame game;
    Bitmap framebuffer;
    Thread renderThread = null;
    SurfaceHolder holder;
    volatile boolean running = false;
    
public AndroidFastRenderView(AndroidGame game, Bitmap framebuffer) { super(game); this.game = game; this.framebuffer = framebuffer; this.holder = getHolder(); } public void resume() { running = true; renderThread = new Thread(this); renderThread.start(); } public void run() { Rect dstRect = new Rect(); long startTime = System.nanoTime(); while(running) { if(!holder.getSurface().isValid()) continue; float deltaTime = (System.nanoTime()-startTime) / 1000000000.0f; startTime = System.nanoTime(); game.getCurrentScreen().update(deltaTime); game.getCurrentScreen().present(deltaTime); Canvas canvas = holder.lockCanvas(); canvas.getClipBounds(dstRect); canvas.drawBitmap(framebuffer, null, dstRect, null); holder.unlockCanvasAndPost(canvas); } } public void pause() { running = false; while(true) { try { renderThread.join(); break; } catch (InterruptedException e) { // retry } } }

Si necesitas que te explique algo avisa.

1 respuesta
kraneok

#4 Muuchas gracias, lo voy a leer!

TheNext

Hola, para usar canvas de forma eficiente en java2D y evitar parpadeos, tienes que activar el doubleBuffering. Que yo sepa canvas no lo utiliza automaticamente.

Para eso tienes que crearte un bufferStrategy:

protected BufferStrategy buffer;

y llamar a

createBufferStrategy(2);
buffer = getBufferStrategy();

cuando inicializas el canvas.

Despues, en tu bucle de renderizado, para repintar el canvas haces:


Graphics g;
try{g=buffer.getDrawGraphics();}catch(Exception e){return;}
this.paint((Graphics2D)g);
g.dispose();


     if( !buffer.contentsLost() )
  buffer.show();[/b]

siendo paint la funcion donde llamas a todos los metodos de dibujado.

Supongo que también tendras puesto el ignorar el repaint automático ( this.setIgnoreRepaint(true); ), y llamas tu a mano a las funciones de redibujado con un bucle a 30 fps o 60 fps o lo que prefieras :P

Saludos!

1 1 respuesta
kraneok

#6 Oh la! laaa!!, eso si que mola xD.
Muchas gracias!!

kraneok

Otra dudita, lo del Canvas ya lo solucioné, muchísimas gracias, de todos modos me queda muchísimo por afinar.

Bien, os comento, el gameloop debe tener lo del deltaTime, bien, pues no sé calcularlo bien.
Os pego aquí el código que llevo hecho y si puede ser me guiáis un poquito, a ver si consigo verlo.

public void loopGame()
    {
    
//Calculate time paint in miliseconds long deltaTime = 1 / g_GameFPS; //<- int 60 long currentTime = 2; while( !isFinishedGame() ) { //While game not finished if( ) { } currentTime = System.currentTimeMillis(); } }

Gracias de nuevo y un saludete!

TheNext

La sincronización de tiempos en java/windows es otra patada en los cojones para el programador.

Yo me basé en el algoritmo de sincronización del LWJGL:

https://github.com/LWJGL/lwjgl/blob/master/src/java/org/lwjgl/opengl/Sync.java

crea una funcion, Sync, que te sincroniza al framerate que quieras.

Saludos.

BLZKZ

lectura recomendable: http://www.java-gaming.org/topics/game-loops/24220/view.html

Además ese foro es muy bueno.

TheNext

Si, el foro de java-gaming es el mejor para resolver tus dudas de java (aparte de este claro xd).

Usuarios habituales

  • TheNext
  • BLZKZ
  • kraneok
  • bLero