2

I am making a game that connects a line between a 2 players. I want to make it so I can tell when an object (which is also a sprite) collides with the line.

The way I thought of doing this is creating a line that acts a sprite. The line will be able to change length depending on where the players are.

I'm a bit new to PyGame so I'm not too sure on what I have so far:

class Line(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface([400,400])
        self.image.set_colorkey((0,0,0))
        self.rect = self.image.get_rect()
        self.rect.x = 0
        self.rect.y = 0
        pygame.draw.line(screen,(0,0,255),(0,0),(400,400),2)

NOTE: A similar post to this already exists, however, what the person is asking in that post is different than in this post. The general idea may be the same, but I'd like to know a simpler way.

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
Bagel
  • 82
  • 11
  • You're on the right track! Try attaching the line to a specific point (e.g. the center of their sprite) on each player, and then updating the line when the players move (e.g. place it upon their center again). – Conduit Dec 24 '15 at 17:48
  • I don't know if Pygame implements its own collision, but here is some advice if it doesn't: collision with lines is a bit hard - you will need to look at the movement path of objects that can be "hit" over the course of the frame you are animating. If an object's hitbox starts on one side of the line and moves to the other, then you have a collision. – Conduit Dec 24 '15 at 17:51

1 Answers1

3

I recommend to test the collision with bit masks. See How can I made a collision mask? and the documentation of pygame.sprite.collide_mask():

Collision detection between two sprites, using masks.

collide_mask(SpriteLeft, SpriteRight) -> point

Tests for collision between two sprites, by testing if their bitmasks overlap. If the sprites have a "mask" attribute, that is used as the mask, otherwise a mask is created from the sprite image. Intended to be passed as a collided callback function to the *collide functions. Sprites must have a "rect" and an optional "mask" attribute.

All you have to do is add a mask attribute to the Sprites classes. Use pygame.mask.from_surface to creates a Mask from the given Surface:

class Line(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface([400, 400])
        self.image.set_colorkey((0, 0, 0))
        self.rect = self.image.get_rect()
        self.rect.x = 0
        self.rect.y = 0
        pygame.draw.line(screen, (0, 0, 255), (0, 0 ), (400, 400), 2)

        self.mask = pygame.mask.from_surface(self.image)

Use pygame.sprite.collide_mask() to detect the collision between 2 Sprites with a Mask:

class SpriteObject(pygame.sprite.Sprite):
    def __init__(self, x, y, image):
        super().__init__()
        self.image = image
        self.rect = self.image.get_rect(center = (x, y))
        self.mask = pygame.mask.from_surface(self.image)
other_sprite = SpriteObject(0, 0, sprite_image)
line_sprite = Line(*window.get_rect().center)
if pygame.sprite.collide_mask(line_sprite, other_sprite):
    print("hit")

See also Sprite mask


Minimal example:

import math
import pygame

class SpriteObject(pygame.sprite.Sprite):
    def __init__(self, x, y, image):
        super().__init__()
        self.image = image
        self.rect = self.image.get_rect(center = (x, y))
        self.mask = pygame.mask.from_surface(self.image)
    def update(self):
        self.rect.center = pygame.mouse.get_pos()

class Line(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.Surface((200, 200))
        self.image.set_colorkey((0, 0, 0))
        self.rect = self.image.get_rect(center = (x, y))
        self.angle = 0
    def update(self):
        vec = round(math.cos(self.angle * math.pi / 180) * 100), round(math.sin(self.angle * math.pi / 180) * 100)
        self.angle = (self.angle + 1) % 360
        self.image.fill(0)
        pygame.draw.line(self.image, (255, 255, 0), (100 - vec[0], 100 - vec[1]), (100 + vec[0], 100 + vec[1]), 5)
        self.mask = pygame.mask.from_surface(self.image)
        
pygame.init()
window = pygame.display.set_mode((500, 500))
clock = pygame.time.Clock()

sprite_image = pygame.image.load('AirPlane.png').convert_alpha()
moving_object = SpriteObject(0, 0, sprite_image)
line_object = Line(*window.get_rect().center)
all_sprites = pygame.sprite.Group([moving_object, line_object])
red = 0

run = True
while run:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
 
    all_sprites.update()

    if pygame.sprite.collide_mask(moving_object, line_object):
        red = min(255, red+4)
    else: 
        red = 0

    window.fill((red, 0, 0))
    all_sprites.draw(window)
    pygame.display.flip()

pygame.quit()
exit()
Rabbid76
  • 202,892
  • 27
  • 131
  • 174