Collisions aren't being detected in pygame

…衆ロ難τιáo~ 提交于 2021-02-11 12:12:06

问题


I don't know why but collisions aren't being detected in my game. Its an asteroids style game and I want the bullets to destroy the asteroids, and the game to end when the ship gets hit by one, but they're passing through each other without doing any of that.

Here's my code:

import pygame
from math import sin, cos, pi

from random import randint

scr_width = 800
scr_height = 600
window = pygame.display.set_mode((scr_width, scr_height))
pygame.display.set_caption("Asteroids")
clock = pygame.time.Clock()
space_img = pygame.image.load("sprites/space.jpg")
red = (255, 0, 0)

class Ship:
    def __init__(self, x, y):

        self.x = x
        self.y = y
        self.width = 0
        self.vel = 0
        self.vel_max = 12
        self.angle = 0
        self.hitbox = (self.x, self.y, 10, 10)
        self.ship_img = pygame.image.load("sprites/ship_off.png")
        self.ship_img_copy = pygame.transform.rotate(self.ship_img, self.angle)

    def draw(self):
        self.ship_img = pygame.image.load("sprites/ship_off.png")
        self.ship_img_copy = pygame.transform.rotate(self.ship_img, self.angle)

        window.blit(self.ship_img_copy,
                    (self.x - (self.ship_img_copy.get_width()) / 2, self.y - (self.ship_img_copy.get_height()) / 2))

        keys = pygame.key.get_pressed()

        if keys[pygame.K_w]:
            self.ship_img = pygame.image.load("sprites/ship_on.png")
            self.ship_img_copy = pygame.transform.rotate(self.ship_img, self.angle)
            window.blit(self.ship_img_copy,
                        (self.x - (self.ship_img_copy.get_width()) / 2, self.y - (self.ship_img_copy.get_height()) / 2))
# collision stuff
        self.mask = pygame.mask.from_surface(self.ship_img_copy)
        self.rect = pygame.Rect(self.x - (self.ship_img_copy.get_width()) / 2,
                                self.y - (self.ship_img_copy.get_height()) / 2,
                                self.ship_img_copy.get_width(), self.ship_img_copy.get_height())

    def move(self):
        keys = pygame.key.get_pressed()
        # todo acceleration and thrust mechanics
        if keys[pygame.K_w]:
            self.vel = min(self.vel + 1, self.vel_max)
        elif self.vel > 0:
            self.vel = self.vel - 0.4
        if keys[pygame.K_a]:
            self.angle += 7

        if keys[pygame.K_d]:
            self.angle -= 7

        self.x += self.vel * cos(self.angle * (pi / 180) + (90 * pi / 180))
        self.y -= self.vel * sin(self.angle * (pi / 180) + 90 * (pi / 180))
        # So that if it leaves one side it comes from the other
        if self.y < 0:
            self.y = (self.y - self.vel) % 600

        elif self.y > 600:
            self.y = (self.y + self.vel) % 600

        elif self.x < 0:
            self.x = (self.x - self.vel) % 800

        elif self.x > 800:
            self.x = (self.x + self.vel) % 800


class Asteroid:

    def __init__(self):

        self.ang_change = randint(1, 5)
        self.ang = randint(0, 90) * (pi / 180)
        y_values = [1, 599]
        self.sx = randint(0, 800)
        self.sy = y_values[randint(0, 1)]
        # If object spawns from the top, it moves down instead of moving up and de-spawning immediately
        if self.sy == y_values[0]:
            self.neg = -1
        else:
            self.neg = 1
        self.speed = randint(5, 10)
        self.ang += self.ang_change
        self.asteroid_angle = randint(0, 80)
        self.asteroid_img = pygame.image.load("sprites/asteroid.png")
        self.asteroid_copy = pygame.transform.rotate(self.asteroid_img, self.ang)

    def generate(self):
        self.ang += self.ang_change
        self.asteroid_img = pygame.image.load("sprites/asteroid.png")
        self.asteroid_copy = pygame.transform.rotate(self.asteroid_img, self.ang)

        window.blit(self.asteroid_copy,
                    (self.sx - (self.asteroid_copy.get_width()) / 2, self.sy - (self.asteroid_copy.get_height()) / 2))
