nur ein wenig Code ausgelagert (Modul), jetzt Probleme...

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
Benutzeravatar
Don Polettone
User
Beiträge: 115
Registriert: Dienstag 23. November 2010, 20:26
Wohnort: Schweiz

Hallo da alle,

Ich habe mit Pygame einen kleinen Kartengenerator programmiert. Dieser lädt ein Tileset aus 4 Tiles und baut anhand vo den eingegebenen Wegpunkt-Koordinaten (z.Bsp. karte1 = [(0, 0), (1, 3), etc etc ] eine Strasse. Dieser tut's eigentlich ganz gut so weit, man kann die Tilesets zeichnen und reinladen und auch verschiedene Karten natürlich. Das ganze sieht dann z.Bsp. so aus:

Bild

...knuffig.

Dann habe ich in der Funktion, die die ganze Karte baut, baue_karte(), noch hinzugefügt, dass jedes Mal, wenn ein Tile gebaut wird, Pygame das ganze anzeigen soll (pygame.display.flip()) und dann wollte ich noch eine Pause (pygame.time.wait(500)), damit man dem Generator schön zuschauen kann, wie die Karte gebaut wird. Und das funzte super-prächtig :D

Aber nun habe ich mir langsam gedacht, dass es positiv wäre, etwas Code auslagern zu können, und JETZT habe ich ein echt doofes Problem:

Habe probehalber ein Modul namens tilesets ausgelagert, dass die Grafiken des gewählten Tilesets lädt und ihnen globale Variablen zuweist:

Code: Alles auswählen

# -*- coding: cp1252 -*-

import os
import pygame

def tileset_waehlen(tileset):

    global vertikal
    global horizontal
    global ecke1
    global ecke3
    global ecke7
    global ecke9
    global kreuzung
    global t
    
    tile_pfad = "C:\\users\\Killa\\Game Shizzle Dizzle\\Graphics\\Tiles\\" # Dateiordner

    if tileset == "strasse_61":
        strasse_bild = os.path.join(tile_pfad, "strasse_61.png")
        strasse_ecke_bild = os.path.join(tile_pfad, "strasse_61_ecke.png")
        strasse_kreuzung_bild = os.path.join(tile_pfad, "strasse_61_kreuzung.png")
        strasse_t_bild = os.path.join(tile_pfad, "strasse_61_t.png")

    if tileset == "eis_61":
        strasse_bild = os.path.join(tile_pfad, "eis_61.png")
        strasse_ecke_bild = os.path.join(tile_pfad, "eis_61_ecke.png")
        strasse_kreuzung_bild = os.path.join(tile_pfad, "eis_61_kreuzung.png")
        strasse_t_bild = os.path.join(tile_pfad, "eis_61_t.png")

    if tileset == "jungle_56":
        strasse_bild = os.path.join(tile_pfad, "jungle_56.png")
        strasse_ecke_bild = os.path.join(tile_pfad, "jungle_56_ecke.png")
        strasse_kreuzung_bild = os.path.join(tile_pfad, "jungle_56_kreuzung.png")
        strasse_t_bild = os.path.join(tile_pfad, "jungle_56_t.png")

    if tileset == "strasse_48":
        strasse_bild = os.path.join(tile_pfad, "strasse_48.png")
        strasse_ecke_bild = os.path.join(tile_pfad, "strasse_48_ecke.png")
        strasse_kreuzung_bild = os.path.join(tile_pfad, "strasse_48_kreuzung.png")
        strasse_t_bild = os.path.join(tile_pfad, "strasse_48_t.png")
        
    if tileset == "schnee_48":
        strasse_bild = os.path.join(tile_pfad, "schnee_48.png")
        strasse_ecke_bild = os.path.join(tile_pfad, "schnee_48_ecke.png")
        strasse_kreuzung_bild = os.path.join(tile_pfad, "schnee_48_kreuzung.png")
        strasse_t_bild = os.path.join(tile_pfad, "schnee_48_t.png")

    if tileset == "jungle_48":
        strasse_bild = os.path.join(tile_pfad, "jungle_48.png")
        strasse_ecke_bild = os.path.join(tile_pfad, "jungle_48_ecke.png")
        strasse_kreuzung_bild = os.path.join(tile_pfad, "jungle_48_kreuzung.png")
        strasse_t_bild = os.path.join(tile_pfad, "jungle_48_t.png")
        
    if tileset == "test_48":
        strasse_bild = os.path.join(tile_pfad, "test_48.png")
        strasse_ecke_bild = os.path.join(tile_pfad, "test_48_ecke.png")
        strasse_kreuzung_bild = os.path.join(tile_pfad, "test_48_kreuzung.png")
        strasse_t_bild = os.path.join(tile_pfad, "test_48_t.png")

    if tileset == "felsweg_40":
        strasse_bild = os.path.join(tile_pfad, "felsweg_40.png")
        strasse_ecke_bild = os.path.join(tile_pfad, "felsweg_40_ecke.png")
        strasse_kreuzung_bild = os.path.join(tile_pfad, "felsweg_40_kreuzung.png")
        strasse_t_bild = os.path.join(tile_pfad, "felsweg_40_t.png")

    vertikal = pygame.image.load(strasse_bild).convert()
    horizontal = pygame.transform.rotate(vertikal, -90)
    ecke1 = pygame.image.load(strasse_ecke_bild).convert()
    ecke3 = pygame.transform.rotate(ecke1, 90)
    ecke7 = pygame.transform.rotate(ecke1, -90)
    ecke9 = pygame.transform.rotate(ecke1, 180)
    kreuzung = pygame.image.load(strasse_kreuzung_bild).convert()
    t = pygame.image.load(strasse_t_bild).convert()
Ganz zuerst in meinem Script wird dieses Modul nur importiert (import tilesets). Dann führe ich die Funktion im Modul aus und gebe den Namen des gewünschten Tilesets als Argument mit:

tilesets.tileset_waehlen("strasse_48")

Erst jetzt lade ich die im Modul definierten globalen Variablen ins Script:

from tilesets import *

Das wäre dann eigentlich alles. Ich habe also im Progi nicht viel geändert, nur etwas Code ausgelagert. Und jetzt der Hammer: Der Map-Generator tut's zwar noch, aber leidet unter Performanceproblemen... Die Strasse zeichnet sich ein Stück weit, dann (nach 7, 8 Tiles oder so, zeigt der Cursor keine Rückmeldung an und das Programm ist weg, solange, bis es alle Tiles gebaut hat. Dann ploppt die ganze Strasse auf und das Progi ist wieder stabil... Was ich damit sagen - oder besser fragen - will, ist: Warum macht das erst jetzt Probleme, wo ich mit diesem Modul arbeite? Zuvor hat das gegeigt...

Kann mir jemand helfen? Wäre echt toll; ich schnall's echt nicht mehr.

Danke bestens,


Henry Jones Jr.
Ich code, also bin ich.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Werde mal alle ``global`` los, dann wirst du gezwungen sein, dir Gedanken über die Struktur deines Programms zu machen :-)
Benutzeravatar
Don Polettone
User
Beiträge: 115
Registriert: Dienstag 23. November 2010, 20:26
Wohnort: Schweiz

