2011-12-15 11 views
14

He estado estudiando y haciendo pequeños juegos por un tiempo, y he decidido últimamente que trataría de desarrollar juegos para Android.¿La mejor manera de separar la lógica del juego de la representación para un juego rápido para Android con OpenGL?

Para mí, saltar de código C++ nativo a Android Java no fue tan difícil, pero me da dolores de cabeza pensar cómo podría mantener la lógica separada de la representación.

He estado leyendo por aquí y en otros sitios que:

Es mejor no crear otro hilo para ello, sólo porque voluntad Android con seguridad no tendrá problemas para su procesamiento.

Significado del código sería algo como esto:

public void onDrawFrame(GL10 gl) { 
    doLogicCalculations(); 
    clearScreen(); 
    drawSprites(); 
} 

Pero no estoy seguro de si eso sería el mejor enfoque. Dado que no creo que me guste cómo se verá si pongo mi lógica dentro del método GLRenderer::onDrawFrame. Hasta donde yo sé, este método está destinado a dibujar, y puedo ralentizar los cuadros si pongo lógica allí. Sin mencionar que daña los conceptos de POO en mi comprensión.

creo que el uso de hilos podría ser cierto, esta es la forma en que estaba planeando:

Actividad principal:

public void onCreate(Bundle savedInstanceState) { 
    //set fullscreen, etc 

    GLSurfaceView view = new GLSurfaceView(this); 
    //Configure view 

    GameManager game = new GameManager(); 
    game.start(context, view); 

    setContentView(view); 
} 

GameManager:

OpenGLRenderer renderer; 
Boolean running; 
public void start(Context context, GLSurfaceView view) { 
    this.renderer = new OpenGLRenderer(context); 
    view.setRenderer(this.renderer); 

    //create Texturelib, create sound system... 

    running = true; 
    //create a thread to run GameManager::update() 
} 

public void update(){ 
    while(running){ 
     //update game logic here 
     //put, edit and remove sprites from renderer list 
     //set running to false to quit game 
    } 
} 

y, finalmente, OpenGLRenderer:

ListOrMap toDraw; 
public void onDrawFrame(GL10 gl) { 
    for(sprite i : toDraw) 
    { 
     i.draw(); 
    } 
} 

Esta es una idea aproximada, no completamente completa. Este patrón lo mantendría todo separado y se vería un poco mejor, pero ¿es el mejor para el rendimiento?

Mientras investigué, la mayoría de los ejemplos de juegos con subprocesos utilizan canvas o surfaceview, esos no se adaptarán a mi caso, porque estoy usando OpenGLES.

Así que aquí están mis preguntas:

Cuál es la mejor manera de separar mi lógica juego de la prestación cuando se utiliza OpenGLES? Enhebrando mi aplicación ? Ponga la lógica en un método separado y simplemente llámelo desde el método de sorteo?

Respuesta

10

Así que creo que hay dos maneras en que puede ir aquí.

  1. ¿Todas las actualizaciones de onDrawFrame(): Esto es similar al uso de GLUT, y Android será llamar a esta función tan a menudo como sea posible. (Desactívelo con setRenderMode(RENDERMODE_WHEN_DIRTY).) Esta función recibe un llamado en su propio hilo (no en el hilo de la interfaz de usuario) y significa que llama a su actualización lógica aquí. Tu problema inicial fue que esto parece un poco desordenado, y estoy de acuerdo, pero solo porque la función se llama onDrawFrame(). Si se llamó onUpdateScene(), la actualización de la lógica se ajustaría bien a este modelo. Pero no es lo peor del mundo, fue diseñado de esta manera, y la gente lo hace.

  2. lógica dará su propio hilo: Esto es más complicado ya que ahora está tratando con tres hilos: el hilo de interfaz de usuario para la entrada, el hilo onDrawFrame() rinde por un dibujo cosas, y el hilo de la lógica para trabajar continuamente con ambos entrada y representación de datos. Úselo si necesita tener un modelo realmente preciso de lo que está sucediendo, incluso si su tasa de cuadros está bajando (por ejemplo, respuesta de colisión precisa). Esto puede ser conceptualmente un poco más limpio, pero no es prácticamente más limpio.

Recomendaría # 1. Realmente no necesitas # 2. Si lo hace, puede agregarlo más tarde, supongo, pero la mayoría de la gente simplemente usa la primera opción porque el hardware es lo suficientemente rápido para que no tenga que diseñarlo.

+0

¡Gran respuesta Steve, gracias!Creo que incluso si no me gusta estropear el diseño, seguiré el # 1. Mi juego está activo, pero no hay cálculos pesados ​​sobre él, como la física o una gran cantidad de comprobación de colisión. Solo para estar seguro de mis pensamientos, mi enfoque de enhebrar la pregunta sería correcto si lo elijo (escritura de hilo lógico en un mapa sincronizado y el hilo de renderizado leyéndolo). –

+0

Oh, una cosa más. La gente me dijo que limite mis actualizaciones lógicas a 30 fps, ¿este es el caso también para renderizar? ¿Debería limitar la función completa a esperar 33 ms por cada actualización, o simplemente actualizar la lógica si se ha pasado 33 ms desde la última actualización? –

+0

No creo que tengas que limitarlo, pero quizás no esperes algo mejor que eso en algunos teléfonos. Esto se debe a que la velocidad de fotogramas de algunos de los primeros teléfonos HDPI (creo que en el momento del Droid 1) era limitada, lo que significa que no podían dibujar una pantalla llena de píxeles más de 30 veces por segundo. Tal vez la GPU pueda generar cuadros más rápido que eso, pero la CPU no puede moverlos a la pantalla lo suficientemente rápido. Este no es el caso en los teléfonos más nuevos, creo. –

6

Mantenga su diseño tan simple como sea posible :) El estándar (y tal vez el mejor) enfoque es el siguiente:

public void update(){ 
    while(running){ 
     updateAll(); 
     renderAll(); 
    } 
} 

pagaría la atención en algunos momentos:

  • se necesita llamar a los métodos update y render secuencialmente, evite llamar al update dos veces por vez
  • si prefiere m ultithreading (no lo hago), diseñe sus métodos para que update escriba datos y render solo lea
  • tenga en cuenta que OpenGL tiene su propio "hilo", por lo que cuando llama a la función GL solo envía algún comando a OpenGL (excepto glFlush() - completa todos los comandos)
+0

hmm, como su último tema sugiere, se llama al onDrawFrame de OpenGLrenderer en otro hilo? o simplemente gl.GLxxx() que se envían como solicitudes a otro hilo que sea pertinente para GL? –

+0

@Gtoknu Cuando recibes la llamada 'onDrawFrame()', estás en otro hilo. Vea aquí: http://developer.android.com/reference/android/opengl/GLSurfaceView.Renderer.html –

+0

@SteveBlackwell Oh, gracias, no me he dado cuenta. Pero, ¿cómo podría la respuesta de brigadir ser cierta si no puedo invocar onDrawFrame() ya que otro subproceso la llama automáticamente? esos son confusos. –

Cuestiones relacionadas