Klassen und Funktionen in Pygame

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
k-net
User
Beiträge: 24
Registriert: Donnerstag 22. Oktober 2020, 16:43

Hallo zusammen,

es geht bei meiner Frage letztendlich um pygame, allerdings glaube ich, dass mein Problem, mit dem ich nicht weiterkomme, ein grundlegendes ist, daher schildere ich es zunächst einmal hier. Wenn falsch, bitte verschieben.

Ich versuche seit einiger Zeit Python zu erlernen. Nur aus Spaß und Hobby. Ich habe einige Tutorials durchgearbeitet (z. B. pythonbuch.com und python-lernen.de) und auf Youtube Anleitungen gesehen und die gestellten Aufgaben gelöst. Auch hier habe ich schon als Gast einige Hilfen gefunden. Es macht Spaß! Ich werde kein begnadeter Programmierer werden, dafür reicht mein mathematisches Know How wohl nicht aus. Aber das muss ich auch nicht. Dennoch möchte ich weiter lernen und hoffe, ihr könnt (und wollt) helfen. Ich habe mich lange gesträubt, hier zu posten, da ich es selber lösen wollte, aber jetzt häng ich schon seit Tagen fest und komm einfach nicht vom Fleck.

Ich versuche mich aktuell in pygame. Später soll daraus ein Spiel werden, man bewegt ein Quadrat mit den Pfeiltasten, von rechts kommen mehrere Quadrate an zufälliger Position ins Bild und wenn man eins davon berührt, hat man verloren. Bis dahin ist es aber noch etwas hin.

Ich bin soweit, dass ich ein Quadrat mit den Pfeiltasten bewegen kann und ein weiteres von rechts zufällig durch das Bild läuft. Jetzt möchte ich noch zahlreiche weitere Quadrate von rechts kommen lassen. Das kann ich auch erreichen, indem ich weitere Quadrate in den Loop schreibe... Aber das kann ja nicht richtig sein, da es doch völlig an OOP vorbeigeht.

Nachdem was ich gelesen habe, muss ich eine Klasse erstellen, welche die Eigenschaften der zufälligen Quadrate definiert, verbunden mit einer Funktion, welche die Bewegung der Quadrate festlegt. Sodass ich dann im Loop "nur noch" angeben muss, wie viele Quadrate aus der Klasse erstellt werden und sich bewegen sollen.

Hier mal der Code des "Spiels" bisher:

Code: Alles auswählen

import pygame
import sys
import random

#Fenster
pygame.init()
screen = pygame.display.set_mode([1000, 500])
pygame.display.set_caption('Quadrat')
clock = pygame.time.Clock()

#Fensterumrandung
rand_oben = pygame.draw.rect(screen, (0, 0, 0), (0, 0, 1000, 1))
rand_rechts = pygame.draw.rect(screen, (0, 0, 0), (1000, 0, 1, 500))
rand_unten = pygame.draw.rect(screen, (0, 0, 0), (0, 500, 1000, 1))
rand_links = pygame.draw.rect(screen, (0, 0, 0), (0, 0, 1, 500))

#Variablen
x = 5
y = 5
x2 = 970
y2 = random.randint(5, 475)

#Loop
go = True
while go:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

    quadrat1 = pygame.Rect(x, y, 25, 25)
    quadrat2 = pygame.Rect(x2, y2, 25, 25)

    tastendruck = pygame.key.get_pressed()
    if tastendruck[pygame.K_UP] and not quadrat1.colliderect(rand_oben):
        y -= 5
    if tastendruck[pygame.K_RIGHT] and not quadrat1.colliderect(rand_rechts):
        x += 5
    if tastendruck[pygame.K_DOWN] and not quadrat1.colliderect(rand_unten):
        y += 5
    if tastendruck[pygame.K_LEFT] and not quadrat1.colliderect(rand_links):
        x -= 5

    if x2 <= 970 and not x2 == 0:
        x2 = x2 - 5
    else:
        x2 = 970
        y2 = random.randint(5, 475)

    screen.fill((0, 0, 0))

    quadrat1 = pygame.draw.rect(screen, (255, 255, 255), (x, y, 25, 25))
    quadrat2 = pygame.draw.rect(screen, (255, 0, 0), (x2, y2, 25, 25))

    pygame.display.update()
    clock.tick(60)

Ich denke mir, es läuft auf eine Klasse hinaus, in etwa so:

Code: Alles auswählen

class quadrat:
    def __init__(self, x, y, breite, hoehe):
        self.x = x
        self.y = y
        self.breite = breite
        self.hoehe = hoehe
    def bewegen(self, x, y):
        if self.x <= 970 and not self.x == 0:
            self.x = self.x - 5
        else:
            self.x = 970
            self.y = random.randint(5, 475)
Aber mir scheinen grundlegende Fehler zu unterlaufen... ich bekomme die ganze Sache nicht ans laufen und bekomme es auch nicht im Loop untergebracht.

Es wäre toll, wenn der ein oder andere die Zeit findet, mich zu unterstützen. Insbesondere so, dass ich verstehe, was passiert. Wenn es noch Tutorials gibt, die anschaulich verdeutlichen, was passiert und was ich machen muss um mit Klassen und Funktionen zu arbeiten, dann gerne auch so. Ich kämpfe mich lieber selber durch und verstehe wie es geht, als dass ich einfach nur Codezeilen kopiere. Ich habe schon viel gesucht, aber bisher hat Google mir nicht weitergeholfen.

Gruß und Dank!
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Die Klasse hat ihre Probleme, aber sollte halbwegs tun. Ohne zu zeigen was du probierst kann man da sonst nicht viel sagen.
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@k-net: Die Klasse sollte `Quadrat` heissen. Denn `quadrat` wäre ein guter Name für ein Exemplar davon. Nummerieren sollte man Namen nicht.

Du hast im bisherigen Spiel ja bereits `Rect`-Objekte verwendet/kennengelernt. So eins solltest Du auch in der `Quardat`-Klasse verwenden, statt dort die Angaben an einzelne Attribute zu binden. Denn `Rect`-Objekte haben ja sehr praktische Eigenschaften und Methoden, zum Beispiel zum Verschrieben, sie können den Zeichenfunktionen übergeben werden, und für Test auf Kollisionen haben sie auch etwas.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
k-net
User
Beiträge: 24
Registriert: Donnerstag 22. Oktober 2020, 16:43

Schonmal danke für die Antworten.
Zunächst der aktuelle Code ab Variablen, da habe ich Veränderungen vorgenommen:

Code: Alles auswählen

#Variablen
x = 5
y = 5
x2 = 970
y2 = random.randint(5, 475)
geschwindigkeit = random.randint(5, 15)

class Quadrat:
    def __init__(self, x2, y2, breite, hoehe, geschwindigkeit):
        self.x2 = x2
        self.y2 = y2
        self.breite = breite
        self.hoehe = hoehe
        self.geschwindigkeit = geschwindigkeit
    def bewegen(self):
        if self.x2 <= 970 and not self.x2 == 0:
            self.x2 = self.x2 - 5
        else:
            self.x2 = 970
            self.y2 = random.randint(5, 475)
            self.geschwindigkeit = random.randint(5, 15)

#Loop
go = True
while go:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

    spieler = pygame.Rect(x, y, 25, 25)
    quadrat = Quadrat


    tastendruck = pygame.key.get_pressed()
    if tastendruck[pygame.K_UP] and not spieler.colliderect(rand_oben):
        y -= 5
    if tastendruck[pygame.K_RIGHT] and not spieler.colliderect(rand_rechts):
        x += 5
    if tastendruck[pygame.K_DOWN] and not spieler.colliderect(rand_unten):
        y += 5
    if tastendruck[pygame.K_LEFT] and not spieler.colliderect(rand_links):
        x -= 5

    screen.fill((0, 0, 0))

    spieler = pygame.draw.rect(screen, (255, 255, 255), (x, y, 25, 25))
    quadrat = pygame.draw.rect(screen, (255, 0, 0), (x2, y2, 25, 25))
    quadrat.bewegen()

    pygame.display.update()
    clock.tick(60)


@__deets__
Ich habe die Klasse erstellt, so wie hoffe, dass sie arbeitsfähig ist. Dann weise ich an, dass "quadrat" zur Klasse "Quadrat" gehört und möchte im weiteren, dass mit 'quadrat' so verfahren wird, wie 'bewegen' es vorgibt. Die Fehlermeldung von Pycharm ist "'pygame.Rect' object has no attribute 'bewegen'" und damit stehe ich leider auf dem Schlauch. Ich weiß nicht, was fehlt bzw. wo ich was falsch gemacht habe.


@__blackjack__
Verstehe ich dich in sofern richtig, dass ich quasi in die Klasse wie folgt einfügen kann:

Code: Alles auswählen

class Quadrat:
    def __init__(self, x2, y2, breite, hoehe, geschwindigkeit):
        self.x2 = x2
        self.y2 = y2
        self.breite = breite
        self.hoehe = hoehe
        self.geschwindigkeit = geschwindigkeit
    def bewegen(self):
        if self.x2 <= 970 and not self.x2 == 0:
            self.x2 = self.x2 - 5
        else:
            self.x2 = 970
            self.y2 = random.randint(5, 475)
            self.geschwindigkeit = random.randint(5, 15)
    def zeichnen(self):
        quadrat = pygame.draw.rect(screen, (255, 0, 0), (x2, y2, 25, 25))
Sodass in der Klasse auch der Befehl ist, das Quadrat darzustellen? Von "Rect" kenne ich bisher nur das im Code vorhandene, was die Feinheiten angeht, habe ich noch Nachholbedarf.
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@k-net: Du binest am Anfang den Namen `quadrat` an die Klasse `Quadrat`, was keinen Sinn macht. Vielleicht wolltest Du hier ja ein Objekt vom Typ `Quadrat` erstellen, aber dann musst Du das auch *machen* und die entsprechenden Argumente übergeben, denn so ein Quadrat lässt sich ja nicht ohne die Informationen erstellen man braucht um es zu beschreiben.

Die Fehlermeldung bezüglich `bewegen()` bekommst Du weil Du den Namen `quadrat` direkt in der Zeile davor an ein `Rect`-Objekt bindest. Das hat natürlich diese Methode nicht.

Fang übrigens am besten gar nicht erst an Code auf Modulebene so unübersichtlich zu mischen. Das Hauptprogramm steckst Du am besten in eine Funktion. Die heisst üblicherweise `main()`.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
k-net
User
Beiträge: 24
Registriert: Donnerstag 22. Oktober 2020, 16:43

Hm, ich dachte ich hätte es so gemacht, wie ich in einem Tutorial gelesen habe. Vermutlich habe ich etwas falsch verstanden. Ich habe aber auch noch keins gefunden, das mir die Verwendung von Klassen in Pygame anschaulich erklärt.

Ich verstehe nur bedingt worauf du mich aufmerksam machen möchtest. Das Quadrat erstellen kann ich natürlich, aber wie weise ich ihm die in der Klasse beschriebenen Bewegung zu?

Ich werde weiter nach Erklärungen suchen und diese lesen. Für Links zu guten Anleitungen wäre ich dankbar. Wenn ich also nicht zeitnah antworte, bin ich mit lernen und ausprobieren beschäftigt.
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@k-net: Es gibt bei Klassen in Verbindung mit Pygame ja auch nichts besonderes zu beachten. Man muss halt Klassen allgemein verstanden haben, das hat nichts mit Pygame zu tun. Weder braucht man zum Verständnis von Klassen Pygame, noch ändert sich irgendetwas durch Pygame. Darum gibt es keine Tutorials „So funktionieren Klassen mit Pygame“, sondern nur welche „So funktionieren Klassen“.