sorry, Problem hat sich von selbst gelöst - musste aus irgendeinem Grund den PC neu starten, dann lief's wieder.

Aber danke für die Antwort!

Aber im Ernst: Wie würdest Du denn dieses Modul aufrufen ohne global, wenn da einige globale Variablen definiert werden sollen je nach dem, welches Argument ich mitliefere..?

Hast Du mir einen Tip?

Gruss,


Henry
Ich code, also bin ich.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Henry Jones Jr. hat geschrieben:Aber im Ernst: Wie würdest Du denn dieses Modul aufrufen ohne global, wenn da einige globale Variablen definiert werden sollen je nach dem, welches Argument ich mitliefere..?
Sowas gibt es nicht. Globale Variablen sind entweder Klassen, Funktionen oder Konstanten. Globale Variablen, die ihren Wert ändern, existieren ab sofort in deinem Denkmodell nicht mehr.

Und hiermit ist der erste Schritte in Richtung anständiger Code getan :-)
Benutzeravatar
Don Polettone
User
Beiträge: 115
Registriert: Dienstag 23. November 2010, 20:26
Wohnort: Schweiz

danke für den Tip :-)

Du sagst also, globale Variablen sollten nicht lose in der Syntax rumschwirren, und wenn doch, sollten es Konstanten sein - ist das richtig? Hättest Du vielleicht ein Tutorial oder einen Codefetzen oder etwas in die Richtung, welches sich damit befasst, wie ein Progi aufgebaut sein sollte (sprich: Wo machen Konstanten Sinn, was verpackt man besser in Klassen und Funktionen etc? Sowas würde mir sehr helfen, damit ich weiss, wie mein Kartengenerator aufgebaut sein sollte. Oder hättest Du mir einen Denkanstoss, wo ich anfangen soll, den Generator zu strukturieren, so dass ich keine losen, globalen Variablen mehr habe abgesehen von den Konstanten?

jedenfalls besten Dank für Deine Ungterstützung, weiss diese wirklich zu schätzen.

Gruss,


Henry
Ich code, also bin ich.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Hm, hab jetzt gar nichts gefunden was sich mal ausführlich über die Programmstruktur auslässt...

Grundsätzlich: Strukturiert an das Problem rangehen und dabei die Arbeit in kleine Häppchen aufteilen.

Was soll dein Programm also tun? Zuerst willst du ein Tileset wählen, dessen Tiles dann geladen werden. Das ist schonmal eine Funktion. Danach soll der Weg gezeichnet werden, wobei an jeder Stelle herausgefunden werden muss, welches Tile aus dem Set jetzt angebracht ist. Das macht nochmal mindestens zwei andere Funktionen.

Natürlich musst du zwischen den Funktionen jetzt auch Daten austauschen, z.B. bekommt die Tileset-Lade-Funktion den Namen des Tilesets und gibt das geladene Tileset zurück. Dafür solltest du dir jetzt jeweils Datenstrukturen ausdenken, in denen man diese Daten gut speichern kann. Für diese Funktion böten sich jetzt als Name ein String an und als Rückgabestruktur z.B. ein Dictionary als Tile <-> Pygame-Struktur Mapping an. Vielleicht hast du aber auch eine viel bessere Idee :-)
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

