Actualmente estoy creando un RPG 2D en C ++ 11 con Allegro 5 y boost.
Mi objetivo es actualizar de alguna manera la configuración de mi juego cuando se cambia una opción en el Menú de opciones. No quiero obligar al usuario a reiniciar mi juego. Otros juegos no requieren reinicio al cambiar la resolución o pasar de pantalla completa a ventana, por lo que mi juego tampoco debería. Vea una vista simplificada del sistema a continuación.
Tenga en cuenta que no necesariamente quiero llamar directamente a mi objeto Juego desde la pantalla Opciones. La línea punteada es simplemente para ilustrar el efecto que estoy tratando de lograr; causar de alguna manera una actualización del juego cuando se cambia una opción en una parte diferente del sistema.
Explicación detallada
ScreenManager contiene una lista de todos los GameScreen
objetos que existen actualmente. Estas serán varias pantallas en el juego, incluidas ventanas emergentes. Este diseño se adhiere más o menos a la muestra de Game State Management en C # / XNA .
El ScreenManager
contiene una referencia a mi Game
objeto. El Game
objeto inicializa y modifica la configuración del juego. Si quiero cambiar la resolución, ir a pantalla completa o silenciar el volumen, lo haría en la Game
clase.
Sin embargo, la pantalla OptionsScreen actualmente no puede acceder a la clase Game. Vea el diagrama a continuación:
Un GameScreen puede señalar tres eventos onFinished
, onTransitionStart
y onTransitionEnd
. No hay onOptionsChanged
porque solo una pantalla hace eso. ScreenManager no puede configurar el manejo de eventos para eso porque maneja todas las pantallas como GameScreen
s.
Mi pregunta es, ¿cómo puedo cambiar mi diseño para que un cambio en el menú Opciones no requiera un reinicio, sino que se cambie de inmediato? Preferiblemente solicitaría que mi Game
objeto se actualice una vez que se haga clic en el botón Aplicar.
Respuestas:
Por lo que he visto, el enfoque más fácil es leer un archivo de opciones en el inicio para determinar la configuración de visualización actual; luego, cuando se muestre la pantalla de opciones, cargue todas las opciones actuales de un archivo.
Cuando los cambios se finalizan mediante un botón
apply
ook
, se guardan nuevamente en un archivo. Si algún cambio afecta la pantalla, notifique al usuario que el juego debe reiniciarse para que surta efecto.Cuando se reinicia el juego, la configuración de pantalla (ahora nueva) se vuelve a leer del archivo.
--EDITAR--
Y ... habría ayudado si me hubiera dado cuenta de la última frase. No quieres tener que reiniciar. Hace las cosas un poco más difíciles dependiendo de su implementación y su biblioteca de gráficos de fondo.
IIRC, Allegro tiene una llamada de función que le permite cambiar la configuración de la pantalla sobre la marcha. Todavía no estoy actualizado en Allegro 5, pero sé que podrías hacerlo en 4.
fuente
Esto es lo que hago por mi juego. Tengo 2 funciones separadas para inicializar cosas, 'init' y 'reset'. Init solo se llama una vez al inicio y hace cosas que no dependen de ninguna configuración, como cargar activos principales. Restablecer hace cosas como diseñar la interfaz de usuario en función de la resolución de la pantalla, por lo que se llama cada vez que cambia la configuración.
No estoy familiarizado con Allegro, pero mi respuesta es bastante general, así que espero que te ayude a ti oa cualquier otra persona con un problema similar.
fuente
Sin alterar su arquitectura actual, veo dos formas. Primero, puede almacenar un puntero a la
Game
instancia en laOptionsScreen
clase. En segundo lugar, puede que laGame
clase obtenga la configuración actual en un intervalo determinado, digamos cada segundo.Para adaptarse realmente a la nueva configuración, la
Game
clase debe implementar algún tipo de funciones de reinicio que recupere la configuración actual y se reinicialice en función de ellas.Para una solución limpia, necesita un administrador global de algún tipo, por lo que es más difícil de implementar. Por ejemplo, un sistema de eventos o sistema de mensajería. Es muy útil dejar que las clases se comuniquen sin enlaces tan fuertes como la agregación o la composición, etc.
Con un administrador de eventos global,
OptionsScreen
simplemente podría disparar globalmente un evento redibujado que seGame
haya registrado para escuchar antes.En general, puede implementar una clase de administrador para almacenar eventos y devoluciones de llamada escuchándolos en un mapa hash. Luego puede crear una sola instancia de ese administrador y pasarle punteros a sus componentes. Usar C ++ más nuevo es bastante fácil, ya que puede usarlo
std::unordered_map
como mapa hash ystd::function
almacenar devoluciones de llamada. Existen diferentes enfoques que puede usar como clave. Por ejemplo, puede hacer que el administrador de eventos se base en una cadena que hace que los componentes sean aún más independientes. En ese caso, usaríastd::string
como clave en el mapa hash. Personalmente me gusta esto y definitivamente no es un problema de rendimiento, pero la mayoría de los sistemas de eventos tradicionales funcionan con eventos como clases.fuente
Bueno, este es un caso específico del patrón Observador.
Hay una solución que implica devoluciones de llamada. Esta es la mejor manera de hacer esto si desea un acoplamiento flojo, y creo que también es el más limpio. Esto no implicará ningún administrador global o singletons.
Básicamente, necesitarás tener algún tipo de
SettingsStore
. Ahí guarda la configuración. Cuando cree una nueva pantalla, necesitarán un puntero a la tienda. En el caso deOptionsScreen
que modificará algunos de los valores de configuración en sí. En el caso delGameScreen
solo los leerá. Por lo tanto, en su juego crearía solo una instancia, que se pasará por todas las pantallas que lo requieran.Ahora, esa
SettingsStore
clase tendrá una lista denotifiables
. Son clases que implementan una determinadaISettingChanged
interfaz. La interfaz sería simple y contiene el siguiente método:Luego, en su pantalla implementará la lógica para cada configuración que le interese. A continuación, agregarse a la tienda para ser notificado:
store->notifyOnChange(this);
. Cuando se cambia una configuración, la devolución de llamada se llama con el nombre de la configuración. El nuevo valor de configuración se puede recuperar deSettingsStore
.Ahora, esto se puede aumentar aún más con las siguientes ideas:
SettingsStore
(cadena de constante) para evitar que se copien las cadenas.fuente
Lea su configuración de un archivo en variables. Haga que su Administrador de pantalla rastree si la pantalla de la que acaba de salir era la pantalla Opciones y, si lo fuera, vuelva a cargar su configuración desde las variables. Cuando el usuario salga de su juego, vuelva a escribir la configuración en las variables en el archivo.
fuente