Python (pygame) 碰撞检测 - 精灵与组

1 投票
2 回答
1351 浏览
提问于 2025-04-18 16:35

我刚开始学Python,正在尝试写一个游戏。在这个游戏里,角色会被发射出去,当他和地面上的一个精灵互动时,会发生一些变化,比如速度会改变。抱歉我的代码有点乱。我从几个教程中拿了一些示例,但我无法把它们结合在一起。

我该如何检测玩家和炸弹的碰撞呢?

import pygame
import random
import math

drag = 1
gravity = (math.pi, .4)
elasticity = .75

# Colors
BLACK    = (   0,   0,   0)
WHITE    = ( 255, 255, 255)
BLUE     = (   0,   0, 255)
RED      = ( 255,   0,   0)
GREEN    = (   0, 255,   0)

# Screen dimensions
SCREEN_WIDTH  = 800
SCREEN_HEIGHT = 600

def addVectors((angle1, length1), (angle2, length2)):
    x = math.sin(angle1) * length1 + math.sin(angle2) * length2
    y = math.cos(angle1) * length1 + math.cos(angle2) * length2

    angle = 0.5 * math.pi - math.atan2(y, x)
    length = math.hypot(x, y)

    return (angle, length)

class Player(pygame.sprite.Sprite):

    change_x = 0
    change_y = 0

    level = None

    def __init__(self, x, y):

        pygame.sprite.Sprite.__init__(self)

        self.image = pygame.image.load('player.png')
        image_rect = self.image.get_rect()
        self.rect = pygame.rect.Rect(x, y, image_rect.width, image_rect.height)

    def update(self):
        pass

    def move(self):
        (self.angle, self.speed) = addVectors((self.angle, self.speed), gravity)
        self.rect.x += math.sin(self.angle) * self.speed
        self.rect.y -= math.cos(self.angle) * self.speed
        self.speed *= drag



    def bounce(self):
        if self.rect.x > 800 - self.rect.width:
            self.rect.x = 2*(800 - self.rect.width) - self.rect.x
            self.angle = - self.angle
            self.speed *= elasticity

        elif self.rect.x < 0:
            self.rect.x = 2*self.rect.width - self.rect.x
            self.angle = - self.angle
            self.speed *= elasticity

        if self.rect.y > SCREEN_HEIGHT - self.rect.height:
            self.rect.y = 2*(SCREEN_HEIGHT - self.rect.height) - self.rect.y
            self.angle = math.pi - self.angle
            self.speed *= elasticity

class Bomb(pygame.sprite.Sprite):

    def __init__(self, x, y):

        pygame.sprite.Sprite.__init__(self)

        self.image = pygame.image.load('cherry.png')
        image_rect = self.image.get_rect()
        self.rect = pygame.rect.Rect(x, y, image_rect.width, image_rect.height)


class Level():

    bomb_list = None
    world_shift = 0

    def __init__(self, player):
        self.bomb_list = pygame.sprite.Group()
        self.player = player

    def update(self):
        self.bomb_list.update()

    def draw(self, screen):
        screen.fill(BLACK)

        self.bomb_list.draw(screen)

    def shift_world(self, shift_x):

        self.world_shift += shift_x

        for bomb in self.bomb_list:
            bomb.rect.x += shift_x

            if bomb.rect.x < 0:
                self.bomb_list.remove(bomb)
                self.bomb_list.add(Bomb(random.randint(SCREEN_WIDTH, 2*SCREEN_WIDTH), 580)) 

我不确定这个Level_01类是否真的有必要:

class Level_01(Level):

    def __init__(self, player):

        Level.__init__(self, player)

        bombs = 0
        for n in range(10):
            self.bomb_list.add(Bomb(random.randint(0, SCREEN_WIDTH), 580))

这是我尝试写的碰撞检测方法。我不确定这个方法应该放在一个类里、主程序里,还是单独放。我似乎无法同时获取炸弹的列表和玩家的信息。

def detectCollisions(sprite1, sprite_group):
    if pygame.sprite.spritecollideany(sprite1, sprite_group):
        sprite_group.remove(pygame.sprite.spritecollideany(sprite1, sprite_group))
        print True
    else:
        print False


def main():

    pygame.init()

    size = [SCREEN_WIDTH, SCREEN_HEIGHT]
    screen = pygame.display.set_mode(size)


    active_sprite_list = pygame.sprite.Group()
    enemy_list = pygame.sprite.Group()

    player = Player(0, 0)
    player.speed = 30
    player.angle = math.radians(45)

    player.rect.x = 500
    player.rect.y = SCREEN_HEIGHT - player.rect.height
    active_sprite_list.add(player)

    done = False

    clock = pygame.time.Clock()

    current_level = Level_01(player)

    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_LEFT:
                    player.go_left()
                if event.key == pygame.K_RIGHT:
                    player.go_right()           

        active_sprite_list.update()
        enemy_list.update()
        player.level = current_level
        player.move()
        player.bounce()

        if player.rect.x >= 500:
            diff = player.rect.x - 500
            player.rect.x = 500
            current_level.shift_world(-diff)

        if player.rect.x <= 120:
            diff = 120 - player.rect.x
            player.rect.x = 120
            current_level.shift_world(diff)

        current_level.draw(screen)
        active_sprite_list.draw(screen)
        enemy_list.draw(screen)

        clock.tick(60)

        pygame.display.flip()

    pygame.quit()

if __name__ == "__main__":
    main()

谢谢你们的帮助!

2 个回答

0

我建议为每个类创建精灵组,使用 pygame.sprite.Group(),比如 Bomb(炸弹)和 Player(玩家)。然后,可以使用 pygame.sprite.spritecollide() 来检测碰撞。

举个例子:

def Main()   
    ...
    player_list = pygame.sprite.Group()
    bomb_list = pygame.sprite.Group()
    ...

然后在你的逻辑处理循环中,创建一个 PlayerBomb 实例后,你可以这样做:

for bomb in bomb_list:
    # See if the bombs has collided with the player.
        bomb_hit_list = pygame.sprite.spritecollide(bomb, player_list, True)

        # For each bomb hit, remove bomb 
        for bomb in bomb_hit_list:
            bomb_list.remove(bomb)
            # --- Put code here that will trigger with each collision ---
0

最简单的方法就是画出像素(或者虚拟像素),如果在画炸弹或人物的像素时发现它们重叠了,那就说明发生了碰撞。

不过,如果你需要更高效的碰撞检测方法,可以让这个过程变得更复杂。可以参考维基百科上的碰撞检测内容。

撰写回答