在制作一个平台游戏时,我生成了地形,但没有碰撞检测。下面是我尝试用来检查碰撞的代码:
def player_move(self):
self.player.rect.x += self.player.velX
self.check_collision(self.player, self.player.velX, 0)
self.player.rect.y += self.player.velY
self.check_collision(self.player, 0, self.player.velY)
def check_collision(self, sprite, x_vel, y_vel):
# for every tile in Background.levelStructure, check for collision
for block in self.moon.get_surrounding_blocks(sprite):
if block is not None:
if pygame.sprite.collide_rect(sprite, block):
# we've collided! now we must move the collided sprite a step back
if x_vel < 0:
sprite.rect.x = block.rect.x + block.rect.w
if x_vel > 0:
sprite.rect.x = block.rect.x - sprite.rect.w
if y_vel < 0:
sprite.rect.y = block.rect.y + block.rect.h
我还提供了关卡生成代码,以帮助解决问题:
import pygame, random
# Level
class Block(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('data/images/block.png')
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
def render(self, surface):
surface.blit(self.image, self.rect)
class Background():
def __init__(self):
self.bg_image = pygame.image.load('data/images/background.jpg')
# create a 2-dimensional array (etc. tile[x][y]) for holding the level structure
self.levelStructure = [[None for i in range(50)] for i in range(50)]
# generate level on initialization
self.generateLevel()
def generateLevel(self):
# fill self.levelStructure with tiles
for x in range(20):
for y in range(9):
if y < 11:
# make top space for the player to be
continue
tempBlock = Block(x*40, y*40)
# add block to both and self.levelStructure
self.levelStructure[x][y] = tempBlock
# generate some random terrain
for x in range(25):
# get a random height
height = random.randint(2, 5)
for y in range(height):
y += 15
tempBlock = Block(x*40, (y-height)*40)
self.levelStructure[x][y-height] = tempBlock
def get_surrounding_blocks(self, sprite):
# calculate the grid position of the sprite
sprite_x = sprite.rect.x // 40
sprite_y = sprite.rect.y // 40
# generate a list of surrounding sprites
blocks = []
for x in range(-2, 2+1):
for y in range(-2, 2+1):
blocks.append(self.levelStructure[sprite_x + x][sprite_y + y])
return blocks
def render(self, surface):
surface.blit(self.bg_image, (0, 0))
# also draw the blocks we created
for x in range(22):
for y in range(15):
block = self.levelStructure[x][y]
# check if there is a block, or if the grid is empty
if block is not None:
block.render(surface)
2、解决方案
存在几个问题:
- 没有更新速度,导致速度不会改变。
- 重复迭代所有方块两次,一次检查 X 轴,一次检查 Y 轴。对于复杂的地图,这将导致速度减半。
- 使用
collide_rect
函数来测试碰撞不十分妥当。当速度较高时,玩家可能会跳过方块而不产生碰撞。 - 在移动物体后才检查碰撞,这可能导致问题,因为需要将物体移回原来的位置。
以下是改进后的代码:
def player_move(self):
# create some local vars for ease of reading...
x = self.player.rect.x
x_next = x + self.player.velX
y = self.player.rect.y
y_next = y + self.player.vely
w = self.player.rect.w
h = self.player.rect.h
# has a collision happened?
hitX = False
hitY = False
for block in self.moon.get_surrounding_blocks(self.player):
# empty blocks are 'None' in this map system...
if not block:
continue
if self.player.velX > 0: # moving right
if x + w < block.x and x_next + w >= block.x:
# collision!
x_next = block.x - w
hitX = True
elif self.player.velX < 0: # moving left
if x > block.x + block.w and x_next > block.x + block.w:
# collision!
x_next = block.x + block.w
hitX = True
if self.player.velY...... # do the same for Y axis...
# now update rect to have the new collision fixed positions:
self.player.rect.x = x_next
self.player.rect.y = y_next
# and if there was a collision, fix the velocity.
if hitX:
self.velX = 0 # or 0 - velX to bounce instead of stop
if hitY:
self.velY = 0 # or 0 - velY to bounce instead of stop