PyGame zeichnet nicht immer Linien

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
Moin
User
Beiträge: 3
Registriert: Dienstag 31. März 2020, 21:56

Hallo liebe Community,
erstmal ein Lob: tolles Forum und tolle Leute!
Ich habe mich jetzt mal registriert, nachdem mir das Forum schon unzählige Male weiter geholfen hat, und auch gleich mal eine Frage:

Ich möchte ein Spiel mit PyGame programmieren bei dem es um folgendes geht:
Man wird in einem zufällig generierten Labyrinth ausgesetzt und muss den Weg nach draußen finden. Blöd ist nur, es ist dunkel, man sieht also nichts.
Also läuft man mit dem Spieler (Im Moment mal ein roter Punkt) in eine Richtung, wenn jedoch eine Wand im Weg ist, so kommt man nicht weiter und diese wird im Spielfeld eingezeichnet.
Man macht also Wände sichtbar, in dem man dagegen läuft.
Das Spielfeld ist in Zellen eingeteilt, das kommt daher, dass das ursprünglich ein Spiel war, welches wir auf Karopapier gespielt haben :)

Später sind einige Dinge geplant, auf die Man im Labyrinth stoßen kann (Beispiele: Fällt man in einen Brunnen, so kommt man irgendwo anders im Labyrinth wieder heraus, man weiß aber nicht wo, oder: Es gibt einen Minotaurus,
dieser jagt einen und kommt einem jedes Mal, wenn Man gegen eine Wand läuft eine Zelle näher.)

Nun ist ja bekannt, dass PyGame Programme eine Hauptschleife besitzen, die unablässig durchlaufen wird. Die Idee war also, alle Wände von Anfang an zeichnen zu lassen, allerdings schwarze Wände auf schwarzem Hintergrund.
Jedes Mal, wenn Man gegen eine Wand läuft, wird diese in eine Liste (bzw. Dictionary) hineingeschrieben, welche beim erneuten Zeichnen der Wände abgefragt und die darin eingetragenen (entdeckten) Wände sichtbar gezeichnet werden.

Unsichtbar werden alle Wände gezeichnet (das sieht Man, wenn man die Farbe dieser ändert), bei den sichtbare (entdeckten) Wänden gibt es aber ein Problem:
Läuft der Spieler von rechts nach links gegen eine Wand, so wird diese sichtbar eingezeichnet, genau so von unten nach oben.
Läuft er jedoch von links nach rechts oder von oben nach unten gegen eine Wand, so wird diese gar nicht eingezeichnet. Dabei ist das die ein und die selbe Funktion !? Wieso?
Ich habe überprüft, ob die Funktion aufgerufen wird und ja: das Skript wird vollständig ausgeführt, nur der Befehl pygame.draw.line() wird für die oben beschriebenen Fälle "übergangen"?!

Ich habe die betreffende Funktion Mal mit ####### PROBLEM ####### gekennzeichnet, ich habe auch versucht, das Programm verständlich zu machen. Hier der Code:

Code: Alles auswählen

import pygame as pg
import random


