Pygame wall collision

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
ibrahim00
User
Beiträge: 5
Registriert: Montag 31. August 2015, 10:10

Hey Leute,

Ich bin ein Einsteiger was die Spiele Programmierung in Pygame angeht. Ich habe folgendes test-script geschrieben, mit dem ich herausfinden wollte, wie man denn die collsion detection richtig in Verbindung mit Gegenständen, Steinen, Bäumen etc.. verwendet.

Code: Alles auswählen

import pygame
import sys

white = (255, 255, 255)
black = (  0,   0,   0)


# Player class
class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load('../foo.png')
        self.rect = self.image.get_rect()


class Rock(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load('../rock.png')
        self.rect = self.image.get_rect()
        self.rect.x = 50
        self.rect.y = 50

# essential pygame init
pygame.init()

# screen
screen_width = 400
screen_height = 400
screen_size = (screen_width, screen_height)
screen = pygame.display.set_mode(screen_size)

# List for all sprites
sprites = pygame.sprite.Group()


# Rock
rock = Rock()
sprites.add(rock)

# Create player
player = Player()
sprites.add(player)

done = False

clock = pygame.time.Clock()
while not done:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
            sys.exit()

    sprites.update()

    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_LEFT] and not player.rect.colliderect(rock.rect):
        #if not player.rect.colliderect(rock.rect):
        move = (-1, 0)
        player.rect = player.rect.move(move)
    if pressed[pygame.K_RIGHT] and not player.rect.colliderect(rock.rect):
        move = (1, 0)
        player.rect = player.rect.move(move)
    if pressed[pygame.K_UP] and not player.rect.colliderect(rock.rect):
        move = (0, -1)
        player.rect = player.rect.move(move)
    if pressed[pygame.K_DOWN] and not player.rect.colliderect(rock.rect):
        move = (0, 1)
        player.rect = player.rect.move(move)

    screen.fill(white)

    sprites.draw(screen)

    pygame.display.flip()

    clock.tick(30)
Die Kollision funktioniert :lol:, aber leider geht es dann auch nicht mehr weiter nach dem man kollidiert ist. Ich denke mal das liegt einfach daran, dass ich mich die ganze Zeit bewege, irgendwann kollidiere und dann, weil ich nach einer Kollision frage und es wahr ist, dass ich schon längst in dem Stein stecke, ich nicht mehr raus komme. Wie kann ich mir jetzt nun aber colliderect zu Nutzen machen oder ist das eine schlechte Vorangehensweise? Hat jemand von euch Tipps für mich? :D
BlackJack

@ibrahim00: Du müsstest erst das `Rect` verschieben, beispielsweise mit `move()` einen neuen Wert erstellen und den *nicht* gleich an das `rect`-Attribut binden, damit den Kollisionstest machen und das erst dem `rect`-Attribut zuweisen wenn keine Kollision vorliegt.

Noch ein paar Anmerkungen: Konstantennamen schreibt man laut Style Guide for Python Code komplett in Grossbuchstaben. Also `WHITE` statt `white`. (`black` wird übrigens nirgends verwendet.)

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Kommentare die dem Leser keinen Mehrwert liefern sind überflüssig. Vor der Definition der Klasse `Player` braucht man kein ``# Player class``. Im Grunde sind alle Kommentare in dem gezeigten Quelltext sinnfrei weil sie nichts aussagen was man nicht auch am dazugehörigen Quelltext einfach ablesen kann.

`done` ist überflüssig weil der Wert gesetzt, aber nie verändert oder sinnvoll abgefragt wird.

Nach `sys.exit()` wird nichts mehr ausgeführt.

`pygame.key.get_pressed()` sollte man nicht verwenden. Da steht in der Dokumentation auch ein Hinweis dass man damit Tastendrücke ”verpassen” kann. Man sollte stattdessen die Ereignisse behandeln.

Wenn man von `Sprite` erbt sollte man auch dessen Signatur der `__init__()` übernehmen. Das kann man hier sogar sinnvoll einsetzen das die Basisklasse `SpriteGroup`-Exemplare beim erstellen entgegen nehmen kann.

Ungetestet:

Code: Alles auswählen

import sys

import pygame
from pygame.sprite import Sprite