Zusätzlich kannst du noch deine

Code: Alles auswählen

if tileset == "bla"
ersetzen. Ist dir schon aufgefallen, dass alles in diesen Codeblöcken absolut identisch ist bis auf diesen jeweiligen Namen? Das schreit nach String Formatting. Merke: Wiederholung ist böse (TM)!
Benutzeravatar
Don Polettone
User
Beiträge: 115
Registriert: Dienstag 23. November 2010, 20:26
Wohnort: Schweiz

"...Natürlich musst du zwischen den Funktionen jetzt auch Daten austauschen, z.B. bekommt die Tileset-Lade-Funktion den Namen des Tilesets und gibt das geladene Tileset zurück. Dafür solltest du dir jetzt jeweils Datenstrukturen ausdenken, in denen man diese Daten gut speichern kann. ..."

eben dies mit dem Datenaustausch zwischen Funktionen und Modulen etc. macht mir Probleme wie es scheint... wenn ich einer Funktion etwas mitgeben möchte, dann wohl als Argument..? Dann wäre ich ja mal auf dem richtigen Weg gewesen.

Was aber nun, wenn diese Funktion etwas zurückgeben muss und ich dies abfangen möchte? In dem Moment kamen bei mir die globalen Variablen ins Spiel, da viele Funktionen Sachen berechnen, die ich immer wieder brauche in meinem Code... z.Bsp. die Länge eines Tiles aus dem gewählten Tileset, deren Wert dann in einer Konstante "u" gespeichert wird. Dieses "u" kommt danach (fast) überall vor... was soll ich denn da tun? Wäre es besser, wenn meine Funktionen Argumente entgegennehmen und dann einen returnwert zurückgeben, welchen ich dann abfangen kann..? Kann ich dann diese Returnwerte gleich wieder verwenden, ohne diese zuerst in einer globalen Variable zu speichern? Zum Beispiel die Funktion mit den Tilesets (also in meinem Fall mein Modul). Diese nimmt den Namen (String) des aktuellen Tilesets entgegen und "returnt" dann die Grafiken des entsprechenden Tilesets. Um diese dann aber abzufangen, muss ich diesen Grafiken ja aber wieder globale Variablen zuweisen, oder nicht?? *kratz kratz grübel grübel studier*... hmm... irgendwie ist mir das suspekt... kannst Du mir nur noch ein klein wenig auf die Sprünge helfen (kleines Beispiel oder so)? :?

schönen Gruss und vielen Dank,


Henry
Ich code, also bin ich.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Henry Jones Jr. hat geschrieben: Zum Beispiel die Funktion mit den Tilesets (also in meinem Fall mein Modul). Diese nimmt den Namen (String) des aktuellen Tilesets entgegen und "returnt" dann die Grafiken des entsprechenden Tilesets. Um diese dann aber abzufangen, muss ich diesen Grafiken ja aber wieder globale Variablen zuweisen, oder nicht?? *kratz kratz grübel grübel studier*... hmm... irgendwie ist mir das suspekt... kannst Du mir nur noch ein klein wenig auf die Sprünge helfen (kleines Beispiel oder so)?
Du musst gar nichts als globale Variablen speichern. Höchstens als lokale Variablen innerhalb einer Funktion/Klasse. Wenn mehrere Funktionen die gleichen Werte brauchen, dann bastele dir eine "Zentrale", die die ganzen anderen Funktionen aufruft und ggf. die Rückgabewerte als lokale Variable speichert und dann andere Funktionen damit aufruft.

