pygame breakout problem

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
kwon
User
Beiträge: 43
Registriert: Samstag 2. Mai 2020, 11:48

Hallo,
ich habe mit Hilfe aus dem Forum eine Maussteuerung für ein Arkanoid/Breakout-Spiel erstellt. Habe dazu eine Vorlage, die per Tastatur gesteuert wird, angepasst.

Jetzt erhalte ich einen Fehler, mit dem ich nichts anfangen kann. Unter anderem wird da von Zeile 134 gesprochen, allerdings verfügt mein main nur über 120 Zeilen...

Ich werde die Programmteile und die Fehlermeldung hier veröffentlichen, mit der Bitte um Hilfe - also was ich tun kann bzw. falsch mache...

Ich verwende Pycharm als Editor...

Hier die Fehlermeldung:

C:\Users\...\AppData\Local\Microsoft\WindowsApps\python3.10.exe "D:/Daten_Tablet/aufbewahren/Tablet/Programmierung/Python/__weiter breakout/main.py"
pygame 2.1.2 (SDL 2.0.18, Python 3.10.1)
Hello from the pygame community. https://www.pygame.org/contribute.html
Traceback (most recent call last):
File "D:\Daten_Tablet\aufbewahren\Tablet\Programmierung\Python\__weiter breakout\main.py", line 119, in <module>
main()
File "D:\Daten_Tablet\aufbewahren\Tablet\Programmierung\Python\__weiter breakout\main.py", line 27, in main
ball = Ball(WHITE, 10, 10)
File "C:\Users\...\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\pygame\sprite.py", line 116, in __init__
self.add(*groups)
File "C:\Users\...\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\pygame\sprite.py", line 134, in add
self.add(*group)
File "C:\Users\...\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\pygame\sprite.py", line 134, in add
self.add(*group)
TypeError: pygame.sprite.Sprite.add() argument after * must be an iterable, not int

Process finished with exit code 1

hier main:

Code: Alles auswählen

# !/usr/bin/env python3
import pygame
from paddle import Paddle
from ball import Ball
from brick import Brick
pygame.init()

WHITE = (255, 255, 255)
DARKBLUE = (36, 90, 190)
LIGHTBLUE = (0, 176, 240)
RED = (255, 0, 0)
ORANGE = (255, 100, 0)
YELLOW = (255, 255, 0)
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("cooles Breakout")


def main():
    try:
        score = 0
        lives = 5
        pygame.mouse.set_visible(0)
        all_sprites = pygame.sprite.Group()
        all_bricks = pygame.sprite.Group()
        paddle = Paddle(screen.get_rect())
        all_sprites.add(paddle)
        ball = Ball(WHITE, 10, 10)
        all_sprites.add(ball)
        ball.rect.x = 345
        ball.rect.y = 195

        for i in range(7):
            brick = Brick(RED, 80, 30)
            brick.rect.x = 60 + i * 100
            brick.rect.y = 60
            all_sprites.add(brick)
            all_bricks.add(brick)

        for i in range(7):
            brick = Brick(ORANGE, 80, 30)
            brick.rect.x = 60 + i * 100
            brick.rect.y = 100
            all_sprites.add(brick)
            all_bricks.add(brick)

        for i in range(7):
            brick = Brick(YELLOW, 80, 30)
            brick.rect.x = 60 + i * 100
            brick.rect.y = 140
            all_sprites.add(brick)
            all_bricks.add(brick)

        # to control how fast the screen updates
        clock = pygame.time.Clock()
        while True:
            # Limit to 60 frames per second
            clock.tick(60)
            all_sprites.update()
            # Check if the ball is bouncing against the 4 walls
            if ball.rect.x >= 790:
                ball.velocity[0] = -ball.velocity[0]
            if ball.rect.x <=0:
                ball.velocity[0] = -ball.velocity[0]
            if ball.rect.y > 590:
                ball.velocity[1] = -ball.velocity[1]
                lives -= 1
                if lives == 0:
                    font = pygame.font.Font(None, 74)
                    text = font.render("Game over", 1, WHITE)
                    screen.blit(text, (250, 300))
                    pygame.display.flip()
                    pygame.time.wait(3000)
                    # stop the game
                    return
            if ball.rect.y < 40:
                ball.velocity[1] = -ball.velocity[1]

            # Detect collisions between the ball and the paddle
            if pygame.sprite.collide_mask(ball, paddle):
                ball.rect.x -= ball.velocity[0]
                ball.rect.y -= ball.velocity[1]
                ball.bounce()

            # Check if the ball collides with any of the bricks
            brick_collision = pygame.sprite.spritecollide(ball, all_bricks, False)
            for brick in brick_collision:
                ball.bounce()
                score += 1
                brick.kill()
                if len(all_bricks) == 0:
                    font = pygame.font.Font(None, 74)
                    text = font.render("Level complete", 1, WHITE)
                    screen.blit(text, (200, 300))
                    pygame.display.flip()
                    pygame.time.wait(3000)
                    # stop the game
                    return

            screen.fill(DARKBLUE)
            font = pygame.font.Font(None, 34)
            text = font.render("Score: " + str(score), 1, WHITE)
            screen.blit(text, (20, 10))
            text = font.render("Lives: " + str(lives), 1, WHITE)
            screen.blit(text, (650, 10))

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    return
                elif event.type == pygame.MOUSEMOTION:
                    paddle.update(event.pos)
            all_sprites.draw(screen)
            # update the screen with what we have drawn
            pygame.display.flip()
    finally:
        pygame.quit()


