问题
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