pygame中的碰撞检测

3

我写了一个小测试来感受pygame中的碰撞检测。我的玩家可以正常移动并停在岩石上,也就是碰撞发生的地方,但是当我到达岩石后就无法再移开了。你们知道这是为什么吗?下面是我的测试代码:

import pygame
import sys

white = (255, 255, 255)
black = (  0,   0,   0)


# Player class
class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load('../foo.png')
        self.rect = self.image.get_rect()


class Rock(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load('../rock.png')
        self.rect = self.image.get_rect()
        self.rect.x = 50
        self.rect.y = 50

# essential pygame init
pygame.init()

# screen
screen_width = 400
screen_height = 400
screen_size = (screen_width, screen_height)
screen = pygame.display.set_mode(screen_size)

# List for all sprites
sprites = pygame.sprite.Group()


# Rock
rock = Rock()
sprites.add(rock)

# Create player
player = Player()
sprites.add(player)

done = False

clock = pygame.time.Clock()
while not done:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
            sys.exit()

    sprites.update()

    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_LEFT] and not player.rect.colliderect(rock.rect):
        #if not player.rect.colliderect(rock.rect):
        move = (-1, 0)
        player.rect = player.rect.move(move)
    if pressed[pygame.K_RIGHT] and not player.rect.colliderect(rock.rect):
        move = (1, 0)
        player.rect = player.rect.move(move)
    if pressed[pygame.K_UP] and not player.rect.colliderect(rock.rect):
        move = (0, -1)
        player.rect = player.rect.move(move)
    if pressed[pygame.K_DOWN] and not player.rect.colliderect(rock.rect):
        move = (0, 1)
        player.rect = player.rect.move(move)

    screen.fill(white)

    sprites.draw(screen)

    pygame.display.flip()

    clock.tick(30)

pygame.quit()

编辑:当我将移动速度降低到1时,玩家仍然被卡在岩石中。我也更新了代码。

3个回答

3
你需要检查是否会发生碰撞,然后再移动。假设你在岩石下方1个单位,并向上移动1个单位。你可以这样做,因为你现在没有被卡住。但是现在你在岩石里,无法移动。
检查碰撞的简单有效方法是先移动,检查是否有碰撞,如果有必要则返回移动前的位置。

你实际上不需要move backmove会返回一个新的Rect,所以你可以使用它来检查碰撞。如果有碰撞,什么都不需要做。如果没有碰撞,则将player.rect设置为新的rect或(在player.rect上调用move_ip)。 - sloth
@sloth 我的意思不是 Rect.move() 这样的移动,而是“改变位置”(即动词的正常含义)。 - David Mašek
这个回答加上评论是一个很好的方法,我借鉴它来编辑我的答案。 - Henrik

3

您的答案已包含在您的问题中。

(注:无需修改,已为通俗易懂的翻译)
    if .... . and not player.rect.colliderect(rock.rect):

只有当没有发生碰撞时,才允许移动所有移动键。 一旦进入碰撞范围,将不允许任何移动操作。

你必须允许远离障碍物的移动键! 为此,您需要知道碰撞发生的位置... (在角色前面、左侧等)

编辑:灵感来自另一个答案

这个编辑是在接受答案之后包括的 ;) 原因是解决方案更好,不需要检查碰撞的方向,因为它不需要计算任何东西...

思路很简单... 始终尝试移动,只有结果不会导致碰撞时才应用它。

这将始终允许合法移动,并且不会卡住。

唯一的注意点是非常小的障碍物可能会被“跳过”,如果一个移动步骤大于障碍步骤..

检查碰撞的简单有效方法是先移动,然后检查碰撞并在必要时返回。

David Mašek

还有这个评论:

move返回一个新矩形,因此您可以使用它来检查碰撞。如果有碰撞,则什么也不用做。如果没有碰撞,请将player.rect设置为新矩形或(在player.rect上调用move_ip)。

sloth


@不要点击我的个人资料 但是一旦你碰撞上了岩石,你已经陷入其中了。即使你以后可以摆脱它,这可能不是你想要的行为。 - David Mašek

1
如果你的岩石不在5个单位的边界上,你可以移动“进入”岩石,然后就会被卡住。

我将移动速度改为1并再次测试,但仍然卡住了。我是否错过了你的答案的要点? - BoJack Horseman
@Don'tclickonmyprofile:这很有趣。请将这个信息添加到你的原始问题中。 - Marcus Müller

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接