Problemas de subprocesos de Android envolviendo mi cabeza alrededor del diseño

9

Tengo problemas para entender el diseño del juego. En la plataforma Android, tengo una actividad y configuro su vista de contenido con una vista de superficie personalizada. La vista de superficie personalizada actúa como mi panel y creo instancias de todas las clases y hago todo el dibujo y el cálculo allí.

Pregunta: ¿Debería crear las instancias de otras clases en mi actividad?

Ahora creo una clase de hilo personalizada que maneja el ciclo del juego.

Pregunta: ¿Cómo uso esta clase en todas mis actividades? ¿O tengo que crear una instancia separada de la clase de hilo extendida cada vez?

En mi juego anterior, tenía varios niveles que tenían que crear una instancia de la clase de subproceso y en la clase de subproceso tuve que establecer métodos de constructor para cada nivel separado y en el bucle usar una instrucción de cambio para verificar qué nivel necesita representar y actualizar. Lo siento si eso suena confuso.

Solo quiero saber si el método que estoy usando es ineficiente (que probablemente lo sea) y cómo diseñarlo de la manera correcta. He leído muchos tutoriales y todavía tengo muchos problemas con este tema en particular. ¿Quizás un enlace a algunos tutoriales que explican esto? Gracias.

semajhan
fuente

Respuestas:

13

Recomiendo encarecidamente que tenga un hilo de renderizado (usar Canvas/ OpenGL ES, Canvasprobablemente sea un poco más fácil de configurar) y un hilo de juego donde coloque la lógica del juego.

Para realmente "cargar" el juego, puede crear una clase GameEngine y convertirlo en el punto central de su aplicación. Cuando su renderizador esté listo, puede crear una devolución de llamada a la instancia de GameEngine que creará e iniciará dos subprocesos utilizando un Runnablepara el renderizado y otro Runnablepara la lógica del juego.

Código de muestra:

Inicio de la aplicación

private GameEngine engine;
private CanvasRenderer renderer;

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   // Create instances of your two Runnable classes and pass that into
   // the GameEngine constructor.
   // Create an instance of the game engine.
   engine = new GameEngine(canvasRunnable, gamelogicRunnable);
   renderer = new CanvasRenderer(this, engine); 
   setContentView(renderer); 
}

CanvasRenderer

private GameEngine engine;    

// Save your instance from the GameEngine reference in your constrcutor and make
// a global initializion for your GameEngine instance.  

@Override
public void surfaceCreated(SurfaceHolder holder) {  
   // One time setup here.
   // When your view is ready, make this callback to the 
   // GameEngine.
   engine.surfaceIsReady();
}

Motor de juegos

private Thread canvasThread;
private CanvasRunnable canvasRunnable;
// You should be able to figure out how to create a second thread
// where you should put your game logic. :)

// Constructor stuff like creating instances of your threads
// and passing references as you wish to those.
// Don't start the threads here.
// Remember to set references from your Runnable's into your Thread's 
// instances here!

/**
 * Callback. Now your renderer is ready and you
 * can start your threads.
 */
public void surfaceIsReady() {
   thread.setName("Canvas");
   thread.start();
   // Same for game logic.
}
Wroclai
fuente
Wow gracias. Me gustó cómo lo explicaste. Esa única explicación me ilumina todo el concepto.
semajhan
@semajhan: solo pregunta si tienes más problemas. :)
Esto es lo que tengo en mente: la clase GameEngine que actúa como un "enlace" o "referencia" a todas las demás clases con el panel. Actividad> Panel> GameEngine> todas las demás clases.
semajhan
@semajhan: Exactamente. Sólo para su conocimiento: si usted decide ir con OpenGL ESusted debe saber que el render en OpenGL ESque ya tiene su propio hilo y en ese caso no es necesario crear manualmente y comenzar una nueva Thready Runnablepara ese sistema.
Ignora este comentario.
semajhan
3

Por lo general, su ciclo de juego es autónomo dentro de una sola Actividad.

cuando cambias de actividad, pausas / matas tu bucle de juego. Las actividades separadas deberían corresponder a pausar el juego de todos modos (por ejemplo, porque ha cambiado a una actividad de "enviar correo electrónico a amigos" o "menú principal")

Para niveles adicionales, no deberías crear o eliminar nuevos hilos ... a menos que cambies a una actividad de "nivel completo, cargando el siguiente nivel, espera", y tendrás que reiniciar el "juego principal" "Actividad de todos modos. Pero incluso en ese caso, en realidad no estás haciendo hilos "adicionales", solo estás haciendo un hilo en esa Actividad, y matando / reiniciando / matando / reiniciando secuencialmente ... esa Actividad. cada vez que se completa un nivel.


fuente
3

Si entiendes alemán, este tutorial es muy bueno.

Para el idioma inglés, puedo recomendar este tutorial

Con respecto a la clase de hilo: no sé si es realmente necesario que pueda hacer referencia a todas las clases en su aplicación. En mi juego, lo resolví de esa manera:

La clase que es responsable de dibujar la GUI principal tiene un método de renderizado anulado. En este método, se llama una clase de subproceso que actualiza todos los elementos de la GUI y procesa las entradas del usuario.

El hilo también es responsable de mantener una velocidad de fotogramas constante. Dependiendo del juego que estés desarrollando, esto podría ser importante.

RoflcoptrException
fuente
Ese tutorial en alemán es bueno, pero fue traducido aproximadamente en vai google, por lo que es difícil de entender.
semajhan