Reputation: 21
Hey guys I need help with an issue about collisions on pygame. I have a boundary map to set up pixel perfect collision by the use of mask overlaps. The collision is detected without any issues, but when it comes to stopping the player from going through the wall, there's always some way of being sent through it. I've done a few attempts (a lot of them really), including even freezing the player when you press three buttons at once to avoid it from spazzing out, but to no avail.
Right now, I check which button the user presses to set the direction:
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
key = pygame.key.get_pressed()
if key[pygame.K_w]:
COLLISION_DIRECTION = "UP"
if key[pygame.K_s]:
COLLISION_DIRECTION = "DOWN"
if key[pygame.K_d]:
COLLISION_DIRECTION = "RIGHT"
if key[pygame.K_a]:
COLLISION_DIRECTION = "LEFT"
And then do a check on collision based on the direction. In my last desperate attempt, I resorted to saving the last non collision position on an array, translating the world (I'm moving the world, not the player) to that position and subtracting, or adding the movement speed:
if boundariesMap_mask.overlap(player.mask, (offset_x, offset_y)) is None:
lastPos = [world.x, world.y]
else:
while COLLISION_DIRECTION == "UP":
world.y = (lastPos[1]-DIST_WORLD)
break
while COLLISION_DIRECTION == "DOWN":
world.y = (lastPos[1]+DIST_WORLD)
break
while COLLISION_DIRECTION == "LEFT":
world.x = (lastPos[0]-DIST_WORLD)
break
while COLLISION_DIRECTION == "RIGHT":
world.x = (lastPos[0]+DIST_WORLD)
break
My best guess is that I should block all additional input while moving back to the safe position to avoid changing the COLLISION_DIRECTION. Can you guys give me any help?
world.x and world.y are the variable coordinates of the world
Edit: I forgot to add the the world constructor class
class World(pygame.sprite.Sprite):
def __init__(self):
self.sprite = pygame.image.load('bin\\assets\\test.png')
self.x = 220
self.y = 45
def draw(self, surface):
surface.blit(self.sprite, (self.x, self.y))
def handle_keys(self):
key = pygame.key.get_pressed()
if pygame.joystick.get_count():
joystick_x = round(my_joystick.get_axis(0))
joystick_y = round(my_joystick.get_axis(1))
if joystick_x < 0:
self.x += DIST_WORLD
if joystick_x > 0:
self.x -= DIST_WORLD
if joystick_y < 0:
self.y += DIST_WORLD
if joystick_y > 0:
self.y -= DIST_WORLD
if key[pygame.K_w]:
self.y += DIST_WORLD
if key[pygame.K_s]:
self.y -= DIST_WORLD
if key[pygame.K_a]:
self.x += DIST_WORLD
if key[pygame.K_d]:
self.x -= DIST_WORLD
Upvotes: 2
Views: 708
Reputation: 67
Judging from the question and your response in the comments, it appears that the glitch is triggered whenever three or more keypresses are registered, causing the COLLISION_DIRECTION
and the World.y
and World.x
to be updated inappropriately.
Consider the fact that if the player is moving along a certain direction in the X or Y axis, they cannot be moving in the opposite direction at the same time. Consider also the fact that the code in your main game loop allows the value of COLLISION_DIRECTION
to be overwritten if the player is pressing multiple keys at once; for example, if W, A, and S were held down simultaneously, the final COLLISION_DIRECTION
would be "LEFT"
.
I'm assuming you're allowing diagonal motion, so what we want to do here is make COLLISION_DETECTION
meaningful for the x-axis and the y-axis simultaneously. Then, we must limit the number of keypress checks to exactly two (once for the x-axis and once for the y-axis) so that the three-button problem doesn't happen.
In your main game loop:
## Instead of one COLLISION_DIRECTION, we now have two separate variables:
## COLLISION_DIRECTION_X and COLLISION_DIRECTION_Y
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
key = pygame.key.get_pressed()
## Using elif so we don't mess up COLLISION_DIRECTION_Y
if key[pygame.K_w]:
COLLISION_DIRECTION_Y = "UP"
elif key[pygame.K_s]:
COLLISION_DIRECTION_Y = "DOWN"
## Using elif so we don't mess up COLLISION_DIRECTION_X
if key[pygame.K_d]:
COLLISION_DIRECTION_X = "RIGHT"
elif key[pygame.K_a]:
COLLISION_DIRECTION_X = "LEFT"
Updating your collision detection accordingly:
if boundariesMap_mask.overlap(player.mask, (offset_x, offset_y)) is None:
lastPos = [world.x, world.y]
else:
## Redundant while-break replaced with simple if-statements;
## Using while is not exactly necessary because we carry out
## these statements each frame
if COLLISION_DIRECTION_Y == "UP":
world.y = (lastPos[1]-DIST_WORLD)
elif COLLISION_DIRECTION_Y == "DOWN":
world.y = (lastPos[1]+DIST_WORLD)
if COLLISION_DIRECTION_X == "LEFT":
world.x = (lastPos[0]-DIST_WORLD)
elif COLLISION_DIRECTION_X == "RIGHT":
world.x = (lastPos[0]+DIST_WORLD)
And then, in your handle_keys
function:
def handle_keys(self):
key = pygame.key.get_pressed()
if pygame.joystick.get_count():
joystick_x = round(my_joystick.get_axis(0))
joystick_y = round(my_joystick.get_axis(1))
## Restrict joystick x-input checking, because we logically
## cannot be both greater and less than zero
if joystick_x < 0:
self.x += DIST_WORLD
elif joystick_x > 0:
self.x -= DIST_WORLD
## Restrict joystick y-input checking, because we logically
## cannot be both greater and less than zero
if joystick_y < 0:
self.y += DIST_WORLD
elif joystick_y > 0:
self.y -= DIST_WORLD
## Restrict keypress vertical axis checking for the same reason
if key[pygame.K_w]:
self.y += DIST_WORLD
elif key[pygame.K_s]:
self.y -= DIST_WORLD
## Restrict keypress horizontal axis checking for same reason
if key[pygame.K_a]:
self.x += DIST_WORLD
elif key[pygame.K_d]:
self.x -= DIST_WORLD
I hope this helped! If the code doesn't do it, I apologize, but in the very least, I hope my comments prior to the code provide some insight.
Upvotes: 1