用墙壁进行pygame碰撞检测

时间:2017-06-23 12:09:49

标签: python pygame collision-detection

目前我花了大约半个小时试图找出如何更好地实现这个脚本。我尝试了一些方法,但它们并没有完全奏效。 当运行脚本时,播放器会出现故障,有时播放器只有在接触后才能移动到块中。

x = 64*3
y = 0
xvel = 0
yvel = 0
grounded = True

playerRect = pygame.Rect ((x, y, 64, 64))

collidelist = []

level = ["#=======##=========================",
         "#=======#==========================",
         "#==###############=========###=====",
         "#===============#####==============",
         "#==================================",
         "###################################"]

def makelevel (level):
    x = y = 0
    def checkline (line, x, y):
        for character in line:
            if character == "#":
                block = pygame.draw.rect (screen, (50, 50, 255), (x * 64, y * 64, 64, 64))
                collidelist.append (block)
            x += 1
    for line in level:
        checkline (line, x, y)
        y += 1
def move (xvel, yvel):
    global x
    global y
    global playerRect
    global collideList
    x += xvel
    y += yvel
    for block in collidelist:
        if playerRect.colliderect(block):
            x += -xvel * 2
            y += -yvel * 2
            break
makelevel (level)

while True:
    screen.fill ([0, 0, 0])
    makelevel (level)
    playerRect = pygame.Rect ((x, y, 64, 64))
    pygame.draw.rect (screen, (255, 255, 255), playerRect)
    for event in pygame.event.get ():
        if event.type == pygame.QUIT:
            pygame.quit ()
            sys.exit ()
            exit ()
    pressed = pygame.key.get_pressed ()
    if pressed [pygame.K_RIGHT]:
        move (5, 0)
    if pressed [pygame.K_LEFT]:
        move (-5, 0)
    if pressed [pygame.K_UP]:
        move (0, -5)
    if pressed [pygame.K_DOWN]:
        move (0, 5)
    pygame.display.update ()

1 个答案:

答案 0 :(得分:0)

要处理与墙壁的碰撞,最简单的解决方案是首先沿x轴移动播放器rect或sprite,检查它是否与墙壁碰撞,然后如果我们移动则设置其rect.right = block.left向右或rect.left = block.right如果我们向左移动。然后你用y轴做同样的事情。我们必须单独进行,否则我们不知道方向以及如何重置矩形的位置。

以下是一个例子:

import sys
import pygame


def makelevel(level):
    collidelist = []
    for y, line in enumerate(level):
        for x, character in enumerate(line):
            if character == "#":
                block = pygame.Rect(x*64, y*64, 64, 64)
                collidelist.append(block)
    return collidelist


def move(xvel, yvel, player_rect, collideList):
    # Move the rect along the x-axis first.
    player_rect.x += xvel
    # Check if it collides with a block.
    for block in collidelist:
        if player_rect.colliderect(block):
            if xvel < 0:  # We're moving to the left.
                # Move the player out of the block.
                player_rect.left = block.right
            elif xvel > 0:  # We're moving to the right.
                player_rect.right = block.left
            break

    # Now do the same for the y-axis.
    player_rect.y += yvel
    for block in collidelist:
        if player_rect.colliderect(block):
            if yvel < 0:
                player_rect.top = block.bottom
            elif yvel > 0:
                player_rect.bottom = block.top
            break


pygame.init()
screen = pygame.display.set_mode((800, 600))
BLOCK_COLOR = (50, 50, 255)
BG_COLOR = (0, 0, 0)

level = ["#=======##=========================",
         "#=======#==========================",
         "#==###############=========###=====",
         "#===============#####==============",
         "#==================================",
         "###################################",]

collidelist = makelevel(level)
player_rect = pygame.Rect((64*3, 0, 64, 64))
# A clock to limit the frame rate.
clock = pygame.time.Clock()

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

    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_RIGHT]:
        move(5, 0, player_rect, collidelist)
    if pressed[pygame.K_LEFT]:
        move(-5, 0, player_rect, collidelist)
    if pressed[pygame.K_UP]:
        move(0, -5, player_rect, collidelist)
    if pressed[pygame.K_DOWN]:
        move(0, 5, player_rect, collidelist)

    # Draw everything.
    screen.fill(BG_COLOR)

    for rect in collidelist:
        pygame.draw.rect(screen, BLOCK_COLOR, rect)

    pygame.draw.rect(screen, (255, 255, 255), player_rect)

    pygame.display.update()
    clock.tick(30)  # Limit frame rate to 30 fps.

我需要解决您的代码中的一些其他问题(即使codereview是offtopic(请查看http://codereview.stackexchange.com/)):

  • 最严重的问题是您在主循环中调用makelevel,因此您会向collidelist添加越来越多的内容,直到内存不足为止。在此函数中创建列表,然后返回并将其分配给while循环上方的变量,然后重复使用它。

  • 没有必要在checkline内定义makelevel功能。

  • 全局变量会对代码的可理解性和可维护性产生不良影响。而是将所需的变量传递给需要它们的函数并返回结果。

  • 使用enumerate代替y中的xmakelevel增加1。