Muchos juegos de Android ni siquiera tienen el alcance suficiente para justificar guardar / cargar u opciones / preferencias, sin importar los personajes personalizados y similares, simplemente porque se juegan de forma manual durante 10 minutos en el tren a casa.
No cometas el error que cometí al embarcarme en algo con un gran alcance para un primer juego de Android. Haz un montón de juegos más pequeños y luego elige algo más grande
Detalles de Android sabios:
Estructura:
Recomendaría tener casi todo el juego en una clase de actividad. Android trabaja con la idea de que cada actividad es como una mini aplicación, semi-independiente de las otras actividades en la aplicación. La aplicación es esencialmente una pila de actividades, con el usuario viendo cuál está en la parte superior.
Cuando saca uno de la parte superior, generalmente se destruye y se volverá a crear la próxima vez que el usuario comience una nueva actividad (intención de actividad de inicio). Esto dificulta el mantenimiento de estados entre actividades y conduce a una arquitectura de actividad única.
Es posible que desee tener una actividad de tipo de pantalla de Inicio con un menú "Nuevo juego / Cargar juego / Opciones" y hacer que sea la actividad de inicio. Sin embargo, terminarás teniendo que tener un menú en el juego también en tu actividad de juego, que tendrá la mayoría de las mismas funciones.
Mi propia aplicación, hice una "MenuActivity" que tiene las funciones que lanzan un nuevo juego, guardar, cargar, cambiar las opciones. Entonces mi HomeActivity lo extiende, al igual que mi GameActivity.
Como todo está en una clase de Actividad, recomendaría hacer una jerarquía de clases de actividad y usar lotes de alcance privados, protegidos y predeterminados. Al hacerlo de esta manera, al menos puede dividir las cosas en un archivo diferente para evitar tener un archivo de actividad inmanejable. Por ejemplo, para mi propia aplicación:
GraphicsEngine extends MenuActivity
.
PhysicsEngine extends GraphicsEngine
.
GameLogicActivity extends PhysicsEngine
.
UIActivity extends GameLogicActivity
.
Dado que mi aplicación es opengl-es en 3D, muchas de las cosas que hago no funcionan en actividades cruzadas, por lo que todo está en la misma actividad, por lo que tal vez estoy predispuesto a usar una arquitectura de actividad
-
Enhebrar
Para la actividad del juego, tienes dos hilos (o 3 si estás haciendo cosas de 3D opengl). Un hilo es el UI / hilo principal. Este es el hilo en el que la actividad comienza y se ejecuta y que Android te la da.
Este subproceso de interfaz de usuario es el único en el que puede actualizar los elementos de la interfaz de usuario (vistas, diseños, etc.). También es donde se ejecutarán los escuchas para la entrada del usuario. No ve ninguna de las mecánicas del hilo de la interfaz de usuario, solo que se ejecuta en un bucle en segundo plano en algún lugar.
El segundo hilo, lo haces tú mismo (recomendaría usar AsyncTask , a pesar de todas sus deficiencias). Esto hace todas las cosas que no son UI que haces en un ciclo de juego normal, como actualizar el movimiento, calcular colisiones, cálculos de combate, etc.
Haces este hilo / clase AsyncTask como una clase interna de tu actividad. De esa manera, puede tener algunos objetos de alcance de actividad amplia ( Vector<Spaceship>
) a los que se puede acceder tanto con el hilo de la interfaz de usuario como con el hilo del bucle del juego.
Dado que la lógica del juego continúa en el hilo del bucle del juego, ese es el único hilo que necesitará cambiar realmente los valores de las variables (actualizar la velocidad del tanque, reducir el HP del jugador). El subproceso de la interfaz de usuario solo lee valores, por lo que debería haber problemas mínimos de concurrencia.
Lo complicado es conseguir que el hilo de la interfaz de usuario realice actualizaciones a pedido del hilo del bucle del juego. Hay un par de formas de hacer esto. Si lee la documentación de AsyncTask, tiene el método publishingProgress () / onProgressUpdate (). En realidad, esto está agregando cosas a la cola de hilos de la interfaz de usuario para hacer el siguiente ciclo.
Handler hace exactamente lo mismo, donde implementa el método handleMessage () y ese método es realmente ejecutado por el siguiente ciclo del subproceso UI.
Finalmente, cualquier entrada de usuario, al estar en el hilo de la interfaz de usuario, puede actualizar los elementos de la interfaz de usuario inmediatamente (así que dentro de la implementación de cualquier oyente onClick / onTouch). Si necesita actualizar los objetos del juego, puede usar cosas como sincronizar, o puede implementar su propia cola de actualizaciones en AsyncTask que, como el hilo de la interfaz de usuario, pasará la próxima vez que ejecute el bucle del juego
Guía de subprocesos de Android .
-
UI
En cuanto a la estructura de la interfaz de usuario real dentro de la actividad individual, recomiendo tener un diseño de marco como su diseño base. Los elementos secundarios en un diseño de marco funcionan como una cola. El primer elemento se dibuja primero, el segundo se dibuja encima del primero, el tercero encima del segundo.
De esta manera, puede tener múltiples archivos de diseño XML y, al administrar los elementos secundarios del diseño del marco, puede intercambiar fácilmente conjuntos de vistas dentro y fuera.
Si siempre tiene SurfaceView en la parte inferior (primer elemento secundario en el diseño del marco), puede usar todas las vistas / widgets usuales de Android (botones, vistas de texto, vistas de desplazamiento), etc. en la parte superior de la vista de superficie, lo que simplemente hace La parte gráfica del juego.
Entonces, por ejemplo, si algo se está cargando, puede pausar el ciclo del juego / simplemente omitirlo todo, agregar una pantalla opaca de 'carga' como el último elemento secundario en el diseño del marco y el usuario verá 'cargar' en su pantalla , sin darse cuenta de que hay otras opiniones detrás de esto. De esta manera, no tiene que eliminar vistas que tardan mucho tiempo en configurarse o que causan complicaciones cada vez que se agregan / eliminan.
También recomendaría usar los lotes View.setVisibility. Podría, por ejemplo, agregar un diseño completo de 'inventario' a su diseño de marco base y luego simplemente configurar la Visibilidad (Ver.Visible) cuando el usuario haga clic para ver su inventario, y establecer la Visibilidad (Ver. Ir) cuando lo cierren nuevamente. De esa manera, ni siquiera está administrando los elementos secundarios del diseño del marco, solo agrega todo y hace que las cosas sean visibles / invisibles cuando el usuario hace cosas diferentes
Esto ayuda a enhebrar nuevamente; A medida que el usuario hace clic para abrir su inventario, el onCLickListener se maneja en el subproceso de la interfaz de usuario, el inventario se hace visible allí y se llama al método updateInventory, nuevamente desde el subproceso de la interfaz de usuario, con solo getters en todos los objetos del juego que se llaman
Aquí está el diagrama que hice para una pregunta anterior sobre la idea de diseño de actividad / marco único: