Klassen und pygames

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
reb55
User
Beiträge: 8
Registriert: Donnerstag 25. August 2022, 13:09

Hallo Community,
ich habe ein Problem mit der Programmierung eines Kreises, der sich über den Bildschirm bewegen soll. Da es nicht bei einem Kreis bleiben soll habe ich den Kreis als Klasse ausgelagert.
Leider bieten die Tutorials bzgl. Klasse für meinen Zweck keine sinnvollen Beispiele .
Die Ausführung des unten stehenden Codes erbrachte, dass die Position des Kreises sich nicht ändert. Das sollte sie eigentlich.
Ich vermute dass es mit den Übergabeparametern bzw. Variablen zu tun hat.
Bitte weist mich nicht auf die Ineffizienz des Codes hin; es ist mir schon klar dass man den verbessern kann (wenn er mal läuft). Ich versuche auf diese Weise mir die OOP beizubringen und da ist es sinnvoller mit kleinen Brötchen zu beginnen. Die rect-Funktion möchte ich nebenbei gesagt bewusst nicht benutzen.
Wäre schön wenn ich auf meinen Denkfehler hingewiesen werde; ich bin Momentan in einer Sackgasse.
Der Code:
import pygame as pg

hoehe = 800
weite = 1200

pg.init()


screen = pg.display.set_mode((weite, hoehe))


pg.display.set_caption("Moving circle")

x = 200
y = 200


vx = 5
vy = 5

red = (255,0,0)
black = (0,0,0)

run = True


class baelle():
def __init__(self,x,y,vx,vy):
self.x = x
self.y = y
self.vx = vx
self.vy = vy


def show(self, x,y):
pg.draw.circle(screen,red,(x,y),5)

def hide(self, x,y):
pg.draw.circle(screen,black,(x, y),5)

def move (self,x,y,vx,vy):
x += vx
y += vx
if (x < 5) or (x > weite-10):
vx = -vx
if (y < 5) or (y > hoehe - 10):
vy = -vy


ball1 = baelle(x,y,vx,vy)

while run:

pg.time.delay(50)

for event in pg.event.get():
if event.type == pg.QUIT:
run = False


ball1.hide
pg.display.update()
ball1.move(x,y,vx,vy)
ball1.show(x,y)


pg.quit()
Benutzeravatar
Dennis89
User
Beiträge: 1153
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

beim schnell drüber schauen vermute ich das du mit 'move' deinen Kreis bewegen lassen willst und dazu änderst du die Koordinaten, die du in der Klasse festgelegt hast. Zumindest denke ich dass du das machen willst, tust du aber nicht, du änderst die lokalen 'x'- und 'y'-Werte in der Funktion 'move'.

Du bindest in einer Klasse die Namen nicht grundlos an 'self'. Eine Funktion, die innerhalb der Klasse ist und ja auch schon 'self' als Argument bekommen hat, sollte dann auch 'self' nutzen um auf die entsprechenden Werte zuzugreifen.

Code: Alles auswählen

class baelle():
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def move(self, vx, vy):
        self.x += vx
        self.y += vx
Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
reb55
User
Beiträge: 8
Registriert: Donnerstag 25. August 2022, 13:09

Richtig vermutet, der Ball sollte sich bewegen. Tut er aber noch nicht mal mit "self". Dies dient nach meinen Informationen dazu, mehreren Bällen (nächster Ausbauschritt) die richtigen Parameter zuzuweisen.
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Auch bei nur einem Ball dient das dazu, dessen Position durch move wirklich zu verändern. Was genau also hat du da probiert?
reb55
User
Beiträge: 8
Registriert: Donnerstag 25. August 2022, 13:09

Meine Idee war durch die Methode move die Koordinaten x und y dauerhaft zu verändern - nicht nur in der Methode (da funktioniert es).
Benutzeravatar
Dennis89
User
Beiträge: 1153
Registriert: Freitag 11. Dezember 2020, 15:13

Okay und das machst du, in dem du die x- und y- Koordinaten in der Klasse änderst, denn da sind sie "gespeichert" bzw. an 'self' gebunden. Du musst daran denken, dass du nicht nur eine Funktion in der Klasse hast, da sind ja auch noch andere, die auf die Werte in der Klasse zugreifen müssen. Da sollte man auch mal drüber schauen und über 'self' nachdenken.

Sooo weit weg bist du nicht.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
sparrow
User
Beiträge: 4187
Registriert: Freitag 17. April 2009, 10:28

@reb55: Der Beitrag von __deets__ bleibt damit unbeantwortet.
Weniger Prosa, mehr Code. Zeig was du tust, statt eines "es funktioniert nicht".
reb55
User
Beiträge: 8
Registriert: Donnerstag 25. August 2022, 13:09

:)
OK. Das self wird wichtig, wenn ich mehrere Instanzen habe die auf die Position zugreifen müssen. Das Problem ist, dass der Kreis auf einer Position festgenagelt ist. Selbst wenn ich die move-Methode ändere:
def move (self,x,y,vx,vy):
self.x += vx
self.y += vx
if (x < 5) or (x > weite-10):
vx = -vx
if (y < 5) or (y > hoehe - 10):
vy = -vy

Innerhalb der Methode wird die Position geändert, sobald das Hauptprogramm zum Zuge kommt wird die Position auf die ursprünglichen Werte zurückgesetzt.

Ergänzung: habs jetzt geschnallt - danke für die Nachhilfe.
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@reb55: Anmerkungen zum Quelltext:

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

Man nummeriert keine Namen. Dann will man sich entweder bessere Namen überlegen, oder gar keine Einzelnamen/-werte verwenden, sondern eine Datenstruktur. Oft eine Liste. Hier ist die 1 an `ball` einfach nur sinnlos.

Namen sollten auch keine Abkürzungen enthalten, oder gar nur daraus bestehen. `x` und `y` für Koordinaten sind okay, aber `vx` und `vy` nicht. Wenn man `x_velocity` und `y_velocity` meint, sollte man das auch so schreiben.

`weite` sollte wohl eher `breite` heissen. `baelle` modelliert keine Bälle sondern *einen* Ball, sollte also auch so heissen: `Ball`.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Dann wäre Dir Dein Fehler aufgefallen, denn dann kann keine Methode mehr einfach auf magische Weise auf irgendwelche Variablen zugreifen die zufällig im Modul existieren. Und da fällt dann auch auf, dass `screen` auch übergeben werden muss.

Die Klammern bei den ``if``-Anweisungen in der `move()`-Methode sind überflüssig. Da in beiden Teilbedingungen ein Operand gleich ist, würde man das hier auch eher mit Operatorverkettung auszudrücken.

Warum die Asymmetrie, dass unten und rechts doppelt so viel Platz für das reflektieren der Beschleunigung vorgesehen ist? `x` und `y` beschreiben ja den Mittelpunkt des Kreises und nicht die linke obere Ecke von einem Quadrat, das den Ball umschliesst. Ich würde hier auch keine magischen festen Werte verwenden.

Warum verzichtest Du auf `Rect`-Objekte? Dann verwende wenigstens `Vector2`-Objekte.

Zwischenstand:

Code: Alles auswählen

#!/usr/bin/env python3
import pygame

HOEHE = 800
BREITE = 1200

BLACK = (0, 0, 0)
RED = (255, 0, 0)


class Ball:
    def __init__(self, position, radius, velocity):
        self.position = position
        self.radius = radius
        self.velocity = velocity

    def _draw(self, surface, color):
        pygame.draw.circle(surface, color, self.position, self.radius)

    def show(self, surface):
        self._draw(surface, RED)

    def hide(self, surface):
        self._draw(surface, BLACK)

    def move(self, surface):
        breite, hoehe = surface.get_size()
        self.position += self.velocity
        if not self.radius < self.position.x < breite - self.radius:
            self.velocity = self.velocity.reflect(pygame.Vector2(1, 0))
        if not self.radius < self.position.y < hoehe - self.radius:
            self.velocity = self.velocity.reflect(pygame.Vector2(0, 1))


def main():
    pygame.init()
    try:
        screen = pygame.display.set_mode((BREITE, HOEHE))
        pygame.display.set_caption("Moving circle")
        ball = Ball(pygame.Vector2(200, 200), 5, pygame.Vector2(5, 5))
        while True:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    return

            ball.hide(screen)
            ball.move(screen)
            ball.show(screen)
            pygame.display.update()
            pygame.time.delay(50)
    finally:
        pygame.quit()


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten