Pygame: Collision by Sides of Sprite
Asked Answered
C

5

8

Is there a way in pygame to look for a collision between the a particular side of a sprite and a particular side of another sprite in pygame? For example, if the top of sprite A collides with the bottom of Sprite B, return True.

I am certain there is a way to do this, but I can't find any particular method in the documentation.

Cornice answered 24/11, 2013 at 20:30 Comment(2)
Do you care which sides take part in the collision? I.e. do you want to include only collisions involving, say, top and bottom sides only and ignore the others or don't care about this at all?Hammad
Also post something which will make people see that you've actually attempted the problem, ideally some of your code which you've written to tackle it.Hammad
B
18

There is no function to get sides collision in PyGame.

But you could try to use pygame.Rect.collidepoint to test if A.rect.midleft, A.rect.midright, A.rect.midtop, A.rect.midbottom, A.rect.topleft, A.rect.bottomleft , A.rect.topright, A.rect.bottomright are inside B.rect (pygame.Rect).


EDIT:

Example code. Use arrows to move player and touch enemy.

(probably it is not optimal solution)

import pygame

WHITE = (255,255,255)
BLACK = (0  ,0  ,0  )
RED   = (255,0  ,0  )
GREEN = (0  ,255,0  )
BLUE  = (0  ,0  ,255)

class Player():

    def __init__(self, x=0, y=0, width=150, height=150):

        self.rect = pygame.Rect(x, y, width, height)

        self.speed_x = 5
        self.speed_y = 5

        self.move_x = 0
        self.move_y = 0

        self.collision = [False] * 9

        self.font = pygame.font.SysFont("", 32)
        self.text = "";

    def set_center(self, screen):
        self.rect.center = screen.get_rect().center

    def event_handler(self, event):
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                self.move_x -= self.speed_x
            elif event.key == pygame.K_RIGHT:
                self.move_x += self.speed_x
            elif event.key == pygame.K_UP:
                self.move_y -= self.speed_y
            elif event.key == pygame.K_DOWN:
                self.move_y += self.speed_y

        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT:
                self.move_x += self.speed_x
            elif event.key == pygame.K_RIGHT:
                self.move_x -= self.speed_x
            elif event.key == pygame.K_UP:
                self.move_y += self.speed_y
            elif event.key == pygame.K_DOWN:
                self.move_y -= self.speed_y

    def update(self):
        self.rect.x += self.move_x
        self.rect.y += self.move_y

    def draw(self, screen):

        pygame.draw.rect(screen, WHITE, self.rect, 2)
        self.draw_point(screen, self.rect.topleft, self.collision[0])
        self.draw_point(screen, self.rect.topright, self.collision[1])
        self.draw_point(screen, self.rect.bottomleft, self.collision[2])
        self.draw_point(screen, self.rect.bottomright, self.collision[3])

        self.draw_point(screen, self.rect.midleft, self.collision[4])
        self.draw_point(screen, self.rect.midright, self.collision[5])
        self.draw_point(screen, self.rect.midtop, self.collision[6])
        self.draw_point(screen, self.rect.midbottom, self.collision[7])

        self.draw_point(screen, self.rect.center, self.collision[8])

    def draw_point(self, screen, pos, collision):
        if not collision:
            pygame.draw.circle(screen, GREEN, pos, 5)
        else:
            pygame.draw.circle(screen, RED, pos, 5)

    def check_collision(self, rect):
        self.collision[0] = rect.collidepoint(self.rect.topleft)
        self.collision[1] = rect.collidepoint(self.rect.topright)
        self.collision[2] = rect.collidepoint(self.rect.bottomleft)
        self.collision[3] = rect.collidepoint(self.rect.bottomright)

        self.collision[4] = rect.collidepoint(self.rect.midleft)
        self.collision[5] = rect.collidepoint(self.rect.midright)
        self.collision[6] = rect.collidepoint(self.rect.midtop)
        self.collision[7] = rect.collidepoint(self.rect.midbottom)

        self.collision[8] = rect.collidepoint(self.rect.center)

    def render_collision_info(self):

        text = "collision: "
        print "collision:",

        if self.collision[0] or self.collision[2] or self.collision[4]:
            text += "left "
            print "left",

        if self.collision[1] or self.collision[3] or self.collision[5]:
            text += "right "
            print "right",

        if self.collision[0] or self.collision[1] or self.collision[6]:
            text += "top "
            print "top",

        if self.collision[2] or self.collision[3] or self.collision[7]:
            text += "bottom "
            print "bottom",

        if self.collision[8]:
            text += "center "
            print "center",

        print

        self.text = self.font.render(text, 1, WHITE)

    def draw_collision_info(self, screen, pos):
        screen.blit(self.text, pos)

