Skip to content Skip to sidebar Skip to footer

How To Get Coordinates/area Of Collision In Pygame

I have a game where the bullet is moving so fast that in a single frame it is already across the screen. That being said, it has already collided with multiple walls. Currently, I

Solution 1:

If the bullets travel too fast to collide with the walls or enemies, you need ray casting (or alternatively move the bullets in multiple small steps). Here's a simple ray casting example that returns the closest collision point. I use vectors and pygame.Rect.collidepoint to see if a point along the heading vector collides with an obstacle.

import sys
import pygame as pg
from pygame.math import Vector2


classWall(pg.sprite.Sprite):

    def__init__(self, x, y, w, h, *groups):
        super().__init__(*groups)
        self.image = pg.Surface((w, h))
        self.image.fill(pg.Color('goldenrod4'))
        self.rect = self.image.get_rect(topleft=(x, y))


defray_cast(origin, target, obstacles):
    """Calculate the closest collision point.

    Adds the normalized `direction` vector to the `current_pos` to
    move along the heading vector and uses `pygame.Rect.collidepoint`
    to see if `current_pos` collides with an obstacle.

    Args:
        origin (pygame.math.Vector2, tuple, list): Origin of the ray.
        target (pygame.math.Vector2, tuple, list): Endpoint of the ray.
        obstacles (pygame.sprite.Group): A group of obstacles.

    Returns:
        pygame.math.Vector2: Closest collision point or target.
    """
    current_pos = Vector2(origin)
    heading = target - origin
    # A normalized vector that points to the target.
    direction = heading.normalize()
    for _ inrange(int(heading.length())):
        current_pos += direction
        for sprite in obstacles:
            # If the current_pos collides with an# obstacle, return it.if sprite.rect.collidepoint(current_pos):
                return current_pos
    # Otherwise return the target.return Vector2(target)


defmain():
    screen = pg.display.set_mode((640, 480))
    clock = pg.time.Clock()
    all_sprites = pg.sprite.Group()
    walls = pg.sprite.Group()
    Wall(100, 170, 90, 20, all_sprites, walls)
    Wall(200, 100, 20, 140, all_sprites, walls)
    Wall(400, 60, 150, 100, all_sprites, walls)

    pos = Vector2(320, 440)
    done = Falsewhilenot done:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                done = True

        all_sprites.update()
        collision_point = ray_cast(pos, pg.mouse.get_pos(), walls)
        screen.fill((30, 30, 30))
        all_sprites.draw(screen)
        pg.draw.line(screen, (50, 190, 100), pos, pg.mouse.get_pos(), 2)
        pg.draw.circle(screen, (40, 180, 250), [int(x) for x in collision_point], 5)

        pg.display.flip()
        clock.tick(30)


if __name__ == '__main__':
    pg.init()
    main()
    pg.quit()
    sys.exit()

Solution 2:

Unfortunately PyGame doesn't give us a built-in means of returning collision points easy way of doing this so there is a bit of leg work to do.

However, before I explain that, you mentioned in your question that the bullet is moving very fast. I'm not sure how fast we are talking so this might not apply, but in my experience collision becomes a hit and miss at high speeds, especially if you're working on a slower computer.

Assuming that ^ isn't applicable:

We can use pygame.Rect.colliderect to trigger an if statement.

if <bullet-rect>.collidepoint(<wall-rect>):
  print(bullet_x, bullet_y)

Simply swap out and for the actual rects and you should be good to go. One thing to note is that if the bullet is moving right to left, you will have to add the bullet's width to the x value, and if the bullet is moving top to bottom, you will have to add the bullet's height to the y value.

Note: Remember to add pygame.Rect(<bullet-rect>) and pygame.Rect(<wall-rect>) to each value or you'll get an error.

Post a Comment for "How To Get Coordinates/area Of Collision In Pygame"