如何通过碰撞检测阻止玩家精灵穿过块?
How to stop player sprite passing through blocks with collision detection?
我试图阻止玩家穿过方块。我希望他们能够降落在街区上,但如果在街区的另一边发生碰撞,他们会弹开
我之前尝试过更改玩家撞到方块时重置的距离
运行每帧检查是否有碰撞:
hits = pygame.sprite.spritecollide(player, walls, False)
if hits:
player.checkCollisionWall(hits)
玩家Class
import pygame, time, Settings
from pygame.locals import *
vec = pygame.math.Vector2
pygame.init()
class player(pygame.sprite.Sprite):
ACCEL = 0.5 # Acceleration
GFRICTION = vec(-0.2, 0) # Ground Friction
AFRICTION = vec(-0.2, 0) # Air Friction
GRAVITY = 0.8 # must be greater than 0.6
JUMP_HEIGHT = 10
START_X = 25
START_Y = 600
WIDTH = 10
HEIGHT = 10
START_POS = vec(START_X + WIDTH/2, START_Y + HEIGHT) # point at bottom middle
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.Pos = self.START_POS
self.image = pygame.surface.Surface((self.WIDTH, self.HEIGHT))
self.image.fill((0, 255, 0))
self.rect = self.image.get_rect()
self.rect.center = (self.Pos.x, self.Pos.y)
self.vel = vec(0,0) # set velocity as a vector
self.acc = vec(0,0) # set acceleration as a vector
self.inJump = False
self.tryingToJump = False
def update(self):
self.tryingToJump = False
self.acc = vec(0, self.GRAVITY)
self.move()
def draw(self):
# draw the rectangle
self.Pos += self.vel + 0.5 *self.acc
self.rect.center = self.Pos
def move(self):
# identify which keys are pressed
pressed_keys = pygame.key.get_pressed()
if pressed_keys[K_LEFT]:
self.changeX("left")
elif pressed_keys[K_RIGHT]:
self.changeX("right")
if pressed_keys[K_UP]:
self.jump()
# check player is on screen and place player where it should be if neccessary
if self.Pos.y > Settings.WINDOW_HEIGHT:
self.Pos.x = self.START_POS.x
self.Pos.y = self.START_POS.y
if self.Pos.x < 0:
self.vel.x = 2
if self.Pos.x > Settings.WINDOW_WIDTH:
self.vel.x = -2
# apply friction
if self.inJump: #in the air
self.acc.x += self.vel.x * self.AFRICTION.x
else: #touching the ground
self.acc.x += self.vel.x * self.GFRICTION.x
# move the player
self.vel += self.acc
def changeX(self, direction):
# move left or right
if direction == "right":
self.acc.x = self.ACCEL
elif direction == "left":
self.acc.x = -self.ACCEL
def jump(self):
# jump only if standing on a platform
if self.inJump == False:
self.tryingToJump = True
self.inJump = True
self.vel.y -= self.JUMP_HEIGHT
def checkCollisionWall(self, hits):
self.lowestWall = self.highestWall = hits[0]
for i in hits:
if i.rect.bottom > self.lowestWall.rect.bottom:
self.lowestWall = i # find the lowest wall that the player is touching
if i.rect.top < self.highestWall.rect.top:
self.highestWall = i # find the highest wall that the player is touching
if self.vel.y > 0: # check if a block is below
print("below")
self.rect.bottom = self.lowestWall.rect.top
self.acc.y = self.vel.y = 0 # set acceleration and velocity to 0 on the y axis
self.inJump = False
if self.vel.y < 0: # check if a block is above
if not self.tryingToJump: # if the block isn't trying to jump (I have this in otherwise player doesn't jump)
print("above")
self.rect.top = self.highestWall.rect.bottom
self.acc.y = self.vel.y = 0 # set acceleration and velocity to 0 on the y axis
if self.highestWall.rect.top < self.lowestWall.rect.top and self.rect.bottom == self.lowestWall.rect.top: # I have this line in too make sure that the player does not snap to the side of the block it is in when it moves side to side
if self.vel.x > 0:
print("right")
self.rect.right = self.highestWall.rect.left
self.acc.x = self.vel.x = -self.ACCEL # set acceleration and velocity to -0.5 on the x axis
if self.vel.x < 0:
print("left")
self.rect.left = self.highestWall.rect.right
self.acc.x = self.vel.x = self.ACCEL # set acceleration and velocity to 0.5 on the x axis
当我尝试这个时,玩家可以很好地落在方块上,但是当他们接触到方块的底部时,玩家会迅速跳到方块的顶部,而不是从方块上弹开。当玩家在进入方块时跳跃时,玩家会跳到更高方块的顶部。
根据我(不是那么丰富)的经验,如果没有针对每个维度单独测试玩家移动,可能会出现这种问题。
我建议你:
将 x 和 y 移动和碰撞测试分开,以便您可以沿 x 移动玩家,测试碰撞并在需要时固定 x 位置,然后对 y 重复该过程。
编辑 checkCollisionWall
方法,使其采用单个块,而不是块列表。如果您将 x 和 y 运动分开,则玩家最多会与一个方块发生碰撞。因此无需搜索最低的墙和最高的墙。你的代码会更简单。
在此我建议您进行一些修改。 move
已拆分为 move_x
和 move_y
。 checkCollisionWall
变成 checkCollision_x
和 checkCollision_y
.
我还编辑了 update
方法:现在它执行第 1 点中描述的整个过程,因此调用 update
也会检查碰撞。
def update(self):
self.tryingToJump = False
self.acc = vec(0, self.GRAVITY)
self.move_x()
hits = pygame.sprite.spritecollide(player, walls, False)
for bhit in hits:
player.checkCollision_x(bhit)
self.move_y()
hits = pygame.sprite.spritecollide(player, walls, False)
for bhit in hits:
player.checkCollision_y(bhit)
def move_x(self):
# identify which keys are pressed
pressed_keys = pygame.key.get_pressed()
if pressed_keys[K_LEFT]:
self.changeX("left")
elif pressed_keys[K_RIGHT]:
self.changeX("right")
# check player is on screen and place player where it should be if neccessary
if self.Pos.x < 0:
self.vel.x = 2
if self.Pos.x > Settings.WINDOW_WIDTH:
self.vel.x = -2
# apply friction
if self.inJump: #in the air
self.acc.x += self.vel.x * self.AFRICTION.x
else: #touching the ground
self.acc.x += self.vel.x * self.GFRICTION.x
# move the player
self.vel.x += self.acc.x
def move_y(self):
# identify which keys are pressed
pressed_keys = pygame.key.get_pressed()
if pressed_keys[K_UP]:
self.jump()
# check player is on screen and place player where it should be if neccessary
if self.Pos.y > Settings.WINDOW_HEIGHT:
self.Pos.x = self.START_POS.x
self.Pos.y = self.START_POS.y
# move the player
self.vel.y += self.acc.y
def checkCollision_x(self, coll_block):
if self.vel.x > 0:
print("right")
self.rect.right = coll_block.rect.left
self.acc.x = self.vel.x = -self.ACCEL # set acceleration and velocity to -0.5 on the x axis
elif self.vel.x < 0:
print("left")
self.rect.left = coll_block.rect.right
self.acc.x = self.vel.x = self.ACCEL # set acceleration and velocity to 0.5 on the x axis
def checkCollision_y(self, coll_block):
if self.vel.y > 0: # check if a block is below
print("below")
self.rect.bottom = coll_block.rect.top
self.acc.y = self.vel.y = 0 # set acceleration and velocity to 0 on the y axis
self.inJump = False
elif self.vel.y < 0: # check if a block is above
if not self.tryingToJump: # if the block isn't trying to jump (I have this in otherwise player doesn't jump)
print("above")
self.rect.top = coll_block.rect.bottom
self.acc.y = self.vel.y = 0 # set acceleration and velocity to 0 on the y axis
此代码当然没有经过测试,但即使不能解决您的问题,我认为也应该为您指明正确的方向。
我试图阻止玩家穿过方块。我希望他们能够降落在街区上,但如果在街区的另一边发生碰撞,他们会弹开
我之前尝试过更改玩家撞到方块时重置的距离
运行每帧检查是否有碰撞:
hits = pygame.sprite.spritecollide(player, walls, False)
if hits:
player.checkCollisionWall(hits)
玩家Class
import pygame, time, Settings
from pygame.locals import *
vec = pygame.math.Vector2
pygame.init()
class player(pygame.sprite.Sprite):
ACCEL = 0.5 # Acceleration
GFRICTION = vec(-0.2, 0) # Ground Friction
AFRICTION = vec(-0.2, 0) # Air Friction
GRAVITY = 0.8 # must be greater than 0.6
JUMP_HEIGHT = 10
START_X = 25
START_Y = 600
WIDTH = 10
HEIGHT = 10
START_POS = vec(START_X + WIDTH/2, START_Y + HEIGHT) # point at bottom middle
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.Pos = self.START_POS
self.image = pygame.surface.Surface((self.WIDTH, self.HEIGHT))
self.image.fill((0, 255, 0))
self.rect = self.image.get_rect()
self.rect.center = (self.Pos.x, self.Pos.y)
self.vel = vec(0,0) # set velocity as a vector
self.acc = vec(0,0) # set acceleration as a vector
self.inJump = False
self.tryingToJump = False
def update(self):
self.tryingToJump = False
self.acc = vec(0, self.GRAVITY)
self.move()
def draw(self):
# draw the rectangle
self.Pos += self.vel + 0.5 *self.acc
self.rect.center = self.Pos
def move(self):
# identify which keys are pressed
pressed_keys = pygame.key.get_pressed()
if pressed_keys[K_LEFT]:
self.changeX("left")
elif pressed_keys[K_RIGHT]:
self.changeX("right")
if pressed_keys[K_UP]:
self.jump()
# check player is on screen and place player where it should be if neccessary
if self.Pos.y > Settings.WINDOW_HEIGHT:
self.Pos.x = self.START_POS.x
self.Pos.y = self.START_POS.y
if self.Pos.x < 0:
self.vel.x = 2
if self.Pos.x > Settings.WINDOW_WIDTH:
self.vel.x = -2
# apply friction
if self.inJump: #in the air
self.acc.x += self.vel.x * self.AFRICTION.x
else: #touching the ground
self.acc.x += self.vel.x * self.GFRICTION.x
# move the player
self.vel += self.acc
def changeX(self, direction):
# move left or right
if direction == "right":
self.acc.x = self.ACCEL
elif direction == "left":
self.acc.x = -self.ACCEL
def jump(self):
# jump only if standing on a platform
if self.inJump == False:
self.tryingToJump = True
self.inJump = True
self.vel.y -= self.JUMP_HEIGHT
def checkCollisionWall(self, hits):
self.lowestWall = self.highestWall = hits[0]
for i in hits:
if i.rect.bottom > self.lowestWall.rect.bottom:
self.lowestWall = i # find the lowest wall that the player is touching
if i.rect.top < self.highestWall.rect.top:
self.highestWall = i # find the highest wall that the player is touching
if self.vel.y > 0: # check if a block is below
print("below")
self.rect.bottom = self.lowestWall.rect.top
self.acc.y = self.vel.y = 0 # set acceleration and velocity to 0 on the y axis
self.inJump = False
if self.vel.y < 0: # check if a block is above
if not self.tryingToJump: # if the block isn't trying to jump (I have this in otherwise player doesn't jump)
print("above")
self.rect.top = self.highestWall.rect.bottom
self.acc.y = self.vel.y = 0 # set acceleration and velocity to 0 on the y axis
if self.highestWall.rect.top < self.lowestWall.rect.top and self.rect.bottom == self.lowestWall.rect.top: # I have this line in too make sure that the player does not snap to the side of the block it is in when it moves side to side
if self.vel.x > 0:
print("right")
self.rect.right = self.highestWall.rect.left
self.acc.x = self.vel.x = -self.ACCEL # set acceleration and velocity to -0.5 on the x axis
if self.vel.x < 0:
print("left")
self.rect.left = self.highestWall.rect.right
self.acc.x = self.vel.x = self.ACCEL # set acceleration and velocity to 0.5 on the x axis
当我尝试这个时,玩家可以很好地落在方块上,但是当他们接触到方块的底部时,玩家会迅速跳到方块的顶部,而不是从方块上弹开。当玩家在进入方块时跳跃时,玩家会跳到更高方块的顶部。
根据我(不是那么丰富)的经验,如果没有针对每个维度单独测试玩家移动,可能会出现这种问题。
我建议你:
将 x 和 y 移动和碰撞测试分开,以便您可以沿 x 移动玩家,测试碰撞并在需要时固定 x 位置,然后对 y 重复该过程。
编辑
checkCollisionWall
方法,使其采用单个块,而不是块列表。如果您将 x 和 y 运动分开,则玩家最多会与一个方块发生碰撞。因此无需搜索最低的墙和最高的墙。你的代码会更简单。
在此我建议您进行一些修改。 move
已拆分为 move_x
和 move_y
。 checkCollisionWall
变成 checkCollision_x
和 checkCollision_y
.
我还编辑了 update
方法:现在它执行第 1 点中描述的整个过程,因此调用 update
也会检查碰撞。
def update(self):
self.tryingToJump = False
self.acc = vec(0, self.GRAVITY)
self.move_x()
hits = pygame.sprite.spritecollide(player, walls, False)
for bhit in hits:
player.checkCollision_x(bhit)
self.move_y()
hits = pygame.sprite.spritecollide(player, walls, False)
for bhit in hits:
player.checkCollision_y(bhit)
def move_x(self):
# identify which keys are pressed
pressed_keys = pygame.key.get_pressed()
if pressed_keys[K_LEFT]:
self.changeX("left")
elif pressed_keys[K_RIGHT]:
self.changeX("right")
# check player is on screen and place player where it should be if neccessary
if self.Pos.x < 0:
self.vel.x = 2
if self.Pos.x > Settings.WINDOW_WIDTH:
self.vel.x = -2
# apply friction
if self.inJump: #in the air
self.acc.x += self.vel.x * self.AFRICTION.x
else: #touching the ground
self.acc.x += self.vel.x * self.GFRICTION.x
# move the player
self.vel.x += self.acc.x
def move_y(self):
# identify which keys are pressed
pressed_keys = pygame.key.get_pressed()
if pressed_keys[K_UP]:
self.jump()
# check player is on screen and place player where it should be if neccessary
if self.Pos.y > Settings.WINDOW_HEIGHT:
self.Pos.x = self.START_POS.x
self.Pos.y = self.START_POS.y
# move the player
self.vel.y += self.acc.y
def checkCollision_x(self, coll_block):
if self.vel.x > 0:
print("right")
self.rect.right = coll_block.rect.left
self.acc.x = self.vel.x = -self.ACCEL # set acceleration and velocity to -0.5 on the x axis
elif self.vel.x < 0:
print("left")
self.rect.left = coll_block.rect.right
self.acc.x = self.vel.x = self.ACCEL # set acceleration and velocity to 0.5 on the x axis
def checkCollision_y(self, coll_block):
if self.vel.y > 0: # check if a block is below
print("below")
self.rect.bottom = coll_block.rect.top
self.acc.y = self.vel.y = 0 # set acceleration and velocity to 0 on the y axis
self.inJump = False
elif self.vel.y < 0: # check if a block is above
if not self.tryingToJump: # if the block isn't trying to jump (I have this in otherwise player doesn't jump)
print("above")
self.rect.top = coll_block.rect.bottom
self.acc.y = self.vel.y = 0 # set acceleration and velocity to 0 on the y axis
此代码当然没有经过测试,但即使不能解决您的问题,我认为也应该为您指明正确的方向。