0
\$\begingroup\$

I am continuing to work on collision detection. It appears for the most part, everything I have implemented is working. When the game starts, the character is on the left side of the screen (plus a few pixels). If I hold down the left key so he is pushed against the wall, he doesn't go anywhere like is supposed to happen.

However, when I move the character to the right up to the first ledge something weird happens:

At first, he doesn't go through the ledge like he's supposed to:

enter image description here

But then, after holding down the right key for a while, he starts to creep into the ledge:

enter image description here

If I hold down the right key long enough, he ends up inside the ledge completely:

(can't post another pic, because I don't have enough rep)

There is no rhyme or reason as to how long it takes for him to creep forward. It seems to happen at random intervals.

Any clues as to what is happening? The weirdest thing to me is that it only happens when he is colliding with the left side of a "wall" and not the right side. I held down the left key on the left side of the map (the beginning) for quite a while and nothing happened, again like is supposed to happen.

Relevant code:

def col_ground(self, sprite, wall):
    return self.walls[wall].collidepoint(sprite.feet.midbottom)

def col_left(self, sprite, wall):
    return self.walls[wall].collidepoint(sprite.feet.midleft)

def col_right(self, sprite, wall):
    return self.walls[wall].collidepoint(sprite.feet.midright)

def update(self, dt):
    """ Tasks that occur over time should be handled here
    """
    self.group.update(dt)
    # sprite must have a rect called feet, and move_back method,
    # otherwise this will fail

    # TODO: this could probably be modified to handle anytime
    # the sprite is just on the ground or hitting a corner instead
    # of having seprate instances of where the sprite hits the right
    # or left side of a wall
    for sprite in self.group.sprites():
        wall_collide = sprite.feet.collidelist(self.walls)
        walls = sprite.feet.collidelistall(self.walls)

        if wall_collide > -1:
            # if the sprite hits the ground
            if self.col_ground(sprite, wall_collide):
                self.hero.jumping = False
                sprite.move_back_y(dt)
            # if the sprite's right side hits a wall
            if self.col_left(sprite, wall_collide):
                sprite.move_back_x(dt)
            # if the sprite's left side hits a wall
            if self.col_right(sprite, wall_collide):
                sprite.move_back_x(dt)
            # if the sprite is in a corner
            if len(walls) == 2:
                # if in a corner, keep him from going up or down (x, y)
                if not self.walls[walls[1]].collidepoint(sprite.feet.topleft) and \
                not self.walls[walls[0]].collidepoint(sprite.feet.topleft):
                    sprite.move_back_x(dt)
                if not self.walls[walls[0]].collidepoint(sprite.feet.topright) and \
                not self.walls[walls[1]].collidepoint(sprite.feet.topright):
                    sprite.move_back(dt)

If you need any more code snippets or explanation, please let me know.

Edit (movement functions):

def move_back_x(self, dt):
    """ If called after an update, the sprite can move back
    in the x
    """
    self._position[0] = self._old_position[0]
    self.rect.topleft = self._position
    self.feet.midbottom = self.rect.midbottom

def move_back_y(self, dt):
    """ If called after an update, the sprite can move back
    in the y direction
    """
    self._position[1] = self._old_position[1]
    self.rect.topleft = self._position
    self.feet.midbottom = self.rect.midbottom

def move_back(self, dt):
    """ If called after an update, the sprite can move back
    """
    self._position = self._old_position
    self.rect.topleft = self._position
    self.feet.midbottom = self.rect.midbottom
\$\endgroup\$
  • \$\begingroup\$ What is your move_back_x() function doing? If you're using a pygame Rect object for your positioning, then that's likely going to be your problem. Rects only take integer values, and float values will be truncated. This means 1.5 becomes 1, while -1.5 becomes -2. That means that moving in left and right directions will not be symmetrical. Show the rest of your movement code and we can see for sure. \$\endgroup\$ – Chris Sep 5 '16 at 18:04
  • \$\begingroup\$ @chris code added \$\endgroup\$ – David Sep 7 '16 at 5:31
  • \$\begingroup\$ @chris just printing the values, it looks like you're right. The new position gets values that are rounded incorrectly \$\endgroup\$ – David Sep 7 '16 at 5:40
  • \$\begingroup\$ @chris. I just rounded the functions to get the correct int value. Unfortunately now, it's noticeably jittery. I guess the way I go about collisions isn't very good in the first place -__-. Oh well. If you put that as an answer I'll accept it \$\endgroup\$ – David Sep 7 '16 at 5:47
  • \$\begingroup\$ @Chris "Truncate" means to cut off the decimal points, not to round them. So 1.5 becomes 1 and -1.5 becomes -1. \$\endgroup\$ – Ted Klein Bergman Sep 9 '16 at 16:27
0
\$\begingroup\$

A good way to handle this is to use a separate variable for your position - one which can be a float. Do your calculations on this variable, and then just use that to set your rect position each frame.

For example:

def update(self, dt):
    posx += self.vel * dt
    # collision code here, adjust posx
    self.rect.x = posx
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.