Come posso aggiornare le impostazioni di visualizzazione da una schermata Opzioni senza riavviare?


9

Attualmente sto creando un gioco di ruolo 2D in C ++ 11 con Allegro 5 e boost.

Il mio obiettivo è in qualche modo aggiornare le mie impostazioni di gioco quando un'opzione viene modificata nel menu Opzioni. Non voglio forzare l'utente a riavviare il gioco. Altri giochi non richiedono un riavvio quando si cambia risoluzione o si passa da schermo intero a finestre, quindi neanche il mio gioco. Si prega di vedere una vista semplificata del sistema di seguito.

Nota che non voglio necessariamente chiamare direttamente il mio oggetto di gioco dalla schermata Opzioni. La linea tratteggiata serve semplicemente a illustrare l'effetto che sto cercando di ottenere; causare in qualche modo un aggiornamento del gioco quando un'opzione viene cambiata in una parte diversa del sistema.

Spiegazione dettagliata

ScreenManager contiene un elenco di tutti gli GameScreenoggetti attualmente esistenti. Queste saranno varie schermate del gioco inclusi i popup. Questo design aderisce più o meno all'esempio di gestione dello stato di gioco in C # / XNA .

Il ScreenManagercontiene un riferimento al mio Gameoggetto. L' Gameoggetto inizializza e modifica le impostazioni del gioco. Se voglio cambiare la risoluzione, passare a schermo intero o disattivare il volume, lo farei in Gameclasse.

Tuttavia, OptionsScreen al momento non può accedere alla classe di gioco. Vedi lo schema seguente:

Un GameScreen può segnalare tre eventi, onFinished, onTransitionStarte onTransitionEnd. Non c'è onOptionsChangedperché solo una schermata lo fa. ScreenManager non può impostare la gestione degli eventi per questo perché gestisce tutte le schermate come GameScreen.

La mia domanda è: come posso cambiare il mio design in modo che una modifica nel menu Opzioni non richieda un riavvio, ma venga cambiata immediatamente? Preferirei che il mio Gameoggetto si aggiornasse dopo aver fatto clic sul pulsante Applica.


Questa domanda (anche se è relativa al gioco) sembra una soluzione perfetta per programmers.stackexchange.com perché riguarda più il design OO generale che il gioco reale
bughi

Dove hai ottenuto grafici così fantastici?
joltmode

@psycketom: il primo è di Visio 2013 e il secondo è generato dal codice da VS2012. Spero che aiuti!
IAE

Risposte:


1

Da quello che ho visto, l'approccio più semplice è leggere un file di opzioni all'avvio per determinare le impostazioni di visualizzazione correnti; quindi, quando viene visualizzata la schermata delle opzioni, carica tutte le opzioni correnti da un file.

Quando le modifiche vengono finalizzate tramite un pulsante applyo ok, vengono salvate in un file. Se qualche modifica ha effetto sul display, avvisa l'utente che il gioco deve essere riavviato affinché abbiano effetto.

Al riavvio del gioco, le impostazioni di visualizzazione (ora nuove) vengono nuovamente lette dal file.

--MODIFICARE--

Annd ... sarebbe stato d'aiuto se avessi notato l'ultima frase. Non è necessario riavviare. Rende le cose un po 'più difficili a seconda dell'implementazione e della libreria grafica di back-end.

IIRC, Allegro ha una chiamata di funzione che consente di modificare al volo le impostazioni di visualizzazione. Non sono ancora aggiornato su Allegro 5, ma so che potresti farlo in 4.


Non penso che il problema sia legato ad Allegro e alle sue capacità. "La mia domanda è: come posso cambiare il mio design in modo tale che una modifica nel menu Opzioni non richieda un riavvio, ma venga cambiata immediatamente?" Il riavvio è dovuto al fatto che "OptionsScreen al momento non è in grado di accedere alla classe di gioco" e deve caricarli dal file di impostazione se ho capito bene.
ClassicThunder

@Casey: Ciao Casey! :) Sì, sto usando un file di configurazione per memorizzare i dati di visualizzazione pertinenti, ma voglio essere in grado di evitare il riavvio. È un gioco piccolo e altri giochi possono cambiare la risoluzione senza dover riavviare, quindi non vedo perché dovrei costringere l'utente a riavviare con il mio. È un problema di usabilità. Mentre potrei semplicemente chiamare le funzioni di dispay allegro, sto cercando di rimanere OOP e non mescolare le mie chiamate grafiche solo perché posso; Non considero quel buon design.
IAE

Ho evidenziato il bit "senza riavvio" in modo che altri non inciampino nella stessa ultima frase. Un fatto così importante non sarebbe dovuto arrivare alla fine, mi scuso):
IAE

@SoulBeaver Chi dice che devi farlo in modo da non riavviare? È una cosa praticabile richiederlo, infatti sta diventando più comune oggi. Alcune versioni recenti (XCOM: Enemy Unknown, Skyrim, ecc.) Di giochi a budget elevato richiedono un riavvio. (il fatto che siano su Steam potrebbe essere il colpevole a causa della sua sincronizzazione lato server delle opzioni ma non andiamo lì ...)
Casey

1
@Casey: Vero, e per alcune opzioni posso vedere perché questo è persino necessario, ma per cose come a schermo intero / finestre o la risoluzione dello schermo? Non ho mai visto un gioco che richiedesse un riavvio per quelle impostazioni. La mia domanda di base è: perché dovrei costringere il mio utente a riavviare quando il gioco può cambiare automaticamente le impostazioni?
IAE

1

