You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

关于在Pygame中实现子弹精灵与水泥块精灵碰撞检测及对应交互逻辑的技术问询

解决Pygame中子弹与水泥块的碰撞检测问题

我来帮你实现这三个碰撞需求,咱们一步步修改现有代码:

1. 先完善水泥块(Debris)类的属性

首先给Debris类添加受击计数,并且同步它的rect位置(这样才能准确做碰撞检测):

修改debris.py

import time
import pygame
import random

#screen height & width
WIDTH = 1000
HEIGHT = 400
screen = pygame.display.set_mode((WIDTH, HEIGHT))

#debris class
class Debris(pygame.sprite.Sprite):
    def __init__(self, scale, speed):
        pygame.sprite.Sprite.__init__(self)
        self.x = 400
        self.y = HEIGHT / 2 - 200
        self.speed = speed
        self.vy = 0
        self.on_ground = True
        self.move = True
        # 添加受击计数,初始为0
        self.hit_count = 0

        #load debris
        self.images = []
        img = pygame.image.load('debris/cement.png').convert_alpha()
        img = pygame.transform.scale(img, (int(img.get_width()) * scale, (int(img.get_height()) * scale)))
        self.images.append(img)
        self.image = self.images[0]
        self.rect = self.image.get_rect()
        # 初始化rect位置
        self.rect.topleft = (self.x, self.y)

    #draw debris to screen
    def draw(self):
        # 每次绘制时同步rect位置,确保和实际显示位置一致
        self.rect.topleft = (self.x, self.y)
        screen.blit(self.image,(self.x,self.y))

2. 改造子弹的存储与碰撞逻辑

原来的子弹用列表存坐标,没法标记是否停止,咱们改成存字典,每个子弹包含位置和激活状态;然后实现碰撞检测逻辑:

修改car.py

import pygame
from debris import Debris
from autopilot import debris_group
from autopilot import car_group

#screen height & width
WIDTH = 1000
HEIGHT = 400
screen = pygame.display.set_mode((WIDTH,HEIGHT))

#car class
class Car(pygame.sprite.Sprite):
    def __init__(self, scale, speed):
        pygame.sprite.Sprite.__init__(self)
        #load bullets
        self.vel = 5
        # 改造子弹列表:每个元素是字典,包含位置和是否激活(是否在移动)
        self.bullet_list = [] 
        self.bullet = pygame.image.load('car/bullet.png').convert_alpha()
        self.bullet_rect = self.bullet.get_rect()
        self.speed = speed
        self.moving = True
        self.frame = 0
        self.flip = False
        self.direction = 0

        #load car
        self.images = []
        img = pygame.image.load('car/car.png').convert_alpha()
        img = pygame.transform.scale(img, (int(img.get_width()) * scale, (int(img.get_height()) * scale)))
        self.images.append(img)
        self.image = self.images[0]
        self.rect = self.image.get_rect()
        self.update_time = pygame.time.get_ticks()
        self.movingLeft = False
        self.movingRight = False
        self.rect.x = 465
        self.rect.y = 325

    #draw car to screen
    def draw(self):
        # 绘制子弹,停止的子弹也保留显示
        for bullet in self.bullet_list:
            screen.blit(self.bullet, bullet["pos"])
        screen.blit(self.image, (self.rect.centerx, self.rect.centery))

    #move car
    def move(self):
        #reset the movement variables
        dx = 0
        dy = 0

        # moving variables
        if self.movingLeft and self.rect.x > 33:
            dx -= self.speed
            self.flip = True
            self.direction = -1
        if self.movingRight and self.rect.x < 900:
            dx += self.speed
            self.flip = False
            self.direction = 1

        #update rectangle position
        self.rect.x += dx
        self.rect.y += dy

    #shoot bullets
    def shoot(self):
        # 添加子弹时,默认激活(移动状态)
        self.bullet_list.append({
            "pos": [self.rect.centerx + 14.50,self.rect.centery],
            "active": True
        })

    #update bullet travel
    def bullet_update(self):
        for bullet in self.bullet_list[:]:
            # 只有激活的子弹才移动
            if bullet["active"]:
                bullet["pos"][1] -= self.vel
            # 子弹超出屏幕顶部移除
            if bullet["pos"][1] < -self.bullet_rect.height:
                self.bullet_list.remove(bullet)

    #check collision
    def collision(self):
        # 遍历所有子弹和水泥块
        for bullet in self.bullet_list[:]:
            # 跳过已经停止的子弹
            if not bullet["active"]:
                continue
            # 创建子弹的rect用于碰撞检测
            bullet_rect = self.bullet.get_rect()
            bullet_rect.topleft = (bullet["pos"][0], bullet["pos"][1])
            
            for debris in debris_group:
                # 检测子弹和水泥块的碰撞
                if bullet_rect.colliderect(debris.rect):
                    # 判断是否击中水泥块底部:子弹顶部 <= 水泥块底部,且子弹底部 > 水泥块底部
                    # 数值可根据你的精灵图片大小微调
                    if bullet_rect.top <= debris.rect.bottom and bullet_rect.bottom > debris.rect.bottom:
                        # 子弹停止运动
                        bullet["active"] = False
                        # 水泥块受击计数+1
                        debris.hit_count += 1
                        # 受击4次后移除水泥块
                        if debris.hit_count >=4:
                            debris_group.remove(debris)
                    # 其他方向碰撞(侧面/顶部),子弹直接消失(满足不能穿透要求)
                    else:
                        self.bullet_list.remove(bullet)
                    # 一个子弹只碰撞一个水泥块,跳出循环
                    break

3. 调整主循环的绘制逻辑

修改autopilot.py里的水泥块绘制方式,用sprite组的draw方法,这样移除水泥块后会自动停止绘制:

# ... 其他代码不变 ...
#game runs here
run = True
while run:
    #draw street
    screen.blit(bg,[0,0])

    #update groups
    car_group.update()
    debris_group.update() # 添加这行,更新水泥块组

    #draw debris
    # debris.draw() 替换成下面的行
    debris_group.draw(screen)

    #draw car
    car.draw()
    car.move()

    #update bullets
    car.bullet_update()
    car.collision()

# ... 其他代码不变 ...

逻辑说明

  • 子弹不穿透:只要子弹和水泥块发生非底部碰撞,就直接移除子弹;击中底部则标记子弹为停止状态,不再移动。
  • 击中底部子弹停止:通过判断子弹rect与水泥块rect的位置关系,标记子弹为非激活状态,后续不再更新位置。
  • 水泥块4次击中消失:给每个水泥块添加hit_count属性,每次击中底部就+1,达到4次后从sprite组移除,自动停止绘制。

内容的提问来源于stack exchange,提问作者J.R. BEATS

火山引擎 最新活动