Eine der Grundlagen für Klasse ist das man Funktionen verstanden hat. Bei dem Code von Dir habe ich so ein bisschen den Verdacht, dass Klassen für Dich erst der übernächste Schritt sind, und Du vorher auch noch mal Funktionen lernen solltest. Also ”echte” Funktionen. Nicht nur wie die ``def``-Syntax dafür aussieht, sondern das man keine globalen Variablen verwendet, sondern das Funktionen (und später auch Methoden) alles was sie ausser Konstanten verwenden als Argument(e) übergeben bekommen. Und Ergebnisse als Rückgabewert liefern.

Du musst einem erstellten `Quadrat`-Objekt keine Bewegung zuweisen, diese Methode haben `Quadrat`-Objekte dadurch, dass es die entsprechende Methode auf der Klasse gibt. Du hast an der Stelle wo die Fehlermeldung kommt aber gar kein `Quadrat`-Objekt. Warum denkst Du Du hättest da eines? In der Zeile davor definierst Du `quadrat` als den Rückgabewert des `pygame.draw.rect()`-Aufrufs. Diese Funktion liefert kein `Quadrat`-Objekt, die liefert ein `Rect`-Objekt. Und `Rect`-Objekte haben keine `bewegen()`-Methode.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
k-net
User
Beiträge: 24
Registriert: Donnerstag 22. Oktober 2020, 16:43

__blackjack__ hat geschrieben: Samstag 24. Oktober 2020, 17:23 Eine der Grundlagen für Klasse ist das man Funktionen verstanden hat. Bei dem Code von Dir habe ich so ein bisschen den Verdacht, dass Klassen für Dich erst der übernächste Schritt sind, und Du vorher auch noch mal Funktionen lernen solltest.
Ja, den Eindruck habe ich auch gerade :lol:

Okay, dann gehe ich nochmal einen Schritt zurück. Besser von Anfang an vernünftig :geek:. Ich werde die Suchfunktion hier und Google nach entsprechenden Übungen befragen. Wer spontan ein oder mehrere gute Tutorials zu dem Thema weiß, gerne her damit.

Danke.
k-net
User
Beiträge: 24
Registriert: Donnerstag 22. Oktober 2020, 16:43

So, mittlerweile bin ich etwas weiter und habe zumindest einen ersten Zugang zu Klassen gefunden. Ich werde vorerst an meiner Idee mit einigen Modifikationen weiter arbeiten, habe aber aktuell eine Frage:

Wenn ich folgenden Code laufen lasse, haben die dargestellten Quadrate ein schwarzes Quadrat in der Mitte. Das irritiert mich. Warum ist das so? Die sollten doch ausgefüllt sein vollständig. Warum haben die ein Loch in der Mitte?

Code: Alles auswählen

#Module importieren
import pygame
import sys
import random

#Fenster
pygame.init()
screen = pygame.display.set_mode([1000, 500])
pygame.display.set_caption("Don't touch!")
clock = pygame.time.Clock()

#Farben
black = (0, 0, 0)
white = (255, 255, 255)
red = (255, 0, 0)
green = (0, 255, 0)
blue = (0, 0, 255)
yellow = (255, 255, 0)
purple = (255, 0, 255)
teal = (0, 255, 255)

#Liste
colors = [red, green, blue, yellow, purple, teal]

#Klassen
class Objekte_unten():
    def __init__(self):
        self.pos_x = random.randint(5, 995)
        self.pos_y = 495
        self.breite = 20
        self.hoehe = 20
        self.color = colors[random.randint(0, len(colors) - 1)]
        self.speed = random.randint(2, 20)
    def zeichnen(self):
        pygame.draw.rect(screen, (self.color), (self.pos_x, self.pos_y, self.breite, self.hoehe), self.speed)
        if self.pos_y <= 495:
            self.pos_y = self.pos_y - self.speed
        if self.pos_y <= 0:
            self.pos_y = 495
            self.pos_x = random.randint(5, 995)
            self.speed = random.randint(2, 10)
            self.color = colors[random.randint(0, len(colors) - 1)]

quadrat_unten = Objekte_unten()


#Loop
go = True
while go:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

    screen.fill(black)
    quadrat_unten.zeichnen()

    pygame.display.update()

    clock.tick(60)
Antworten