ButterySpirit
ButterySpirit

Reputation: 1

Conflicting "if" statements

So I am trying to implement proper wall collision detection with my player similar to how binding of isaac works (you run into a wall while pressing two keys, when you hit the wall, movement towards the wall is disabled but you can kinda continue sliding at the wall in the direction of the other key). So I tried to create two different if statements in the update method of my player class that handle left/right collisions and up/down collisions respectively but it seems that the order of the if statements seems to be conflicting with one another

def update(self,dt) 
        prev_x = self.x
        prev_y = self.y

        # Update the player's position
        self.world_x += dx
        self.world_y += dy
        self.x += dx
        self.y += dy
# Check for collisions with the "Walls" layer for horizontal movement
        if self.detect_collision_with_walls(self.walls_layer) and self.moving_left:
            self.x = prev_x
            self.world_x = self.x
        if self.detect_collision_with_walls(self.walls_layer) and self.moving_right:
            self.x = prev_x
            self.world_x = self.x

        # Check for collisions with the "Walls" layer for vertical movement
        if self.detect_collision_with_walls(self.walls_layer) and self.moving_up:
            self.y = prev_y
            self.world_y = self.y
            
        if self.detect_collision_with_walls(self.walls_layer) and self.moving_down:
            self.y = prev_y
            self.world_y = self.y

the thing is only the top two statements statements execute as they should, and when the bottom two run they get conflicted with the top ones because both are technically true at the same time. and it locks the player in place until the key that is causing the collision is released because it is essentially doing this:

 # Check for collisions with the "Walls" layer for vertical movement
        if self.detect_collision_with_walls(self.walls_layer) and self.moving_up:
            self.y = prev_y
            self.world_y = self.y
            self.x = prev_x
            self.world_x = self.x
            
        if self.detect_collision_with_walls(self.walls_layer) and self.moving_down:
            self.y = prev_y
            self.world_y = self.y
            self.x=prev_x
            self.world_x = self.x

I tried elif statements and that doesnt seem to help because both conditions are still technically true when the lower priority ones execute

Upvotes: 0

Views: 74

Answers (1)

Brayden
Brayden

Reputation: 53

In order to prevent the locking while still being able to slide against the wall, you would need to know which direction (x or y) to cancel. You would need to do something like:

if self.colliding_with_wall(self.walls_layer, Direction.LEFT) and self.moving_left:
    self.x = prev_x
    self.world_x = self.x
if self.colliding_with_wall(self.walls_layer, Direction.RIGHT) and self.moving_right:
    self.x = prev_x
    self.world_x = self.x

# Check for collisions with the "Walls" layer for vertical movement
if self.colliding_with_wall(self.walls_layer, Direction.ABOVE) and self.moving_up:
    self.y = prev_y
    self.world_y = self.y
        
if self.colliding_with_wall(self.walls_layer, Direction.BELOW) and self.moving_down:
    self.y = prev_y
    self.world_y = self.y

Make sure to implement the function as well:

from enum import Enum, unique

@unique
class Direction(Enum):
    ABOVE = "above"
    BELOW = "below"
    LEFT = "left"
    RIGHT = "right"

def colliding_with_wall(walls, from_direction: Direction) -> bool:
    # Do the same loop to iterate over the walls as before but
    # add an additional check for which direction the player
    # hit the wall from.

    # Save the positions of the wall that the player encountered.
    # I'm not sure if you have 2D or 3D walls in your game so I'll just
    # do a 3D version and you can remove the extra code if yours is
    # only 2D.

    # Make a tuple of the wall's coordinates
    # I used ((top_left_x, top_left_y), (bottom_right_x, bottom_right_y))
    # Used made-up numbers here. Make sure you replace it with the
    # actual coordinates of the wall.
    wall = ((5, 10), (6, 14))

    # I assume that your player isn't single point so you're gonna need
    # to do something similar for the player.
    # Unless, of course, your game works on a tile system where the grid
    # is something like 8x8. In that case, the player only has one
    # coordinate.
    player = ((3, 9), (5, 11))

    # Here, I'll assume that the origin of your game (0, 0) is in the
    # top-left corner of the window. And that y increases going down.

    # I'll also assume that you use a `return false` before this point if
    # The player is not touching a wall at all.
    if from_direction == Direction.ABOVE:
        # Without more information about how your coordinate system works
        # (if it's like Minecraft's position system where the coordinate
        # values are floats/a large number with lots of precision, or
        # if it uses a tile map where you jump from tile to tile)

        # Check if the player is within the edges of the wall
        if player[0][0] < wall[1][0] and player[1][0] > wall[0][0]:
            # We're checking if the player hit the wall so the player
            # must be below the wall.

            # Use the threshold value (5 in this case) to adjust the
            # collision sensitivity based on how much your player moves
            # between function calls.
            if abs(player[0][1] - wall[1][1]) < 5:
                return true
    elif from_direction == Direction.BELOW:
        if player[0][0] < wall[1][0] and player[1][0] > wall[0][0]:
            if abs(player[1][1] - wall[0][1]) < 5:
                return true
    elif from_direction == Direction.LEFT:
        if player[0][1] < wall[1][1] and player[1][1] > wall[0][1]:
            if abs(player[0][0] - wall[1][0]) < 5:
                return true
    elif from_direction == Direction.RIGHT:
        if player[0][1] < wall[1][1] and player[1][1] > wall[0][1]:
            if abs(player[1][0] - wall[0][0]) < 5:
                return true
    else:
        # An invalid direction was entered so you can throw an error
        # here or just remove `else:` and ignore it.
        # If you remove `else:`, you must return either true of false.

If you don't want to hard code values for the Direction enum, you can also use auto. You shouldn't need @unique here based on what Python's Enum docs say.

from enum import Enum, auto

class Direction(Enum):
    ABOVE = auto()
    BELOW = auto()
    LEFT = auto()
    RIGHT = auto()

That should allow you to prevent moving through a wall while still being able to move in the other direction. Keep in mind that StackOverflow isn't exactly an IDE by which I mean that there is no syntax checking and I didn't have the time to actually test this so make sure you check and test this code.


As an additional note, I'd change the movement to only move when not touching a wall instead of moving and if you're touching a wall, then move back. It just makes more sense in my head but this way is fine as well.

Upvotes: 0

Related Questions