Ganz grober Grundriss:

Code: Alles auswählen

def draw_route(route, tileset):
  for chunk in route:
    tile_name = select_tile_for_chunk(chunk) # ggf brauchst du hierfuer auch noch das vorige/folgende Wegstueck?
    tile = tileset[tile_name]
    draw_tile(tile, ...position...)

def main():
  route = [...]
  tileset = load_tileset('foo')
  draw_route(route, tileset)
Benutzeravatar
Don Polettone
User
Beiträge: 115
Registriert: Dienstag 23. November 2010, 20:26
Wohnort: Schweiz

Hallo nochmals :-)

Wow, das sieht schon recht knackig aus...

habe gerade festgestellt, dass ich so Zeugs eigentlich auch schon ohne globale Variablen umgesetzt habe in meinem Progi, z.Bsp. meine Funktion karte_bauen(), welche sich aller Funktionen bedient, welche etwas bauen können und diese in der richtigen Reihenfolge aufruft:

Code: Alles auswählen

def karte_bauen():
    "Zeichnet die gesamte Karte mit start_und_ziel_bauen(), verbinden() und ecke_bauen(). "

    start_und_ziel_bauen()

    ecken_zu_bauen = len(wegpos) - 2
    ecken_gebaut = 0

    while ecken_gebaut < ecken_zu_bauen:
        ecke_bauen(wegpos[ecken_gebaut], wegpos[ecken_gebaut + 1], wegpos[ecken_gebaut + 2])
        ecken_gebaut += 1

    wegpunkte_zu_verbinden = len(wegpos) - 1
    wegpunkte_verbunden = 0

    while wegpunkte_verbunden < wegpunkte_zu_verbinden:
        verbinden(wegpos[wegpunkte_verbunden], wegpos[wegpunkte_verbunden + 1])
        wegpunkte_verbunden += 1
 
#--------------------------------------------------------
bildschirm.fill(farben.farbe_x)

pygame.display.flip()
pygame.time.delay(1500)

karte_bauen()
dies wäre im Ansatz okay, oder..? Man sollte das ganze nur noch ein wenig schöner verpacken und eben knackiger machen...

Noch eine Frage nebenbei: Was hat es eigentlich auf sich mit diesen globalen Variablen, dass diese so verpönt sind (und eigentlich allem Anschein nach nicht mal exisitieren dürften)? Gibt das PC mehr zu rechnen? Sorry, ich bin nicht vom Fach (wer hätte es gedacht *gröhl*)

es staunt:


Henry
Ich code, also bin ich.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Das sieht doch schonmal gleich viel besser aus!

Du könntest ja mal noch versuchen, diese ``while``-Schleife mit den Zählern in eine For-Schleife ohne Zähler zu verwandeln. Ist aber nicht ganz einfach :-)
Henry Jones Jr. hat geschrieben:Noch eine Frage nebenbei: Was hat es eigentlich auf sich mit diesen globalen Variablen, dass diese so verpönt sind (und eigentlich allem Anschein nach nicht mal exisitieren dürften)? Gibt das PC mehr zu rechnen?
Also Performanz ist da glaub ich das geringste Problem -- es geht eher darum, Spaghetti-Code zu vermeiden. Meiner Meinung nach ist es aber wie "goto" in anderen Sprachen: Ist macht den Code in den meisten Fällen schlechter und in den allerwenigsten besser. In letzteren Fällen ist dann auch voll okay das zu benutzen. In den allermeisten Fällen deutet global (goto) aber darauf hin, dass da jemand Gewurschtelcode schreibt. Deswegen wird dir auch sofort auf die Finger geklopft, wenn du es verwendest. Und deswegen verbanne es aus dem Kopf :-)

Ich finde sehr ungeschickt, dass "global" nicht versteckter ist in der Doku bzw. in Tutorials.
Benutzeravatar
Don Polettone
User
Beiträge: 115
Registriert: Dienstag 23. November 2010, 20:26
Wohnort: Schweiz

Hallo da wieder :D

wegen dem mit dem global verstecken: Das ist ja schon OK, dass das gleich erklärt wird, wissen will ich's allemal wie's geht ;-) Aber mich irritiert eher, dass im Tutorial nicht darauf hingewiesen wird (hab zwar noch nicht alles gelesen :oops: ), dass diese globalen Variablen eher zu vermeiden sind, wenn man sich keinen "henryjonesschen" Hackerstyle aneignen möchte... naja.

