Detecting the direction of collision.

Detecting the direction of collision.

Postby dboxall123 » Sun Aug 18, 2013 9:29 pm

Sweet, cheers Mek. I have another question if you don't mind. I want to be able to kill enemies by jumping on them, but die if I contact the sides. But I'm having a bit of trouble. I found a sidescrolling game on Pygame and downloaded it to look at te code. In the code, he writes:
Code: Select all
if enemy.rect.colliderect(self.rect):
    top,right,bottom,left = collide_edges(self.rect,enemy.rect)

I try to do the same thing, but it crashes and says that collide_edges is not defined. However, this is written in Python 2 so it's gonna work differently (probably). So how can I do this with Python 3 and the latest Pygame? I've been looking through the documents and I can't find anything. There must be a way though. Any ideas?
dboxall123
 
Posts: 108
Joined: Fri Jul 12, 2013 5:28 pm

Detecting the direction of collision.

Postby Mekire » Mon Aug 19, 2013 12:54 am

dboxall123 wrote:I want to be able to kill enemies by jumping on them, but die if I contact the sides. But I'm having a bit of trouble. I found a sidescrolling game on Pygame and downloaded it to look at te code. In the code, he writes:
Code: Select all
if enemy.rect.colliderect(self.rect):
    top,right,bottom,left = collide_edges(self.rect,enemy.rect)

I try to do the same thing, but it crashes and says that collide_edges is not defined. However, this is written in Python 2 so it's gonna work differently (probably). So how can I do this with Python 3 and the latest Pygame? I've been looking through the documents and I can't find anything. There must be a way though. Any ideas?

Well whoever wrote that snippet had their own collide_edges function they wrote.

One simple way you could do this would be by checking specific points on the enemy to see if they collided with the player. For example
Code: Select all
if player.rect.collidepoint(enemy.rect.midtop):
If this were true you could assume the player hit the enemy from above (as long as the two are never permitted to stay overlapped). In cases where this isn't possible or isn't sufficient you can do something more complicated by calculating a finite difference of collision rects. I will try to post an example of the latter (or both) later when I have a computer I can actually code on.

-Mek
User avatar
Mekire
 
Posts: 816
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: Detecting the direction of collision.

Postby dboxall123 » Mon Aug 19, 2013 2:00 pm

Yeh that would be great, cheers Mek.
Ps- of course it was a function, how dumb could I be? I was looking through the code for a variable named collide_edges! What an idiot lol
dboxall123
 
Posts: 108
Joined: Fri Jul 12, 2013 5:28 pm

Re: Detecting the direction of collision.

Postby Mekire » Tue Aug 20, 2013 9:49 am

Ok let's take a shot here.

Two implementations. One naive and one complicated.

The following are methods of our Player class.

Naive implementation:
Code: Select all
def get_collision_direction_naive(self,other_sprite):
    """A naive implementation to find direction of collision."""
    check_dict = {"left"   : other_sprite.rect.midleft,
                  "right"  : other_sprite.rect.midright,
                  "top"    : other_sprite.rect.midtop,
                  "bottom" : other_sprite.rect.midbottom}
    for direction,test in check_dict.items():
        if self.rect.collidepoint(test):
            return direction
    return False
This just tests if the middle edge points of the other sprite we are colliding with are contained within our sprite's rect. Limited, but could serve your needs depending on what those might be.

Finite difference of mask collisions implementation:
Code: Select all
def get_collision_direction(self,other_sprite):
    """Find what side of an object the player is running into."""
    dx = self.get_finite_difference(other_sprite,0,self.speed)
    dy = self.get_finite_difference(other_sprite,1,self.speed)
    if abs(dx) > abs(dy):
        if dx > 0:
            return "right"
        else:
            return "left"
    else:
        if dy > 0:
            return "bottom"
        else:
            return "top"

def get_finite_difference(self,other_sprite,index,delta=1):
    """Find the finite difference in area of mask collision with the
    rects position incremented and decremented in axis index."""
    base_offset = [other_sprite.rect.x-self.rect.x,
                   other_sprite.rect.y-self.rect.y]
    offset_high = base_offset[:]
    offset_low = base_offset[:]
    offset_high[index] += delta
    offset_low[index] -= delta
    first_term = self.mask.overlap_area(other_sprite.mask,offset_high)
    second_term = self.mask.overlap_area(other_sprite.mask,offset_low)
    return first_term - second_term
This is a little harder to explain. We check the collisions between our mask and the mask we collide with, incrementing and decrementing our position by delta. We do this separately for the X-axis and the Y-axis. Comparing the relative results from these calculations can tell us the direction of collision (it can actually tell us the exact angle of collision if we are not restricted to orthogonal movement).


I have added examples of each to the four_dir_movement section of my example repo:
Naive implementation
Finite difference of mask collisions implementation

Good luck.
-Mek

Edit:
Oh, another possibly easier (and potentially more accurate) naive implementation. On collision calculate the angle of the vector between the center of your rect and the center of the other rect. If it is between certain ranges (say -45 to 45 degrees) you can make assumptions about the angle between the sprites. I'll write one up later.
User avatar
Mekire
 
Posts: 816
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: Detecting the direction of collision.

Postby dboxall123 » Wed Aug 21, 2013 9:17 am

Sweet,cheers Mek!
dboxall123
 
Posts: 108
Joined: Fri Jul 12, 2013 5:28 pm


Return to Game Development

Who is online

Users browsing this forum: No registered users and 1 guest