Ich erstelle gerade ein 2D-RPG in C ++ 11 mit Allegro 5 und Boost.
Mein Ziel ist es, meine Spieleinstellungen irgendwie zu aktualisieren, wenn eine Option im Optionsmenü geändert wird. Ich möchte den Benutzer nicht zwingen, mein Spiel neu zu starten. Andere Spiele erfordern keinen Neustart, wenn Sie die Auflösung ändern oder vom Vollbildmodus zum Fenster wechseln. Daher sollte mein Spiel dies auch nicht tun. Nachfolgend finden Sie eine vereinfachte Ansicht des Systems.
Bitte beachten Sie, dass ich mein Spielobjekt nicht unbedingt direkt über den Optionsbildschirm aufrufen möchte. Die gestrichelte Linie soll lediglich den Effekt veranschaulichen, den ich erreichen möchte. um irgendwie ein Update des Spiels zu verursachen, wenn eine Option in einem anderen Teil des Systems geändert wird.
Ausführliche Erklärung
Der ScreenManager enthält eine Liste aller GameScreen
aktuell vorhandenen Objekte. Dies sind verschiedene Bildschirme im Spiel, einschließlich Popups. Dieses Design entspricht mehr oder weniger dem Game State Management-Beispiel in C # / XNA .
Das ScreenManager
enthält einen Verweis auf mein Game
Objekt. Das Game
Objekt initialisiert und ändert die Spieleinstellungen. Wenn ich die Auflösung ändern, den Vollbildmodus aktivieren oder die Lautstärke stummschalten möchte, würde ich dies in der Game
Klasse tun .
Der OptionsScreen kann derzeit jedoch nicht auf die Spielklasse zugreifen. Siehe unteres Diagramm:
Ein GameScreen kann drei Ereignisse signalisieren onFinished
, onTransitionStart
und onTransitionEnd
. Es gibt keine, onOptionsChanged
weil das nur ein Bildschirm tut. Der ScreenManager kann die Ereignisbehandlung dafür nicht einrichten, da er alle Bildschirme als GameScreen
s behandelt.
Meine Frage ist, wie kann ich mein Design so ändern, dass eine Änderung im Optionsmenü keinen Neustart erfordert, sondern sofort geändert wird? Ich würde mein Game
Objekt vorzugsweise zur Aktualisierung auffordern, sobald auf die Schaltfläche "Anwenden" geklickt wird.
Antworten:
Nach dem, was ich gesehen habe, ist es am einfachsten, beim Start eine Optionsdatei zu lesen, um die aktuellen Anzeigeeinstellungen zu ermitteln. Laden Sie dann, wenn Ihr Optionsbildschirm angezeigt wird, alle aktuellen Optionen aus einer Datei.
Wenn Änderungen über eine Schaltfläche
apply
oder abgeschlossen werdenok
, werden sie wieder in einer Datei gespeichert. Wenn sich Änderungen auf die Anzeige auswirken, benachrichtigen Sie den Benutzer, dass das Spiel neu gestartet werden muss, damit er wirksam wird.Beim Neustart des Spiels werden die (jetzt neuen) Anzeigeeinstellungen erneut aus der Datei gelesen.
--BEARBEITEN--
Annd ... es hätte geholfen, wenn ich diesen letzten Satz bemerkt hätte. Sie möchten nicht neu starten müssen. Dies erschwert die Arbeit je nach Implementierung und Back-End-Grafikbibliothek etwas.
IIRC, Allegro hat einen Funktionsaufruf, mit dem Sie die Anzeigeeinstellungen im laufenden Betrieb ändern können. Ich bin noch nicht auf Allegro 5, aber ich weiß, dass Sie in 4 könnten.
quelle
Das mache ich für mein Spiel. Ich habe 2 separate Funktionen zum Initialisieren von Sachen, 'init' und 'reset'. Init wird beim Start nur einmal aufgerufen und führt Dinge aus, die nicht auf Einstellungen beruhen, z. B. das Laden von Hauptressourcen. Beim Zurücksetzen wird beispielsweise die Benutzeroberfläche basierend auf der Bildschirmauflösung angeordnet. Sie wird daher jedes Mal aufgerufen, wenn sich die Einstellungen ändern.
Ich bin mit Allegro nicht vertraut, aber meine Antwort ist ziemlich allgemein, also hoffe ich, dass sie Ihnen oder anderen mit einem ähnlichen Problem hilft.
quelle
Ohne Ihre aktuelle Architektur durcheinander zu bringen, sehe ich zwei Möglichkeiten. Zunächst können Sie einen Zeiger auf die
Game
Instanz in derOptionsScreen
Klasse speichern . Zweitens könnte dieGame
Klasse aktuelle Einstellungen in einem bestimmten Intervall abrufen, beispielsweise jede Sekunde.Um sich tatsächlich an die neuen Einstellungen anzupassen, muss die
Game
Klasse eine Art Rücksetzfunktion implementieren, die die aktuellen Einstellungen abruft und basierend auf diesen neu initialisiert.Für eine saubere Lösung benötigen Sie einen globalen Manager. Die Implementierung ist daher aufwändiger. Zum Beispiel ein Ereignissystem oder ein Nachrichtensystem. Es ist sehr nützlich, Klassen ohne so starke Bindungen wie Aggregation oder Komposition usw. kommunizieren zu lassen.
Mit einem globalen Event-Manager kann der
OptionsScreen
einfach ein Redraw-Ereignis global auslösen, dasGame
sich zum Abhören registriert hat.Im Allgemeinen können Sie eine Manager-Klasse implementieren, in der Ereignisse und Rückrufe, die sie abhören, in einer Hash-Map gespeichert werden. Anschließend können Sie eine einzelne Instanz dieses Managers erstellen und Zeiger darauf an Ihre Komponenten übergeben. Die Verwendung von neuerem C ++ ist recht einfach, da Sie es
std::unordered_map
als Hash-Map verwenden undstd::function
Rückrufe speichern können. Es gibt verschiedene Ansätze, die Sie als Schlüssel verwenden können. Sie können beispielsweise die Zeichenfolge des Ereignismanagers festlegen, wodurch Komponenten noch unabhängiger werden. In diesem Fall würden Siestd::string
als Schlüssel in der Hash-Map verwenden. Ich persönlich mag das und es ist definitiv kein Leistungsproblem, aber die meisten traditionellen Ereignissysteme arbeiten mit Ereignissen als Klassen.quelle
Nun, dies ist ein spezieller Fall des Observer-Musters.
Es gibt eine Lösung, die Rückrufe beinhaltet. Dies ist der beste Weg, dies zu tun, wenn Sie eine lose Kupplung wünschen, und ich denke, es ist auch der sauberste. Dies beinhaltet keine globalen Manager oder Singletons.
Grundsätzlich müssen Sie eine Art von haben
SettingsStore
. Dort speichern Sie die Einstellungen. Wenn Sie einen neuen Bildschirm erstellen, benötigen sie einen Zeiger auf das Geschäft. Im Falle desOptionsScreen
wird ein Teil des Einstellungswerts selbst geändert. Im Falle der wirdGameScreen
es nur lesen. In Ihrem Spiel würden Sie also nur eine Instanz erstellen, die an alle Bildschirme weitergeleitet wird, für die eine erforderlich ist.Nun wird diese
SettingsStore
Klasse eine Liste von habennotifiables
. Sie sind Klassen, die eine bestimmteISettingChanged
Schnittstelle implementieren . Die Schnittstelle wäre einfach und enthält die folgende Methode:Anschließend implementieren Sie auf Ihrem Bildschirm die Logik für jede Einstellung, die Sie interessiert. Fügen Sie sich dann dem zu benachrichtigenden Geschäft hinzu :
store->notifyOnChange(this);
. Wenn eine Einstellung geändert wird, wird der Rückruf mit dem Einstellungsnamen aufgerufen. Der neue Einstellwert kann dann aus dem abgerufen werdenSettingsStore
.Dies kann nun durch die folgenden Ideen weiter ergänzt werden:
SettingsStore
(const-Zeichenfolgen), um das Einfügen von Zeichenfolgen durch Kopieren zu verhindern.quelle
Lesen Sie Ihre Einstellungen aus einer Datei in Variablen. Lassen Sie Ihren Bildschirmmanager verfolgen, ob der Bildschirm, von dem er gerade kam, der Bildschirm "Optionen" war, und laden Sie gegebenenfalls Ihre Einstellungen aus den Variablen neu. Wenn der Benutzer Ihr Spiel beendet, schreiben Sie die Einstellungen in den Variablen zurück in die Datei.
quelle