Was ist ein Zustandsautomat?
Was sind Zustandsautomaten und wozu werden sie benötigt?
Computerprogramme führen Code aus. Oft ist es abhängig von Bedingungen, welcher Code ausgeführt wird. Bei komplizierteren Programmen kann die Prüfung von Bedingungen unübersichtlich werden:
Bei PGZ Blaster Version 02 könnten wir zum Beispiel eine Variable spiel_laeuft
haben, die True
oder False
sein kann. Bei einem Tastendruck zum Beispiel müssen wir dann erst mal schauen: Läuft das Spiel? Hat der Spieler noch ein Schiff? Oder wurde der Spieler grad getroffen? Wie lang ist der Treffer her? etc...
Sehr viel einfacher und übersichtlicher wird es, wenn wir folgende vier Zustände definieren...
BEREIT
– Vor Beginn des SpielsSPIEL
– Während des SpielsGETROFFEN
– Nach einem Treffer, wenn noch ein weiteres Schiff vorhanden ist.SPIEL_VORBEI
– Wenn das Spiel zuende ist
.... und dann festlegen, welche Ereignisse den Zustand ändern können:
- Im Zustand
BEREIT
kommen wir durch einen beliebigen Tastendruck in den ZustandSPIEL
. - Im Zustand
SPIEL
kommen wir durch einen Treffer des Schiffs in den ZustandGETROFFEN
, wenn noch ein Schiff vorhanden ist, sonst in den ZustandSPIEL_VORBEI.
- Im Zustand
SPIEL_VORBEI
geht es (wiederum nach 4 Sekunden Pause) in den ZustandBEREIT
.
Zustandsautomaten lassen sich sehr gut zeichen. Im Fall von PGZ Blaster 02 sieht das dann so aus:
Beispiele für Zustandsabfragen und -übergänge aus dem Code des PGZ Blaster 02
Bei Tastendruck im Zustand BEREIT
schaltet das Spiel in den Zustand SPIEL.
Beim Drücken der Leertaste im Zustand SPIEL
feuert das Schiff eine Rakete ab.
def on_key_down():
if spiel.zustand == Zustand.BEREIT:
spiel.zustand = Zustand.SPIEL
return
if keyboard.space and spiel.zustand == Zustand.SPIEL:
spiel.schiff.rakete_abfeuern()
Nur im Zustand SPIEL
werden die Spielobjekte (in spiel.aktualisiere()
) aktualisiert:
def update():
if spiel.zustand == Zustand.SPIEL:
spiel.aktualisiere()
Hier die komplette Funktion draw()
. Hier sind die Kommentare im Code:
def draw():
screen.fill((255, 255, 255))
# Textanzeige im Zustand BEREIT:
if spiel.zustand == Zustand.BEREIT:
textnachricht("PRESS ANY KEY TO START")
# Zeichnen der Spielobjekte im Zustand SPIEL:
if spiel.zustand == Zustand.SPIEL:
for actor in spiel.raketen + spiel.bomben + spiel.ufos:
actor.draw()
spiel.schiff.draw()
# Textanzeige im Zustand GETROFFEN:
if spiel.zustand == Zustand.GETROFFEN:
textnachricht("YOU'VE BEEN HIT")
# Textanzeige im Zustand SPIEL_VORBEI:
if spiel.zustand == Zustand.SPIEL_VORBEI:
textnachricht("GAME OVER")
# Punktestand und Anzahl Schiffe nur anzeigen, wenn der Zunstand nicht BEREIT ist:
if spiel.zustand != Zustand.BEREIT:
screen.draw.text("Ships: " + str(spiel.schiff.leben),
(20, H-30),
color="black")
screen.draw.text("Score: " + str(spiel.schiff.punkte),
(W-120, H-30),
color="black")
Ein einfacheres Beispiel für einen Zustandsautomaten
from enum import Enum
class Zustand(Enum):
HUNGRIG = 0
SATT = 1
zustand = Zustand.HUNGRIG
def essen_anbieten():
global zustand
if zustand == Zustand.HUNGRIG:
print("Danke, ich esse gerne eine Portion!")
zustand = Zustand.SATT
return
if zustand == Zustand.SATT:
print("Nein danke, ich habe keinen Hunger!")
return
def sport():
global zustand
if zustand == Zustand.HUNGRIG:
print("Mit leerem Magen mache ich lieber keinen Sport!")
return
if zustand == Zustand.SATT:
print("Ich trainiere jetzt eine Runde!")
zustand = Zustand.HUNGRIG
return