# collision stuff
        self.mask = pygame.mask.from_surface(self.asteroid_copy)
        self.rect = pygame.Rect(self.sx - (self.asteroid_copy.get_width()) / 2,
                                self.sy - (self.asteroid_copy.get_height()) / 2,
                                self.asteroid_copy.get_width(), self.asteroid_copy.get_height())


class Projectiles:

    def __init__(self, x, y, angle):
        self.x = x
        self.y = y
        self.angle = angle
        self.vel = 20
        self.bullet_body = pygame.image.load("sprites/bullet.png")

    def draw(self):
        self.bullet_body = pygame.image.load("sprites/bullet.png")
# collision stuff
        self.rect = pygame.Rect(self.x - (self.bullet_body.get_width()) / 2,
                                self.y - (self.bullet_body.get_height()) / 2, 5, 5)
        window.blit(self.bullet_body, (self.x - 2, self.y))
        self.mask = pygame.mask.from_surface(self.bullet_body)


def redraw():
    window.blit(space_img, (0, 0))
    ship.draw()
    for asteroid in asteroids:
        asteroid.generate()
    for bullet in bullets:
        bullet.draw()

    pygame.display.update()

# collision stuff
def collisions():
    asteroids = pygame.sprite.Group()
    bullets = pygame.sprite.Group()
    if pygame.sprite.spritecollide(ship, asteroids, True, pygame.sprite.collide_mask):
        print("hit")
        pygame.quit()
    pygame.sprite.groupcollide(asteroids, bullets, True, True, pygame.sprite.collide_mask)


# main loop
run = True
ship = Ship(400, 300)
next_fire = pygame.time.get_ticks() + 400
bullets = []
asteroids = []

while run:
    clock.tick(60)
    keys = pygame.key.get_pressed()
    pygame.time.delay(35)
# collision stuff
    collisions()
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    if keys[pygame.K_SPACE]:
        if len(bullets) < 11 and pygame.time.get_ticks() >= next_fire:
            bullets.append(
                Projectiles(round(ship.x + ship.width - 6.5 // 2), round(ship.y + ship.width - 6.5 // 2), ship.angle))
            next_fire = pygame.time.get_ticks() + 400

    for bullet in bullets:
        if 800 > bullet.x > 0 and 600 > bullet.y > 0:
            bullet.x += bullet.vel * cos(bullet.angle * (pi / 180) + 90 * (pi / 180))
            bullet.y -= bullet.vel * sin(bullet.angle * (pi / 180) + 90 * (pi / 180))
        else:
            bullets.pop(bullets.index(bullet))
    # To limit the number of asteroids on screen
    if len(asteroids) < 5:
        asteroids.append(Asteroid())

    for asteroid in asteroids:
        if 805 > asteroid.sx > 0 and 605 > asteroid.sy > 0:
            asteroid.sx += asteroid.speed * cos(asteroid.asteroid_angle * (pi / 180) + 90 * (pi / 180))
            asteroid.sy -= asteroid.speed * sin(asteroid.asteroid_angle * (pi / 180) + 90 * (pi / 180)) * asteroid.neg
            if asteroid.sx < 0:
                asteroid.sx = (asteroid.sx - asteroid.speed) % 805

            elif asteroid.sx > 805:
                asteroid.sx = (asteroid.sx + asteroid.speed) % 805

        else:
            asteroids.pop(asteroids.index(asteroid))

    window.fill((0, 0, 0))
    ship.move()
    redraw()

pygame.quit()

Is it that I can't make a seperate function for collisions, or have I missed something else?


回答1:


I couldn't test code but usually problem is that collision's function use values from self.rect but you don't use them but self.x and self.y to keep position.

You should do in __init__

self.rect = self.image.get_rect()

self.rect.center = (x, y)

and later you can add to self.rect.x and self.rect.y and it will automatically calculate position for

blit(self.image, self.rect)

and for collision with other object

self.rect.colliderect(other.rect)

or with mouse in event.MOUSEBUTTONDOWN

self.rect.collidepoint(event.pos)

to test if object was clicked (ie. button).


If you want to use pygame Group

asteroids = pygame.sprite.Group()
bullets = pygame.sprite.Group()

then you shouldn't create them inside collisions() but at start instead of lists

asteroids = []
bullets = []

and you should add objects to groups instead of appending to lists.

asteroids.add(Asteroid())

and you can even run function for all objects without using for-loop

asteroids.draw()


来源:https://stackoverflow.com/questions/63426550/collisions-arent-being-detected-in-pygame

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!