Questo è quello che faccio per il mio gioco. Ho 2 funzioni separate per l'inizializzazione delle cose, 'init' e 'reset'. Init viene chiamato solo una volta all'avvio e fa cose che non si basano su alcuna impostazione, come il caricamento delle risorse principali. Il ripristino fa cose come il layout dell'interfaccia utente in base alla risoluzione dello schermo, quindi viene chiamato ogni volta che cambiano le impostazioni.

init();
bool quit = false;
while( !quit )
{
    createWindow();
    reset();

    bool settings_changed = false;
    while( !quit && !settings_changed )
    {
        ... main loop
        // set quit=true if user clicks 'quit' in main menu
        // set settings_changed=true if user clicks 'apply' in options
    }

    destroyCurrentWindow();
}

Non ho familiarità con Allegro, ma la mia risposta è abbastanza generale, quindi spero che aiuti te o chiunque altro con un problema simile.


Questa è una soluzione interessante. Stavo cercando di avere un controllo rigoroso ogni volta che viene chiamato un reset, ma lo fai indipendentemente da una modifica. È sicuramente qualcosa che potrei implementare e lo esaminerò, grazie!
IAE

1

Senza incasinare la tua architettura attuale, vedo due modi. Innanzitutto, è possibile memorizzare un puntatore Gameall'istanza nella OptionsScreenclasse. In secondo luogo, è possibile che la Gameclasse recuperi le impostazioni correnti in un determinato intervallo, ad esempio ogni secondo.

Per adattarsi effettivamente alle nuove impostazioni, la Gameclasse deve implementare una sorta di funzioni di ripristino che recupera le impostazioni correnti e reinizializza in base a quelle.

Per una soluzione pulita hai bisogno di un gestore globale di qualche tipo, quindi è più difficile implementarlo. Ad esempio un sistema di eventi o un sistema di messaggistica. È molto utile consentire alle classi di comunicare senza vincoli così forti come aggregazione o composizione e così via.

Con un gestore di eventi globale, OptionsScreenpotrebbe semplicemente lanciare a livello globale un evento di ridisegno che Gameè già stato registrato per l'ascolto.

Generalmente è possibile implementare una classe manager che memorizza eventi e callback ascoltandoli in una mappa hash. Quindi è possibile creare una singola istanza di quel gestore e passarvi i puntatori ai componenti. L'utilizzo del C ++ più recente è abbastanza semplice poiché è possibile utilizzare std::unordered_mapcome hash map e std::functionarchiviare i callback. Esistono diversi approcci che puoi usare come chiave. Ad esempio, è possibile rendere basata sulla stringa del gestore eventi che rende i componenti ancora più indipendenti. In tal caso, useresti std::stringcome chiave nella mappa hash. Personalmente mi piace questo e sicuramente non è un problema di prestazioni, ma la maggior parte dei sistemi di eventi tradizionali funziona con eventi come classi.


Ciao danijar. Sto già usando boost :: segnali per impostare uno schema di gestione degli eventi rudimentale. La linea forte dal GameScreen allo ScreenManager è in realtà quella gestione degli eventi. Hai delle risorse che descrivono in dettaglio l'architettura di un gestore di eventi globale? Più lavoro o meno, sembra che potrebbe portare a un'implementazione pulita.
IAE

Sebbene non abbia letto alcuna ricerca in merito, ho implementato un gestore di eventi per eventi globali per il mio piccolo motore. Se sei interessato all'implementazione, ti invierò un link. Ho aggiornato la mia risposta per fornire maggiori dettagli.
danijar,

1

Bene, questo è un caso specifico del modello Observer.

Esiste una soluzione che prevede callback. Questo è il modo migliore per farlo se si desidera un accoppiamento lento e penso che sia anche il più pulito. Ciò non coinvolgerà alcun manager globale o singoli.

Fondamentalmente, dovrai avere una sorta di SettingsStore. Lì memorizzi le impostazioni. Quando crei una nuova schermata, avranno bisogno di un puntatore al negozio. In questo caso OptionsScreenmodificherà alcuni dei valori delle impostazioni stesse. Nel caso in GameScreencui li leggerà e basta. Quindi, nel tuo gioco creeresti solo un'istanza, che verrà passata lungo tutte le schermate che ne richiedono una.

Ora, quella SettingsStoreclasse avrà un elenco di notifiables. Sono classi che implementano una certa ISettingChangedinterfaccia. L'interfaccia sarebbe semplice e contiene il seguente metodo:

void settingChanged(std::string setting);

Quindi nella tua schermata implementerai la logica per ogni impostazione che ti interessa. Quindi, aggiungere se stessi al negozio da notificare: store->notifyOnChange(this);. Quando si modifica un'impostazione, il callback viene chiamato con il nome dell'impostazione. Il nuovo valore di impostazione può quindi essere recuperato da SettingsStore.

Ora, questo può essere ulteriormente aumentato con le seguenti idee:

  • Utilizzare una classe che memorizza le stringhe delle impostazioni - potrebbe essere anche la SettingsStore(stringhe const) al fine di impedire il copia-incolla delle stringhe in giro.
  • Ancora meglio, invece di una stringa, usa un enum per specificare quali impostazioni hai
  • È anche possibile specificare i valori vecchi e nuovi come argomenti nel callback, ma sarebbe più contorto, dato che si possono avere valori stringa o int o quant'altro. Tocca a voi.

0

Leggi le tue impostazioni da un file in variabili. Tieni traccia del tuo Screen Manager se la schermata da cui era appena arrivata era la schermata Opzioni e, se lo fosse, ricarica le tue impostazioni dalle variabili. Quando l'utente esce dal gioco, riscrivi le impostazioni nelle variabili nel file.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.