jedenfalls hab' ich's mir gemerkt und werde das auf Jeden umsetzen, danke.

...und bzgl. for ... in:

meinst Du so..?

Code: Alles auswählen

ecken_zu_bauen = len(wegpos) - 2
    ecken_gebaut = 0

    for ecke in ecken_zu_bauen:
        ecke_bauen(wegpos[ecken_gebaut], wegpos[ecken_gebaut + 1], wegpos[ecken_gebaut + 2])
haste voll Recht, ganz klar.

sorry ich bin ein Dödel...

Code: Alles auswählen

    ecken_zu_bauen = len(wegpos) - 2
    ecken_gebaut = 0

    for ecke in range(0, ecken_zu_bauen):
        ecke_bauen(wegpos[ecken_gebaut], wegpos[ecken_gebaut + 1], wegpos[ecken_gebaut + 2])
        ecken_gebaut += 1
...das tut's besser :-)

Ich habe noch oft das Problem, dass ich die Syntax (jedenfalls die Basics der Basics (der Basics)) kennen würde; aber trotzdem nicht sehe, dass die eine Variante in einer bestimmten Situation der anderen vorzuziehen wäre... aber da ist Übung angesagt, ist klar.

Angefangen hatte ich dieses Jahr mit Ruby RGSS. Hatte einen Unfall - Schulterbruch - und nix zu tun und gamen war mir langweilig (und zu schmerzhaft), da dachte ich: "Jetzt oder nie!"):

...und DAS kam dabei raus:

Bild

HEY - und jetzt nicht denken, dies wäre mein Ziel gewesen! Ich musste mich einfach mit Animationen auseinandersetzen, und da hab' ich mal irgendwo angefangen und... ja, eben. Eigentlich möchte ich schlussendlich eher strategische, rundenbasierte Spiele (OK, ein wenig Pixel-Blut kann trotzdem nicht schaden :twisted: ) verwirklichen. Aber it' a long way to go...

...und doch: es macht Spass! Nur schon im Vergleich zu Ruby ist Python so einfach zu lesen wie eine Tageszeitung, das hat mich wirklich sofort angesprochen und ich habe mich aus diesem Grund dafür entschieden, es mit Python und Pygame nochmals zu versuchen. Im Übrigen verstehe ich glaub' was mit Spaghetticode gemeint ist... mein Zombie-Baller-Game war geradezu überwuchert mit globalen Variablen und ich hatte mir eigentlich schon beim Map-Generator vorgenommen, dies zu ändern, aber manchmal hatte ich einfach nix Anderes am Start als diese doofen Dingens...

Ach ja, meinen Map-Generator werde ich (hoffentlich) irgendwann dazu verwenden, ein Tower Defense - Spiel zu programmieren - dies wäre im Moment das ultimative End-Ziel. Aber wie gesagt: Weil Python coooool ist, fällt es mir viel leichter, mich an kleinen Dingen zu erfreuen! Als mein Mapgenerator das erste Mal eine komplette Karte so baute, dass man zuschauen konnte, was er genau rechnet (nämlich das, was ich im beigebracht hatte), war das wirklich einfach nur - sorry - geil (also jetzt im anständigen Sinn - ausnahmsweise :mrgreen: ).

Jedenfalls: danke, hat mir definitiv geholfen :-)

Kommentare erwünscht!


Henry
Ich code, also bin ich.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Ich meinte eine For-Schleife ganz ohne Zahlen. Versuch das Ganze mal ohne Hochzählen und ohne ``range`` etc, höchstens mit einem

Code: Alles auswählen

foo[1:]
(falls du nicht weiß was das macht: Python-Interpreter auspacken und mit ner Liste probieren).
Benutzeravatar
Don Polettone
User
Beiträge: 115
Registriert: Dienstag 23. November 2010, 20:26
Wohnort: Schweiz

wow, das ist ja voll sexy ;-)

[:1] soll dann etwa heissen "ab" (eben ":") einem gewissen Index und die Umkehrform wäre [1:]?

aber sorry, ich sehe den Zusammenhang irgendwie nicht... das soll helfen, ohne aufzählen auszukommen..?

..?
Ich code, also bin ich.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Henry Jones Jr. hat geschrieben:[:1] soll dann etwa heissen "ab" (eben ":") einem gewissen Index und die Umkehrform wäre [1:]?
Grade andersrum. Schau zum Beispiel mal hier: http://diveintopython.org/native_data_types/lists.html
Antworten