RockyS
RockyS

Reputation: 21

Passing through walls after collision Pygame

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

Answers (1)

Joey Navarro
Joey Navarro

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

Related Questions