WHITE = pygame.Color('white')


class Player(Sprite):

    def __init__(self, *groups):
        Sprite.__init__(self, *groups)
        self.image = pygame.image.load('../foo.png')
        self.rect = self.image.get_rect()
        self.x_speed = self.y_speed = 0

    def update(self, obstacle, *_args):
        new_rect = self.rect.move(self.x_speed, self.y_speed)
        if not new_rect.colliderect(obstacle.rect):
            self.rect = new_rect


class Rock(Sprite):

    def __init__(self, *groups):
        Sprite.__init__(self, *groups)
        self.image = pygame.image.load('../rock.png')
        self.rect = self.image.get_rect().move(50, 50)


def main():
    pygame.init()
    screen = pygame.display.set_mode((400, 400))
    sprites = pygame.sprite.Group()
    rock = Rock(sprites)
    player = Player(sprites)
    clock = pygame.time.Clock()
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    player.x_speed = -1
                elif event.key == pygame.K_RIGHT:
                    player.x_speed = 1
                elif event.key == pygame.K_UP:
                    player.y_speed = -1
                elif event.key == pygame.K_DOWN:
                    player.y_speed = 1
            
            elif event.type == pygame.KEYUP:
                if event.key in [pygame.K_LEFT, pygame.K_RIGHT]:
                    player.x_speed = 0
                elif event.key in [pygame.K_UP, pygame.K_DOWN]:
                    player.y_speed = 0

        player.update(rock)
        screen.fill(WHITE)
        sprites.draw(screen)
        pygame.display.flip()
        clock.tick(30)


if __name__ == '__main__':
    main()
ibrahim00
User
Beiträge: 5
Registriert: Montag 31. August 2015, 10:10

Alter Schwede :shock:

Vielen vielen Dank für die Antwort BlackJack!!! Die Erklärung ist sehr gut und danke dass du mir den Style Guide gegeben hast. Ich frage mich warum ich nicht von selbst darauf gekommen bin. Da saß ich schon 3 Tage dran.. :oops: :lol: . Super nett, dass du dir die Zeit genommen hast und ich hoffe du hast noch einen schönen Tag :D

Mfg Ibrahim

Edit: Kann ich deine Antwort irgendwie upvoten oder so? Oder ist so ein System hier nicht implementiert?
asaga
User
Beiträge: 2
Registriert: Freitag 16. August 2019, 15:44

Wie sieht es aus, wenn man ein drittes objekt hat, z.b Holz

import sys

import pygame
from pygame.sprite import Sprite

WHITE = pygame.Color('white')


class Player(Sprite):

def __init__(self, *groups):
Sprite.__init__(self, *groups)
self.image = pygame.image.load('foo.png')
self.rect = self.image.get_rect()
self.x_speed = self.y_speed = 0

def update(self, obstacle, *_args):
new_rect = self.rect.move(self.x_speed, self.y_speed)
if not new_rect.colliderect(obstacle.rect):
self.rect = new_rect


class Rock(Sprite):

def __init__(self, *groups):
Sprite.__init__(self, *groups)
self.image = pygame.image.load('rock.png')
self.rect = self.image.get_rect().move(50, 50)

class Holz(Sprite):

def __init__(self, *groups):
Sprite.__init__(self, *groups)
self.image = pygame.image.load('Holz.png')
self.rect = self.image.get_rect().move(150, 150)


def main():
pygame.init()
screen = pygame.display.set_mode((400, 400))
sprites = pygame.sprite.Group()
rock = Rock(sprites)
holz = Holz(sprites)
player = Player(sprites)
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()

elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player.x_speed = -3
elif event.key == pygame.K_RIGHT:
player.x_speed = 3
elif event.key == pygame.K_UP:
player.y_speed = -3
elif event.key == pygame.K_DOWN:
player.y_speed = 3

elif event.type == pygame.KEYUP:
if event.key in [pygame.K_LEFT, pygame.K_RIGHT]:
player.x_speed = 0
elif event.key in [pygame.K_UP, pygame.K_DOWN]:
player.y_speed = 0

player.update(rock)
screen.fill(WHITE)
sprites.draw(screen)
pygame.display.flip()
clock.tick(30)


if __name__ == '__main__':
main()
Antworten