if __name__ == "__main__":
    main()
hier ball:

Code: Alles auswählen

import pygame
from random import randint
BLACK = (0, 0, 0)


class Ball(pygame.sprite.Sprite):
    def __int__(self, color, width, height):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface([width, height])
        self.image.fill(BLACK)
        self.image.set_colorkey(BLACK)

        pygame.draw.rect(self.image, color, [0, 0, width, height])
        self.velocity = [randint(4, 8), randint(-8, 8)]
        self.rect = self.image.get_rect()

    def update(self):
        self.rect.x += self.velocity[0]
        self.rect.y += self.velocity[1]

    def bounce(self):
        self.velocity[0] = -self.velocity[0]
        self.velocity[1] = randint(-8,8)
hier brick:

Code: Alles auswählen

import pygame
BLACK = (0,0,0)


class Brick(pygame.sprite.Sprite):
    # This class represents a brick. It derives from the "Sprite" class in Pygame.

    def __init__(self, color, width, height):
        # Call the parent class (Sprite) constructor
        super().__init__()

        # Pass in the color of the brick, and its x and y position, width and height.
        # Set the background color and set it to be transparent
        self.image = pygame.Surface([width, height])
        self.image.fill(BLACK)
        self.image.set_colorkey(BLACK)

        # Draw the brick (a rectangle!)
        pygame.draw.rect(self.image, color, [0, 0, width, height])

        # Fetch the rectangle object that has the dimensions of the image.
        self.rect = self.image.get_rect()
hier paddle:

Code: Alles auswählen

import pygame
BLACK = (0, 0, 0)


class Paddle(pygame.sprite.Sprite):
    def __init__(self, playfield_rect):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((80, 15))
        self.image.fill(BLACK)
        self.playfield_rect = playfield_rect
        self.rect = self.image.get_rect()
        self.rect.bottom = self.playfield_rect.bottom

    def update(self, position):
        x, _ = position
        self.rect.centerx = x
        self.rect = self.rect.clamp(self.playfield_rect)
Über Hilfe würde ich mich sehr freuen!

Grüße,
kwon
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich kann mir nicht vorstellen, dass der Code zum Fehler passt. Denn der Sprite-Code ist da recht klar: das Sprite wird uebergebenen Gruppen direkt hinzugefueght. Eine Moeglichkeit, deinen Code deutlich besser zu gestalten, indem du da die entsprechende(n) Gruppe(n) gleich angibst, statt das nachtraeglich zu machen. Aber der Punkt ist: du uebergibst da offensichtlich eine Zahl (ich vermute mal eine Koordinate oder ggf. Farbe), aber das entspricht nicht dem Ball-Code, und der Art, wie der die __init__-Methode aufruft. Das muss also jetzt anders aussehen.

Du solltest auch einheitlich super() oder eben nicht verwenden.
kwon
User
Beiträge: 43
Registriert: Samstag 2. Mai 2020, 11:48

Hallo __deets__,

danke für die Antwort...

Ja, ich übergebe dort WHITE = (255, 255, 255) als color.

width und height als jeweils 10.

Kannst du mir vielleicht noch einen Hinweis geben (ich bin mit Python noch nicht so vertraut...)
Vielen Dank...
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mein Hinweis war, dass das nicht sein kann. Du zeigst nicht den Code, der das Problem hat. Dein Code ruft Sprite.__init__ ohne weiter Argumente auf, ausser self natuerlich. Aber die Zeile im sprite.py sieht nunmal so aus:

Code: Alles auswählen

class Sprite(object):
    """simple base class for visible game objects

    pygame.sprite.Sprite(*groups): return Sprite

    The base class for visible game objects. Derived classes will want to
    override the Sprite.update() method and assign Sprite.image and Sprite.rect
    attributes.  The initializer can accept any number of Group instances that
    the Sprite will become a member of.

    When subclassing the Sprite class, be sure to call the base initializer
    before adding the Sprite to Groups.

    """

    def __init__(self, *groups):
        self.__g = {} # The groups the sprite is in
        if groups:
            self.add(*groups)
Und der Fehler, den du bekommst, den bekommst du nur, wenn du etwas fuer groups uebergibst. Tust du aber laut deinem Code nicht. Das passt also nicht. Mehr kann man dazu auch nicht sagen.
Manul
User
Beiträge: 53
Registriert: Samstag 13. Februar 2021, 16:00

