如何在Pygame中获取碰撞的坐标/区域

时间:2017-07-29 12:58:04

标签: python pygame collision-detection collision

我有一个游戏,其子弹移动得如此之快,以至于它已经在一个帧中穿过屏幕。话虽如此,它已经与多个墙相撞。目前,我有一个矩形图像,从子弹当前所在的位置,到子弹将在下一帧中的位置,以便不会错过任何可能在其间的僵尸。

如果它与任何一个墙碰撞,我也会杀死子弹,然后检查它是否与任何僵尸相撞,因为发生的事情是如果墙后面有一个僵尸并且我先检查了僵尸碰撞,它会杀死它僵尸,然后杀死子弹。

所以基本上,我想知道一种找到子弹与墙碰撞的坐标的方法,这样我就可以将它推进到碰撞前的位置,而不是以全速推进子弹。检查僵尸碰撞,然后杀死子弹。

我正在使用蒙版碰撞。

2 个答案:

答案 0 :(得分:2)

如果子弹行进得太快而无法与墙壁或敌人发生碰撞,则需要光线投射(或者以多个小步骤移动子弹)。这是一个简单的光线投射示例,它返回最近的碰撞点。我使用vectorspygame.Rect.collidepoint来查看heading向量上的点是否与障碍物发生碰撞。

import sys
import pygame as pg
from pygame.math import Vector2


class Wall(pg.sprite.Sprite):

    def __init__(self, x, y, w, h, *groups):
        super().__init__(*groups)
        self.image = pg.Surface((w, h))
        self.image.fill(pg.Color('goldenrod4'))
        self.rect = self.image.get_rect(topleft=(x, y))


def ray_cast(origin, target, obstacles):
    """Calculate the closest collision point.

    Adds the normalized `direction` vector to the `current_pos` to
    move along the heading vector and uses `pygame.Rect.collidepoint`
    to see if `current_pos` collides with an obstacle.

    Args:
        origin (pygame.math.Vector2, tuple, list): Origin of the ray.
        target (pygame.math.Vector2, tuple, list): Endpoint of the ray.
        obstacles (pygame.sprite.Group): A group of obstacles.

    Returns:
        pygame.math.Vector2: Closest collision point or target.
    """
    current_pos = Vector2(origin)
    heading = target - origin
    # A normalized vector that points to the target.
    direction = heading.normalize()
    for _ in range(int(heading.length())):
        current_pos += direction
        for sprite in obstacles:
            # If the current_pos collides with an
            # obstacle, return it.
            if sprite.rect.collidepoint(current_pos):
                return current_pos
    # Otherwise return the target.
    return Vector2(target)


def main():
    screen = pg.display.set_mode((640, 480))
    clock = pg.time.Clock()
    all_sprites = pg.sprite.Group()
    walls = pg.sprite.Group()
    Wall(100, 170, 90, 20, all_sprites, walls)
    Wall(200, 100, 20, 140, all_sprites, walls)
    Wall(400, 60, 150, 100, all_sprites, walls)

    pos = Vector2(320, 440)
    done = False

    while not done:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                done = True

        all_sprites.update()
        collision_point = ray_cast(pos, pg.mouse.get_pos(), walls)
        screen.fill((30, 30, 30))
        all_sprites.draw(screen)
        pg.draw.line(screen, (50, 190, 100), pos, pg.mouse.get_pos(), 2)
        pg.draw.circle(screen, (40, 180, 250), [int(x) for x in collision_point], 5)

        pg.display.flip()
        clock.tick(30)


if __name__ == '__main__':
    pg.init()
    main()
    pg.quit()
    sys.exit()

答案 1 :(得分:0)

不幸的是PyGame并没有给我们一个内置的方法来返回碰撞点,这很容易做到这一点,所以有一些腿部工作要做。

然而,在我解释之前,你在问题中提到子弹移动得非常快。我不确定我们说话的速度有多快,所以这可能不适用,但在我的经验中,碰撞会在高速下成为热门,特别是如果你在较慢的电脑上工作。

假设^不适用:

我们可以使用pygame.Rect.colliderect来触发if语句。

if <bullet-rect>.collidepoint(<wall-rect>):
  print(bullet_x, bullet_y)

简单地换掉实际的作品,你应该好好去。需要注意的一点是,如果子弹从右向左移动,则必须将子弹的宽度添加到x值,如果子弹从上到下移动,则必须添加子弹&# 39; s高度为y值。

注意:请务必在每个值中添加pygame.Rect(<bullet-rect>)pygame.Rect(<wall-rect>),否则您将收到错误