Zum Beispiel habe ich eine Spielklasse int
, in der das Leben des Spielers aufgezeichnet wird. Ich habe eine Bedingung
if ( mLives < 1 ) {
// Do some work.
}
Diese Bedingung wird jedoch weiterhin ausgelöst und die Arbeit wird wiederholt ausgeführt. Zum Beispiel möchte ich einen Timer einstellen, um das Spiel in 5 Sekunden zu beenden. Gegenwärtig wird es in jedem Frame auf 5 Sekunden eingestellt und das Spiel endet nie.
Dies ist nur ein Beispiel und ich habe das gleiche Problem in mehreren Bereichen meines Spiels. Ich möchte nach einer Bedingung suchen und dann einmal und nur einmal etwas tun und dann den Code in der if-Anweisung nicht erneut überprüfen oder ausführen. Einige mögliche Lösungen, die in den Sinn kommen, sind ein bool
für jede Bedingung und das Festlegen, bool
wann die Bedingung ausgelöst wird. In der Praxis wird dies jedoch sehr unübersichtlich bools
, da sie als Klassenfelder oder statisch innerhalb der Methode selbst gespeichert werden müssen.
Was ist die richtige Lösung dafür (nicht für mein Beispiel, sondern für die Problemdomäne)? Wie würdest du das in einem Spiel machen?
Antworten:
Ich denke, dass Sie dieses Problem einfach lösen können, indem Sie eine genauere Kontrolle über Ihre möglichen Codepfade ausüben. Wenn Sie beispielsweise überprüfen, ob die Anzahl der Leben des Spielers unter eins gesunken ist, prüfen Sie dann nur, wenn der Spieler ein Leben verliert, und nicht jedes Bild.
Dies setzt wiederum voraus, dass
subtractPlayerLife
dies nur zu bestimmten Anlässen aufgerufen wird, was möglicherweise auf Bedingungen zurückzuführen ist, die Sie für jeden Frame überprüfen müssen (möglicherweise Kollisionen).Durch sorgfältige Kontrolle der Codeausführung können Sie unordentliche Lösungen wie statische Boolesche Werte vermeiden und außerdem Stück für Stück die Ausführung von Code in einem einzelnen Frame reduzieren.
Wenn es etwas gibt, das sich einfach nicht umgestalten lässt und das Sie wirklich nur einmal überprüfen müssen, sind state (wie ein
enum
) oder statische Boolesche Werte der richtige Weg. Um zu vermeiden, dass Sie die Statik selbst deklarieren, können Sie den folgenden Trick anwenden:das würde den folgenden Code machen:
drucken
something
undsomething else
nur einmal. Es liefert nicht den am besten aussehenden Code, aber es funktioniert. Ich bin mir auch ziemlich sicher, dass es Möglichkeiten gibt, dies zu verbessern, indem Sie möglicherweise eindeutige Variablennamen sicherstellen. Dies#define
funktioniert jedoch nur einmal zur Laufzeit. Es gibt keine Möglichkeit, es zurückzusetzen, und es gibt keine Möglichkeit, es zu speichern.Trotz des Tricks empfehle ich dringend, zuerst den Code-Fluss besser zu steuern und dies
#define
als letzten Ausweg oder nur für Debug-Zwecke zu verwenden.quelle
Dies klingt wie der klassische Fall der Verwendung des Statusmusters. In einem Zustand überprüfen Sie Ihren Zustand auf Leben <1. Wenn dieser Zustand eintritt, wechseln Sie in einen Verzögerungszustand. Dieser Verzögerungsstatus wartet auf die angegebene Dauer und wechselt dann in den Beendigungsstatus.
Im einfachsten Ansatz:
Sie können die Verwendung einer State-Klasse zusammen mit einem StateManager / StateMachine in Betracht ziehen, um das Ändern / Verschieben / den Übergang zwischen Zuständen anstelle einer switch-Anweisung zu handhaben.
Sie können Ihre Zustandsverwaltungslösung so komplex gestalten, wie Sie möchten, einschließlich mehrerer aktiver Zustände, eines einzelnen aktiven übergeordneten Zustands mit vielen aktiven untergeordneten Zuständen unter Verwendung einer Hierarchie usw. Verwenden Sie das, was für Sie jetzt Sinn macht, aber ich denke wirklich a state pattern solution macht Ihren Code viel wiederverwendbarer und einfacher zu warten und zu befolgen.
quelle
Wenn Sie sich Sorgen über die Leistung machen, ist die Leistung der mehrmaligen Überprüfung normalerweise nicht signifikant. Das Gleiche gilt für boole Bedingungen.
Wenn Sie an etwas anderem interessiert sind, können Sie etwas verwenden, das dem Null-Entwurfsmuster ähnelt, um dieses Problem zu lösen.
Nehmen wir in diesem Fall an, Sie möchten eine Methode aufrufen,
foo
wenn der Spieler keine Leben mehr hat. Sie würden dies als zwei Klassen implementieren, eine, die aufruftfoo
, und eine, die nichts tut. Nennen wir sieDoNothing
undCallFoo
wie folgt:Dies ist ein bisschen ein "rohes" Beispiel; Die wichtigsten Punkte sind:
FooClass
derDoNothing
oder sein kannCallFoo
. Dies kann eine Basisklasse, eine abstrakte Klasse oder eine Schnittstelle sein.execute
Methode dieser Klasse auf.DoNothing
Instanz).CallFoo
Instanz).Dies ist im Wesentlichen wie eine Mischung aus Strategie- und Nullmustern.
quelle
if
Scheck in dieFooClass
Instanz selbst zu verschieben, damit er nur einmal ausgeführt wird, und das Gleiche gilt für die InstanziierungCallFoo
.