Ich für meinen Teil bin da ein großer Fan des Event-Systems, das mir u.a. mal von Blackjack hier im Forum gezeigt wurde^^. Vorneweg muss ich aber immer dringend erwähnen: Das ist eine Methode, die sich
für mich bewährt hat. Ich habe keine Ahnung, wie "pythonisch" oder überhaupt sauber meine Methode ist, oder ob ich mir ggf. auch einfach unnötig viel Arbeit aufhalse^^.
Das Grundprinzip läuft bei mir eigentlich immer so ab: Eine "Spiel"-Klasse kümmert sich um die Verwaltung und die Logikteile. Entsprechende Methoden ändern Spielelemente und - ganz wichtig - werfen dann auch Events ab, wenn sich etwas ändert.
Dann programmiere ich eine primitive CMD-/Kommandozeilen-Oberfläche, in der ich alle möglichen Logikteile auf Herz und Nieren teste. Wobei "programmieren" nicht das richtige Wort dafür ist; das schludere ich mehr oder weniger so hin. Einfach nur, weil dieser Quelltext-Teil später nie produktiv genutzt wird; Hauptsache ist, dass ich in besagter CMD-Oberfläche dann das eigentliche "Spiel"-Objekt erstelle und deren Events dann an eigene Methoden binde, um die GUI dann entsprechend anzupassen.
Passt alles, schmeiße ich die CMD-Oberfläche raus und implementiere alles in einer passenderen Oberfläche (je nachdem welches GUI-Toolkit ich dann haben will, z.B. Tkinter oder Kivy). Der Punkt ist: Es hat den Vorteil, dass man auftretende Fehler dann (in der CMD-Oberfläche) darauf abklopfen kann, ob es sich um einen Logik- oder um einen Darstellungsbug handelt. Ich für meinen Teil empfinde es als große Erleichterung (gerade bei halbwegs komplexen GUIs, die es in Spielen fast immer gibt), Logikbugs dank der CMD-Oberfläche komplett ausschließen zu können.
Ich habe hier ein Programmierbuch über Spieleprogrammierung rumliegen, dass das Ganze in einer (für mich) sehr heftigen Art und Weise durchzieht; dort werden Einzelklassen von z.B. der Spiellogik selbst, Input, Grafikdarstellung, Sound und sogar das verwendete GUI-Toolkit einzeln gekapselt und dann in einer "Manager"-ähnlichen Klasse zusammengestöpselt. Das Eventsystem verwendet der Autor in einer Singleton-Klasse und ist auch recht umfangreich aufgebaut, z.B. mit der Möglichkeit, Events mitzuteilen, dass bei Änderungen an anderen Events wieder neue Events spawnen sollen. Für kleinere Projekte ist das natürlich kompletter Overkill... Aber die Auseinandersetzung mit dem Thema hilft, das Prinzip dahinter zu verstehen und plötzlich kommen einem selbst "große" und komplexe Programme/Spiele garnicht mehr so "magisch" vor. (TL;DR, ich weiß^^)
---
Als (nur exemplarisches) Beispiel: Die Event-Klasse:
Code: Alles auswählen
class Event(object):
def __init__(self):
self.listener = list()
def register(self, listener):
self.listener.append(listener)
def unregister(self, listener):
if listener in self.listener:
self.listener.remove(listener)
def notify(self, *args, **kwargs):
for listener in self.listener:
listener(*args, **kwargs)
Jetzt die Klasse, die für die Spiellogik verantwortlich ist. Das Spiel selbst ist simpel: Der Spieler muss einen Zähler einfach auf den Wert 3 bringen, dann hat er gewonnen:
Code: Alles auswählen
class Game(object):
def __init__(self):
# Events bereitstellen
self.event_raise = Event() # Wenn Variable erhoeht wurde
self.event_playerwin = Event() # Wenn das Spiel gewonnen wurde
# Spielvariable: Ziel ist es, diese auf 2 zu bringen
self.gamevalue = 0
def player_move(self):
self.gamevalue += 1
self.event_raise.notify(self.gamevalue)
if self.gamevalue == 3:
self.event_playerwin.notify()
Faustregel (auch wieder von Blackjack übernommen): Die Spiellogik muss garnicht wissen, was hinterher mit dem Spiel gemacht wird. Es meldet einfach nur brav, ob sich interne Daten in der Spiellogik verändert haben und schickt ggf. noch mit, welche neuen Daten das sind (etwa bei "self.event_raise.notify").
In diesem Beispiel habe ich die "Spiellogik" (hier: Bei Zahl 3 gewinnt der Spieler) direkt in die Klasse eingebaut. Hier gäbs natürlich noch viele andere Möglichkeiten das zu implementieren (z.B. auch in einer anderen Klasse)... Damit das Ganze nicht ausartet (Stichwort
Overengineering), lass ich das mal direkt in der Methode so stehen^^.
Jetzt die Klasse, die für die "Oberfläche" (hier: CMD-Fenster) verantwortlich ist:
Code: Alles auswählen
class CmdViewer(object):
def __init__(self):
# Spiel-Objekt erstellen
self.game = Game()
# Events des Spiel-Objekts an eigene Methoden binden:
self.game.event_raise.register(self.MDL_raise)
self.game.event_playerwin.register(self.MDL_playerwin)
def run(self):
# Jetzt in einer Dauerschleife den Spieler um Input bitten:
while True:
userinp = input("Soll die Zahl erhoeht werden? [jn]")
# Benutzer will Zahl erhoehen? Dann dem Modell melden:
if userinp == "j" or userinp == "J":
self.game.player_move()
else:
exit()
def MDL_raise(self, value):
"""Modell meldet, die Zahl wurde erhoeht."""
print("Die Zahl wurde erhoeht und hat jetzt den Wert {}".format(value))
def MDL_playerwin(self):
"""Modell meldet, der Spieler hat gewonnen."""
print("GLUECKWUNSCH! Du hast das schwerste Spiel ever gewonnen! ;)")
if __name__ == "__main__":
CmdViewer().run()
(Python 2-Nutzer müssen "input" durch "raw_input" ersetzen). Die Kommentare sollten eigentlich alles erklären^^.
Das Ganze müsste für ein Kartenspiel natürlich noch passend und umfangreicher gestaltet werden, aber das Grundprinzip sollte man, denke ich, verstanden haben^^. Auf dieser Grundlage habe ich immerhin Sokoban und Minesweeper schreiben können^^.
Hoffe, ich konnte irgendwie helfen. Alternativ: Hoffe, mir ist noch zu helfen *g* , falls ich z.B. einen extrem umständlichen Weg gegangen bin. Das überlasse ich den Profis hier, die mit Python ihren Lebensunterhalt verdienen^^...