#----------------------------------------------------------------------

class Game():

    def __init__(self):

        pygame.init()

        self.screen = pygame.display.set_mode( (800,600) )
        pygame.display.set_caption("Side Collision")

        self.player = Player()
        self.enemy  = Player()
        self.enemy.set_center(self.screen)


    def run(self):
        clock = pygame.time.Clock()

        RUNNING = True

        while RUNNING:

            # --- events ----

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    RUNNING = False

                elif event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        RUNNING = False

                self.player.event_handler(event)

            # --- updates ---

            self.player.update()
            self.enemy.update()

            self.player.check_collision(self.enemy.rect)
            self.enemy.check_collision(self.player.rect)
            self.player.render_collision_info()
            self.enemy.render_collision_info()

            # --- draws ----

            self.screen.fill(BLACK)

            self.player.draw(self.screen)
            self.enemy.draw(self.screen)

            self.player.draw_collision_info(self.screen, (0,0))
            self.enemy.draw_collision_info(self.screen, (0,32))

            pygame.display.update()

            # --- FPS ---

            clock.tick(30)

        pygame.quit()

#----------------------------------------------------------------------

Game().run()

enter image description here


EDIT (08.2016): version with colllisions rect, rect_ratio, circle

GitHub: furas/python-examples/pygame/collisions

enter image description here

Baecher answered 25/11, 2013 at 2:14 Comment(0)
C
3

I solved this by creating multiple collide boxes. This should help a lot of people.

https://youtu.be/W-Vz6le1YUg

Code:

if tanner.axe.colliderect(oak.red) and. 
      tanner.playerHitBox.colliderect(oak.leftHit) and. 
      keys_pressed[pygame.K_SPACE]:
      Number_of_Hits_Left += 1
      print(Number_of_Hits_Left)

 if tanner.axe.colliderect(oak.red) and. 
       tanner.playerHitBox.colliderect(oak.rightHit) and. 
       keys_pressed[pygame.K_SPACE]:
       Number_of_Hits_Right += 1
       print(Number_of_Hits_Right)

So I have a total of 5 hit boxes to accomplish said mission. And really all you would have to do is create your main hit box, then create 2 side boxes on the left and right side of main hit box, so that they barely overlap. So let's say you shoot a bullet your code would be something like above. "When bullet collides with side box AND when bullet collides with main box, do something."

Confederation answered 25/6, 2021 at 5:23 Comment(0)
H
2

The logic behind collision is like that:

#Assume two surfaces
objectA
objectB

if objectA.right > objectB.left and objectA.left < objectB.right and objectA.top < objectB.bottom and objectA.bottom > objectB.top
    #Collision happens

if you want to detect side collision (as if objectA hitted objectB on the side) you can do the following:

#Here is the code where objectA moves
objectA.X += 10
#Here check Collision, if collision happens objectA hitted objectB from the side
objectA.Y += 10
#Here check Collision again, if collision is true then objectA hitted object B from top/bottom
Hypersensitize answered 28/11, 2013 at 9:24 Comment(0)
A
1

Like Furas has said, no, there is not way to get side collisions in Pygame past the point system he set up. And even that one wont give you what you want, because you can never be sure which direction the collision happened when dealing with rows, columns or corners of Rectangles.

This is why most tutorials recommend saving your sprites initial direction. then moving in the opposite direction in case of a collision.

Autarchy answered 25/11, 2013 at 15:51 Comment(0)
M
0

For objectA give the object this method:

def is_collided_with(self, sprite): return self.rect.colliderect(sprite.rect)

This return statement returns either True or False

then in the main loop for collisions just do: if objectA.is_collided_with(ObjectB): Collision happened!

Mineraloid answered 2/11, 2019 at 21:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.