BREITE = HÖHE = 1000        #
SPALTEN = ZEILEN = 30       # quadratische Sielfläche; quadratische Zellen
ZE_BH = BREITE // SPALTEN   #
quellposition = (-(ZE_BH // 2), -(ZE_BH // 2))  # Koordinaten, um von der Quellposition (links oben in z.B. einer Zelle)
                                                # zur Mitte z.B. einer Zelle zu gelangen (wo der Spieler hin soll)
# QUELLPOSITION:
# Dies bezeichnet immer die "rechnerische" Position, welche auch der Zeichen-Algorythmus verwendet (am Anfang z.B. (0,0))

# pos/ NORMALE POSITION:
# Bezeichnet die "graphische" Position (in der Mitte einer Zelle), der spieler soll ja nicht immer links oben in einer Zelle kleben

delta_linien = {'l': [(0,0), (0,ZE_BH)], 'r': [(ZE_BH,0), (ZE_BH,ZE_BH)], 'o': [(0,0), (ZE_BH,0)], 'u': [(0,ZE_BH), (ZE_BH,ZE_BH)]}
delta_nachbarn = {'l': (-ZE_BH, 0), 'r': (ZE_BH, 0), 'o': (0, -ZE_BH), 'u': (0, ZE_BH)}
richtung_invers = {'l': 'r', 'r': 'l', 'o': 'u', 'u': 'o'}
wandverzeichnis = {}
entdeckte_wände = {'l':[], 'r':[], 'o':[], 'u':[]}  # verzeichnet Wände, gegen die man bereits gelaufen ist
                                                    # (diese sollen später farblich markiert werden)

pg.init()
screen = pg.display.set_mode([BREITE, HÖHE])
farbe_hintergrund = pg.Color('Black')           #
farbe_linie_sichtbar = pg.Color('White')        # Wand erst sichtbar, wenn man sie entdeckt hat (s.o.)
farbe_linie_unsichtbar = pg.Color('Black')      #
farbe_spieler = pg.Color('Red')

raster = {}
for i in range(SPALTEN*ZEILEN):
    pos = i % SPALTEN * ZE_BH, i // SPALTEN * ZE_BH
    raster[pos] = {'l', 'r', 'o', 'u'}

def add_pos(pos1, pos2):                            # um eine Positionen (Tupel) zu addieren
    return pos1[0] + pos2[0], pos1[1] + pos2[1]

def zeichne_zelle(pos, wände):
    for wand in wände:                              # wände enthält alle Wende, die eine Zelle hat (z.B.: 'l','r','u')
        delta_von, delta_bis = delta_linien[wand]   # wand = z.B.: 'l' oder 'r' oder 'u'
        von = add_pos(pos, delta_von)
        bis = add_pos(pos, delta_bis)
        if pos in entdeckte_wände[wand]:            # linie soll sichtbar gezeichnet werden, wenn man sie schon entdeckt hat
            pg.draw.line(screen, farbe_linie_sichtbar, von, bis, 2)                    ############ PROBLEM ############
        else:                                       # ansonsten unsichtbar (schwarz auf schwarz)
            pg.draw.line(screen, farbe_linie_unsichtbar, von, bis, 2)
            wandverzeichnis[pos] = wände            # erstellt ein Wandverzeichnis für jede Zelle (Position)
                                                    # ist wichtig für check_wall() (siehe unten)

def nachbarn_ermitteln(pos):                        # ermittelt alle (möglichen) Nachbarzellen von der aktuellen Position
    nachb = []
    for richtung, delta in delta_nachbarn.items():
        neue_pos = add_pos(pos, delta)
        if neue_pos not in raster:
            continue
        else:
            nachb.append((richtung, neue_pos))
    random.shuffle(nachb)
    return nachb


besucht = set()                                     # set für bereits besuchte Zellen

def labyrinth_erstellen(pos_aktuell, richtung_von):
    besucht.add(pos_aktuell)
    raster[pos_aktuell].remove(richtung_von)
    nachb = nachbarn_ermitteln(pos_aktuell)
    for richtung_nach, pos_neu in nachb:
        if pos_neu in besucht:
            continue
        else:
            raster[pos_aktuell].remove(richtung_nach)
            labyrinth_erstellen(pos_neu, richtung_invers[richtung_nach])    # ruft sich selbst auf und generiert so das Labyrinth

def check_wall(gewünschte_richtung, pos):               # überprüft auf im Weg stehende Wände
    if gewünschte_richtung in wandverzeichnis[pos]:     # (wenn Bewegungsrichtung klar ist)
        return True
    else:
        return False


startx = random.randint(1,29)       #
starty = random.randint(1,29)       # setzt Startkoordinaten für Spieler per Zufall
labyrinth_erstellen((0,0), 'l')     # fängt immer links oben mit dem Erstellen an

alte_pos_spieler = (startx,starty)
neue_pos_spieler = (startx*ZE_BH + ZE_BH // 2, starty*ZE_BH + ZE_BH // 2)   # setzt Spieler in die Welt ZE_BEH // 2 garantiert,
                                                                        # dass er in der Mitte einer Zelle auftaucht
                                                                        # (nicht im linken oberen Eck (= quellposition))
pg_quit = False



############### HAUPTSCHLEIFE ###############



while not pg_quit:
    screen.fill(farbe_hintergrund)
    for pos, wände in raster.items():   # zeichnet sowohl entdeckte als auch unentdeckte Wände immer neu
        zeichne_zelle(pos, wände)       # entdeckte Wände werden so laufend aktualisiert
    for event in pg.event.get():

        if event.type == pg.QUIT or (event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE):    # QUIT event
            pg_quit = True

        if event.type == pg.KEYDOWN and event.key == pg.K_LEFT:             # Kommando für linke Pfeiltaste
            pg.time.delay(100)
            gewünschte_richtung = 'l'
            quellposition_aktuell = add_pos(alte_pos_spieler, quellposition)    # errechnet Quellposition (s.o.)
            if check_wall(gewünschte_richtung, quellposition_aktuell) == True:
                entdeckte_wände[gewünschte_richtung].append(quellposition_aktuell)  # markiert entdeckte Wand
                                                                                    # verschiebt Sieler nicht
            else:
                neue_pos_spieler = add_pos(alte_pos_spieler, delta_nachbarn['l'])   # verschiebt Spieler

        if event.type == pg.KEYDOWN and event.key == pg.K_RIGHT:           # Kommando für rechte Pfeiltaste
            pg.time.delay(100)
            gewünschte_richtung = 'r'
            quellposition_aktuell = add_pos(alte_pos_spieler, quellposition)    # ...
            if check_wall(gewünschte_richtung, quellposition_aktuell) == True:
                entdeckte_wände[gewünschte_richtung].append(quellposition_aktuell)
            else:
                neue_pos_spieler = add_pos(alte_pos_spieler, delta_nachbarn['r'])

        if event.type == pg.KEYDOWN and event.key == pg.K_UP:               # Kommando für Pfeiltaste hoch
            pg.time.delay(100)
            gewünschte_richtung = 'o'
            quellposition_aktuell = add_pos(alte_pos_spieler, quellposition)
            if check_wall(gewünschte_richtung, quellposition_aktuell) == True:
                entdeckte_wände[gewünschte_richtung].append(quellposition_aktuell)
            else:
                neue_pos_spieler = add_pos(alte_pos_spieler, delta_nachbarn['o'])

        if event.type == pg.KEYDOWN and event.key == pg.K_DOWN:             # Kommando für Pfeiltaste runter
            pg.time.delay(100)
            gewünschte_richtung = 'u'
            quellposition_aktuell = add_pos(alte_pos_spieler, quellposition)
            if check_wall(gewünschte_richtung, quellposition_aktuell) == True:
                entdeckte_wände[gewünschte_richtung].append(quellposition_aktuell)
            else:
                neue_pos_spieler = add_pos(alte_pos_spieler, delta_nachbarn['u'])

    pg.draw.circle(screen, farbe_spieler, neue_pos_spieler, 7)      # zeichnet Spieler
    alte_pos_spieler = neue_pos_spieler                             # speichert aktuelle Position des Spielers
    pg.display.flip()

pg.quit()
Wenn jemandem der Fehler auffällt, wäre ich sehr dankbar für Rückmeldung, ich komme gerade echt nicht weiter.

Mit freundlichen Grüßen, MOIN.
Benutzeravatar
__blackjack__
User
Beiträge: 13110
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Moin: Das ist schwer zu lesen und zu verstehen. Die ganzen Kommentare machen die Zeilen zu lang. Kommentare am Zeilenende sollten wirklich nur sehr kurz sein. Für längere Texte schreibt man die Kommentare vor den Code auf den sie sich beziehen. Zeilen werden durch die Art wie Du Kommentare schreibst nicht nur zu lang, es ist auch unnötig arbeit immer die Ausrichtung zu korrigieren wenn man etwas am Code davor ändert.

Konstanten werden KOMPLETT_GROSS geschrieben und Variablen haben auf Modulebene nichts zu suchen. Das Hauptprogramm steht üblicherweise in einer Funktion mit dem Namen `main()` und nicht zwischen anderen Funktionsdefinitionen verteilt.

Funktionen und Methoden bekommen alles was sie ausser Konstanten benötigen als Argument(e) übergeben. Dann kann man Funktionen einzeln für sich betrachten und auch testen, ohne das gesamte Programm mit allen Variablen immer im Kopf haben zu müssen.

Wenn Funktionen oder Methoden übergebene Argumente verändern sollte das für den Leser nicht überraschend sein. Der Funktions-/Methodenname sollte diese Info hergeben oder falls nicht, sollte das dokumentiert sein. Möglichst als Docstring und nicht als Kommentar.

Keine kryptischen Abkürzungen in Namen. Warum `nachb` wenn `nachbar` gemeint ist?

Falls man einen Kommentar braucht um den Namen zu erklären, schauen ob man den Namen verbessern kann, so das der Kommentar nicht nötig ist. Beispiel wäre ``besucht = set() # set für bereits besuchte Zellen`` was man besser als ``besuchte_zellen = set()`` schreiben würde. Das hat alle Informationen die vorher im Kommentar standen, und man hat die dann nicht nur an *der* Stelle, sondern auch überall wo der Name dann benutzt wird.

Man vergleicht keine Wahrheitswerte mit literalen `True` oder `False`. Da kommt doch nur wieder der Wahrheitswert heraus den man sowieso schon hatte, oder dessen Gegenteil. Man nimmt den also direkt oder negiert ihn mit ``not``.

`check_wall()` ist unnötig kompliziert, weil man auch hier bereits den Wahrheitswert hat und ohne ``if``/``else`` einfach zurückgeben kann:

Code: Alles auswählen

def check_wall(gewünschte_richtung, pos):               # überprüft auf im Weg stehende Wände
    if gewünschte_richtung in wandverzeichnis[pos]:     # (wenn Bewegungsrichtung klar ist)
        return True
    else:
        return False

# ->

def check_wall(wandverzeichnis, gewuenschte_richtung, position):
    """
    Prüft auf im Weg stehende Wände (wenn Bewegungsrichtung klar ist).
    """
    return gewuenschte_richtung in wandverzeichnis[position]
``continue`` sollte man meiden wo es geht und in den beiden Fällen wo das verwendet wird, geht sogar sehr einfach: Die Bedingung beim ``if`` umdrehen, den Code aus dem ``else``- in den ``if``-Zweig verschieben.

Der Code bei der Tastenauswertung für die einzelnen Richtungen ist viermal fast identisch. Solche Codewiederholungen sollte man vermeiden. Das könnte man beispielsweise als Funktion heraus ziehen.

Diese einbuchstabigen Zeichenketten für die Richtungen wären mir zu kryptisch und zu fehleranfällig. Dafür würde man Konstanten definieren mit aussagekräftigen Namen. Eventuell auch mit Hilfe des `enum`-Moduls.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Moin
User
Beiträge: 3
Registriert: Dienstag 31. März 2020, 21:56

Vielen Dank,

beim Programmieren habe ich einfach noch nicht so viel Erfahrung, aber es ist richtig, lesbar machen sollte man seinen Code können.

Ich überarbeite mein Skript jetzt Mal, so, wie oben geschrieben und poste es dann nochmal.

Trotzdem schonmal vielen Dank für die Hilfe,
Moin.
Moin
User
Beiträge: 3
Registriert: Dienstag 31. März 2020, 21:56

Also das hat jetzt eine Weile gedauert, die aktuellen Umstände sind schon komisch,
naja, jedenfalls hoffe ich, jemand kann mir bei dem Problem helfen, PyGame zeichnet einfach nicht immer Linien, obwohl es soll.
Hier der Code:

Code: Alles auswählen

import pygame as pg
import random


BREITE = HÖHE = 1000
SPALTEN = ZEILEN = 30
ZELLE_BH = BREITE // SPALTEN
vektor_quellposition = (-(ZELLE_BH // 2), -(ZELLE_BH // 2))

LINKS = 'l'
RECHTS = 'r'
OBEN = 'o'
UNTEN = 'u'

pg.init()
screen = pg.display.set_mode([BREITE, HÖHE])
delta_linien = {LINKS: [(0,0), (0,ZELLE_BH)], RECHTS: [(ZELLE_BH,0), (ZELLE_BH,ZELLE_BH)], OBEN: [(0,0), (ZELLE_BH,0)], UNTEN: [(0,ZELLE_BH), (ZELLE_BH,ZELLE_BH)]}
delta_nachbarn = {LINKS: (-ZELLE_BH, 0), RECHTS: (ZELLE_BH, 0), OBEN: (0, -ZELLE_BH), UNTEN: (0, ZELLE_BH)}
richtung_invers = {LINKS: RECHTS, RECHTS: LINKS, OBEN: UNTEN, UNTEN: OBEN}
wandverzeichnis = {}
entdeckte_wände = {LINKS:[], RECHTS:[], OBEN:[], UNTEN:[]}
besuchte_zellen = set()

raster = {}

for i in range(SPALTEN*ZEILEN):
    position = i % SPALTEN * ZELLE_BH, i // SPALTEN * ZELLE_BH
    raster[position] = {LINKS, RECHTS, OBEN, UNTEN}

startx = random.randint(1,29)
starty = random.randint(1,29)
aktuelle_position = (startx*ZELLE_BH + ZELLE_BH // 2, starty*ZELLE_BH + ZELLE_BH // 2)

farbe_hintergrund = pg.Color('Black')
farbe_linie_sichtbar = pg.Color('White')
farbe_linie_unsichtbar = pg.Color('Black')
farbe_spieler = pg.Color('Red')




def add_positionen(position1, position2):
    return position1[0] + position2[0], position1[1] + position2[1]

def zeichne_zelle(position, wände):
    for wand in wände:
        delta_von, delta_bis = delta_linien[wand]
        von = add_positionen(position, delta_von)
        bis = add_positionen(position, delta_bis)
        if position in entdeckte_wände[wand]:
            pg.draw.line(screen, farbe_linie_sichtbar, von, bis, 2)
        else:
            pg.draw.line(screen, farbe_linie_unsichtbar, von, bis, 2)
        wandverzeichnis[position] = wände

def nachbarn_ermitteln(position):
    nachbarn = []
    for richtung, delta in delta_nachbarn.items():
        neue_position = add_positionen(position, delta)
        if neue_position in raster:
            nachbarn.append((richtung, neue_position))

    random.shuffle(nachbarn)
    return nachbarn

def check_wall(gewuenschte_richtung, position):
    return gewuenschte_richtung in wandverzeichnis[position]

def labyrinth_erstellen(position, richtung_von):
    besuchte_zellen.add(position)
    raster[position].remove(richtung_von)
    nachbarn = nachbarn_ermitteln(position)
    for richtung_nach, position_neu in nachbarn:
        if position_neu not in besuchte_zellen:
            raster[position].remove(richtung_nach)
            labyrinth_erstellen(position_neu, richtung_invers[richtung_nach])


labyrinth_erstellen((0,0), LINKS)

pg_quit = False

while not pg_quit:
    screen.fill(farbe_hintergrund)
    for position, wände in raster.items():
        zeichne_zelle(position, wände)
    for event in pg.event.get():

        if event.type == pg.QUIT or (event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE):
            pg_quit = True

        #'''
        if event.type == pg.KEYDOWN and event.key == pg.K_LEFT:
            pg.time.delay(100)
            gewünschte_richtung = LINKS
            quellposition_aktuell = add_positionen(aktuelle_position, vektor_quellposition)
            if check_wall(gewünschte_richtung, quellposition_aktuell):
                if quellposition_aktuell not in entdeckte_wände[gewünschte_richtung]:
                    entdeckte_wände[gewünschte_richtung].append(quellposition_aktuell)
                print(entdeckte_wände)
            else:
                neue_position = add_positionen(aktuelle_position, delta_nachbarn[LINKS])
                aktuelle_position = neue_position

        if event.type == pg.KEYDOWN and event.key == pg.K_RIGHT:
            pg.time.delay(100)
            gewünschte_richtung = RECHTS
            quellposition_aktuell = add_positionen(aktuelle_position, vektor_quellposition)
            if check_wall(gewünschte_richtung, quellposition_aktuell):
                if quellposition_aktuell not in entdeckte_wände[gewünschte_richtung]:
                    entdeckte_wände[gewünschte_richtung].append(quellposition_aktuell)
                print(entdeckte_wände)
            else:
                neue_position = add_positionen(aktuelle_position, delta_nachbarn[RECHTS])
                aktuelle_position = neue_position

        if event.type == pg.KEYDOWN and event.key == pg.K_UP:
            pg.time.delay(100)
            gewünschte_richtung = OBEN
            quellposition_aktuell = add_positionen(aktuelle_position, vektor_quellposition)
            if check_wall(gewünschte_richtung, quellposition_aktuell):
                if quellposition_aktuell not in entdeckte_wände[gewünschte_richtung]:
                    entdeckte_wände[gewünschte_richtung].append(quellposition_aktuell)
                print(entdeckte_wände)
            else:
                neue_position = add_positionen(aktuelle_position, delta_nachbarn[OBEN])
                aktuelle_position = neue_position

        if event.type == pg.KEYDOWN and event.key == pg.K_DOWN:
            pg.time.delay(100)
            gewünschte_richtung = UNTEN
            quellposition_aktuell = add_positionen(aktuelle_position, vektor_quellposition)
            if check_wall(gewünschte_richtung, quellposition_aktuell):
                if quellposition_aktuell not in entdeckte_wände[gewünschte_richtung]:
                    entdeckte_wände[gewünschte_richtung].append(quellposition_aktuell)
                print(entdeckte_wände)
            else:
                neue_position = add_positionen(aktuelle_position, delta_nachbarn[UNTEN])
                aktuelle_position = neue_position

    pg.draw.circle(screen, farbe_spieler, aktuelle_position, 7)
    pg.display.flip()

pg.quit()


Viele Grüße,
Moin.
Antworten