Könnte das Problem das fehlende "i" im Namen der __init__-Methode von Ball sein? Dadurch wird beim Anlegen der Instanz direkt das __init__ von Sprite aufgerufen und das kann mit den übergebenen Argumenten nichts anfangen.
kwon
User
Beiträge: 43
Registriert: Samstag 2. Mai 2020, 11:48

Hallo Manul,
danke, das hat sehr geholfen...

Jetzt habe ich leider einen neuen Fehler:

TypeError Paddle.update() missing 1 required positional argument: 'position'

in Zeile 110 von main übergebe ich event.pos

Bin für jeden Hinweis sehr dankbar...
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

@Manul: gut gesehen!

@kwon: Update wird aber auch über die Gruppe aufgerufen. Und kennt da kein Argument.
kwon
User
Beiträge: 43
Registriert: Samstag 2. Mai 2020, 11:48

Hallo __deets__, hallo Manul,

vielen Dank für eure wertvollen Hinweise!!

Jetzt läuft es...

Dankeschön...

Viele Grüße,

kwon
kwon
User
Beiträge: 43
Registriert: Samstag 2. Mai 2020, 11:48

Hallo,
2 Fragen hätte ich noch:

1) Kann man den Mauscursor irgendwie von der Position her beschränken (also dass der Cursor nur innerhalb des Pygame-Fensters bleibt)?
Leider konnte ich online dazu nichts finden...

2) Als Ball für das Breakout-Spiel habe ich einen Kreis gezeichnet (in einem Rechteck) - muss ich da als collider weiterhin einen rechteckigen nehmen?
Oder macht es Sinn einen runden zu nehmen?

Ich habe nämlich einen blöden Fehler, dass der Ball öfters 90° zu einer Fläche annimmt und dann nicht mehr zu erreichen ist...

Für den rechteckigen habe ich folgendes gemacht:

ein Codeauszug:
width und height ist 10

Kollision paddle <> ball

Code: Alles auswählen

if ball.rect.colliderect(paddle):

Code: Alles auswählen

class Ball(pygame.sprite.Sprite):
    def __init__(self, color, width, height):
        super().__init__()
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((width, height)).convert()
        self.image.fill(BLACK)
        self.image.set_colorkey(BLACK)

        pygame.draw.circle(self.image, color, (5,5),5)
        self.rect = self.image.get_rect()
        self.velocity = [randint(4, 8), randint(-8, 8)]

    def update(self):
        self.rect.x += self.velocity[0]
        self.rect.y += self.velocity[1]

    def bounce(self):
        self.velocity[0] = -self.velocity[0]
        self.velocity[1] = randint(-8, 8)
Ich bitte nochmals um Hilfe...
Vielen Dank...
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nein, man kann die Maus nicht beschraenken. Aber du kannst doch die Koordinaten des Schlaegers entsprechend einfach beschraenken. Kleiner als linke Kante und groesser als rechte Kante duerfen die eben nicht werden.

Problem ist dann nur, dass du mit deiner jetzigen Vorgehensweise Probleme bekommst, weil du ploetzlich eine weile links oder rechts weiter schieben kannst, ohne Bewegung. Und das dann zurueck erst wieder "ueberwinden" musst. Das will man ueblicherweise nicht.

Darum musst du stattdessen https://www.pygame.org/docs/ref/mouse.h ... se.get_rel benutzen, und damit deine Position einfach durch die relative Bewegung updaten.
kwon
User
Beiträge: 43
Registriert: Samstag 2. Mai 2020, 11:48

Hallo __deets__,

dankeschön...
Die Bewegung des Schlägers ist schon beschränkt (mit clamp)...

Das Letzte was bei einem schönen Spiel noch stört ist dass der Ball öfters 90° zu Flächen annimmt (er ist dann nicht mehr zu erreichen)...
Ich verwende einen rechteckigen Collider, sollte ich einen runden nehmen?
Bin für jeden Hinweis dankbar...
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Der collider ist da eher nebensächlich denke ich. Wieso kommt denn das zustande, du hast doch Zufall beim bounce?
kwon
User
Beiträge: 43
Registriert: Samstag 2. Mai 2020, 11:48

Ich weiß es leider nicht...
Naja, dann gebe ich mich damit zufrieden, dass es öfter auftritt...
Ich kann es mit meinem jetzigen Wissen nicht beheben...
Aber danke für die Hilfe... Einen schönen Abend...
Benutzeravatar
__blackjack__
User
Beiträge: 13080
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Naja, bei dem Zufall kann aber auch zufällig 0 bei heraus kommen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
kwon
User
Beiträge: 43
Registriert: Samstag 2. Mai 2020, 11:48

Code: Alles auswählen

    def bounce(self):
        self.velocity[0] = -self.velocity[0]
        a = randint(-8, 8)
        if a != 0:
            self.velocity[1] = a
        else:
            self.velocity[1] = 1
Das ist mir eben eingefallen...

Oder ist das schlecht?
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn es das Problem löst, ist das schon ok.
Antworten