0
\$\begingroup\$

The way I handle simple collision detection causes a really frequent and easy to reproduce glitch, which affects gameplay severely. Here's my code (Python 3.5):

 def move(self, _map):
    self.col = False
    for y in _map.actors:
        for actor in y:
            if actor[1]:
                if not (((self.y + self.h) < (actor[1].y - self.cam.y)) or (self.y > ((actor[1].y - self.cam.y) + actor[1].h)) or (self.x > ((actor[1].x - self.cam.x) + actor[1].w)) or ((self.x + self.w) < (actor[1].x - self.cam.x))):
                    print(self.col)
                    self.col = True
    if not self.col:
        if self.directions[0]:
            self.cam.y -= self.speed
        if self.directions[1]:
            self.cam.y += self.speed
        if self.directions[2]:
            self.cam.x -= self.speed
        if self.directions[3]:
            self.cam.x += self.speed
    else:
        if self.directions[0]:
            self.cam.y += self.speed
        if self.directions[1]:
            self.cam.y -= self.speed
        if self.directions[2]:
            self.cam.x += self.speed
        if self.directions[3]:
            self.cam.x -= self.speed
    if self.running:
        self.speed = system.RUNNING_PLAYER_SPEED
    else:
        self.speed = system.NORMAL_PLAYER_SPEED
    self.x = ((self.cam.w / 2) - (self.w / 2))
    self.y = ((self.cam.h / 2) - (self.h / 2))

As you can see, I'm detecting collision against walls and whatever (Any actor in the map) in the player's "move" method. The logic is pretty straightforward, a basic bounding box collision test (I consider the position of the player relative to the camera, since the player is always in the center (It's the camera that moves)). If I just stop the player if there's a collision, the player gets stuck inside the wall, thus creating the necessity of "teleporting" the player to its former position, as you can see in this other question. The problem with that solution is that it leads to this glitch, which I need to illustrate in order to make myself clear.

In this first example, I'm going up (pressing W) towards the wall.

enter image description here

The collision works fine just pressing W, it gets teleported in the opposite direction at each collision. The problem is when I start pressing S (Down) at the same time. The player goes to the opposite direction, glitching up towards the other side of the wall, since the collision test always returns true when the player is "inside" the wall, just like this:

enter image description here

This would severely compromise the gameplay, since players would be able to play outside the boundaries of the map in multiplayer. How can I avoid this using bounding box collision or other method?

\$\endgroup\$
  • \$\begingroup\$ Could you add a link to your github or bitbucket where I can download a complete working minimal example of your problem? \$\endgroup\$ – James Nov 2 '16 at 4:21
1
\$\begingroup\$

I've finally realized how to solve the problem perfectly. I came to the conclusion that it's not a matter of detecting the collision in the present, but rather a matter of foreseeing the collision in the future. It's a matter of time! Either you record the positions of the player and then teleport him to his past in the event of a collision instead of moving him in the opposite direction in the present, or you check for a possible collision in the future. It works perfectly! The player can't get stuck inside the wall, or glitch through it, because the player never gets there! So that's the answer, future viewers: Accurate collision detection is all about time! (Not considering pixel-perfect detection, of course. That's not always necessary).

def move(self, _map):
    def check_collision(future_pos):
        for y in _map.actors:
            for actor in y:
                if actor[1]:
                    if not (((self.y + self.h) < (actor[1].y - future_pos[1])) or (self.y > ((actor[1].y - future_pos[1]) + actor[1].h)) or (self.x > ((actor[1].x - future_pos[0]) + actor[1].w)) or ((self.x + self.w) < (actor[1].x - future_pos[0]))):
                        return True
        return False
    if self.directions[0] and not check_collision((self.cam.x, self.cam.y - self.speed)):
        self.cam.y -= self.speed
    if self.directions[1] and not check_collision((self.cam.x, self.cam.y + self.speed)):
        self.cam.y += self.speed
    if self.directions[2] and not check_collision((self.cam.x - self.speed, self.cam.y)):
        self.cam.x -= self.speed
    if self.directions[3] and not check_collision((self.cam.x + self.speed, self.cam.y)):
        self.cam.x += self.speed
    if self.running:
        self.speed = system.RUNNING_PLAYER_SPEED
    else:
        self.speed = system.NORMAL_PLAYER_SPEED
    self.x = ((self.cam.w / 2) - (self.w / 2))
    self.y = ((self.cam.h / 2) - (self.h / 2))
\$\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.