You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pygame太空射击游戏碰撞检测失效问题求助

太空射击游戏小行星碰撞检测失效问题

我正在开发一款需要躲避并射击小行星的太空射击游戏,调试小行星碰撞检测功能时发现,测试用的单个小行星会直接穿过飞船,完全不触发游戏结束逻辑。试过多种colliderect函数的用法,问题依然存在,相关代码如下:

import pygame
pygame.init()

#initalizing all the clunky variables
size = (900,700)
BLACK = (0, 0, 30)
RED = (255, 0, 0)
YELLOW = (0, 255, 0)
x_pos = 450
y_pos = 600
global x_pos
global y_pos
direct = 0
w, h = 100, 100
screen = pygame.display.set_mode(size)
klok = pygame.time.Clock()

#main ship image and its rotations
ship = pygame.image.load('u-sniper.png')
shipL = pygame.transform.rotate(ship, 270)
shipR = pygame.transform.rotate(ship, 90)
shipD = pygame.transform.rotate(ship, 180)
#init hitbox
hitbox = ship.get_rect()
hitbox.center = w//2,h//2

#funct for drawing ship
def drawShip():
    if direct == 0:
        screen.blit(ship, [x_pos,y_pos])
    if direct == 1:
        screen.blit(shipR, [x_pos,y_pos])
    if direct == 2:
        screen.blit(shipD, [x_pos,y_pos])
    if direct == 3:
        screen.blit(shipL, [x_pos,y_pos])

#asteroid obstacles (these are meant to collide with the ship)
class asteroid:
    def __init__(self,x,y,spawn):
        self.x = x
        self.y = y
        self.spawn = spawn

    def drawA(self):
        if self.spawn == 1:
            pygame.draw.circle(screen, RED, (self.x,self.y), 30)

    def moveA(self):
        self.y += 8
        if self.y > 650:
            self.spawn = 0

done = False
roid = asteroid(450,0,1)
#asteroid hitbox init
rect_asteroid = (roid.x, roid.y, 30, 30)

#here is where its going wrong, collision dosent register
def checkForCollisions():
    
    collide = pygame.Rect.colliderect(hitbox,rect_asteroid)
    if collide == True:
        done = True
#loop
while not done:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
    screen.fill(BLACK)
    drawShip()
    roid.drawA()
    roid.moveA()
    #calling fuction, but it dosent work
    checkForCollisions()
    #if branch that moves the ship
    if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_UP:
            y_pos -= 5
            direct = 0
        if event.key == pygame.K_DOWN:
            y_pos += 5
            direct = 2
        if event.key == pygame.K_RIGHT:
            x_pos += 5
            direct = 3
        if event.key == pygame.K_LEFT:
            x_pos -= 5
            direct = 1
    
#collision between screen boundaries
    if x_pos > 850:
        x_pos -= 6
    if x_pos < -50:
        x_pos += 6
    if y_pos > 650:
        y_pos -= 6
    if y_pos < 0:
        y_pos += 6

    pygame.display.flip()
    klok.tick(60)
pygame.quit()

问题根源

  • 飞船碰撞盒未同步位置:初始化hitbox后,飞船移动时(修改x_pos/y_pos)未更新hitbox的位置,导致碰撞盒始终停留在初始坐标,完全不跟随飞船实际位置。
  • 小行星碰撞盒未实时更新:rect_asteroid仅在初始化时赋值一次,后续小行星移动(roid.y变化)后,碰撞盒位置未同步更新,永远停留在初始状态。
  • done变量作用域错误:checkForCollisions函数内直接赋值done = True,但未声明global done,实际修改的是局部变量,无法终止主循环。
  • 键盘事件处理逻辑错误:按键判断放在事件循环外部,仅会处理最后一个事件,甚至可能出现event未定义的情况。

修复后的代码

import pygame
pygame.init()

# 初始化变量
size = (900,700)
BLACK = (0, 0, 30)
RED = (255, 0, 0)
YELLOW = (0, 255, 0)
x_pos = 450
y_pos = 600
direct = 0
w, h = 100, 100
screen = pygame.display.set_mode(size)
klok = pygame.time.Clock()

# 飞船图像及旋转版本
ship = pygame.image.load('u-sniper.png')
shipL = pygame.transform.rotate(ship, 270)
shipR = pygame.transform.rotate(ship, 90)
shipD = pygame.transform.rotate(ship, 180)
# 飞船碰撞盒,初始位置与飞船同步
hitbox = ship.get_rect(topleft=(x_pos, y_pos))

# 绘制飞船函数,同步碰撞盒位置
def drawShip():
    global hitbox
    if direct == 0:
        screen.blit(ship, [x_pos,y_pos])
        hitbox = ship.get_rect(topleft=(x_pos, y_pos))
    if direct == 1:
        screen.blit(shipR, [x_pos,y_pos])
        hitbox = shipR.get_rect(topleft=(x_pos, y_pos))
    if direct == 2:
        screen.blit(shipD, [x_pos,y_pos])
        hitbox = shipD.get_rect(topleft=(x_pos, y_pos))
    if direct == 3:
        screen.blit(shipL, [x_pos,y_pos])
        hitbox = shipL.get_rect(topleft=(x_pos, y_pos))

# 小行星类,内置实时碰撞盒
class asteroid:
    def __init__(self,x,y,spawn):
        self.x = x
        self.y = y
        self.spawn = spawn
        # 碰撞盒覆盖整个半径30的圆
        self.rect = pygame.Rect(self.x - 30, self.y - 30, 60, 60)

    def drawA(self):
        if self.spawn == 1:
            pygame.draw.circle(screen, RED, (self.x,self.y), 30)

    def moveA(self):
        if self.spawn == 1:
            self.y += 8
            # 移动时同步碰撞盒中心位置
            self.rect.center = (self.x, self.y)
            if self.y > 650:
                self.spawn = 0

done = False
roid = asteroid(450,0,1)

# 碰撞检测函数,修正变量作用域
def checkForCollisions():
    global done
    # 用实时碰撞盒检测,且仅在小行星激活时判断
    if roid.spawn == 1 and hitbox.colliderect(roid.rect):
        done = True

# 主循环
while not done:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
        # 键盘事件移到事件循环内部处理
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                y_pos -= 5
                direct = 0
            if event.key == pygame.K_DOWN:
                y_pos += 5
                direct = 2
            if event.key == pygame.K_RIGHT:
                x_pos += 5
                direct = 3
            if event.key == pygame.K_LEFT:
                x_pos -= 5
                direct = 1

    screen.fill(BLACK)
    drawShip()
    roid.drawA()
    roid.moveA()
    checkForCollisions()

    # 屏幕边界碰撞处理
    if x_pos > 850:
        x_pos -= 6
    if x_pos < -50:
        x_pos += 6
    if y_pos > 650:
        y_pos -= 6
    if y_pos < 0:
        y_pos += 6

    pygame.display.flip()
    klok.tick(60)

pygame.quit()

修复说明

  1. 同步碰撞盒位置:每次绘制飞船时,根据当前飞船的位置和朝向更新碰撞盒;小行星移动时同步更新自身碰撞盒的中心坐标。
  2. 修正碰撞盒范围:小行星是半径30的圆,对应的碰撞矩形设为60x60,确保覆盖整个圆形区域,避免碰撞检测遗漏。
  3. 修复变量作用域:在checkForCollisions函数中声明global done,确保修改的是全局变量,能正确终止主循环。
  4. 调整事件处理逻辑:将键盘事件处理移到for event in pygame.event.get()循环内部,确保所有按键事件都能被正确响应。

内容的提问来源于stack exchange,提问作者user20759990

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 19:35:34