Collision detection between a polygon and a circle

谁都会走 提交于 2021-01-05 12:32:28

问题


I was wondering how collision detection worked in pygame, between a polygon and a circle, for a football game. What I am trying to do, is make a ball move in the direction that the car hit it in. Please can somebody fix my code, I'm very stuck!!. If you run the following code, two cars will move, but nothing will happen if they hit the ball, and no error messages(Line 26 for my attempt at making it work). For starters, I just want it to print("Hello"). I would really appreciate if someone could help. Thank You

import pygame
from pygame.math import Vector2


pygame.init()
screen = pygame.display.set_mode((1150, 800))

redx = 50
redy = 30
bluex = 100
bluey = 30 
clock = pygame.time.Clock()  
BLUECAR_ORIGINAL = pygame.Surface((bluex, bluey), pygame.SRCALPHA)
pygame.draw.polygon(
    BLUECAR_ORIGINAL, (0, 0, 255), [(0, 30), (50, 20), (50, 10), (0, 0)])
bluecar = BLUECAR_ORIGINAL

REDCAR_ORIGINAL = pygame.Surface((redx,redy), pygame.SRCALPHA)
pygame.draw.polygon(
    REDCAR_ORIGINAL, (255, 0, 0), [(0, 0), (50, 10), (50, 20), (0, 30)])
redcar = REDCAR_ORIGINAL



def paddle_hit():
    if pygame.sprite.collide_rect(ball, bluerect):
        print("HI")
    elif pygame.sprite.collide_rect(ball, redrect):
        print("hello")









pos = Vector2(70, 70)  
vel = Vector2(7, 0)

poss = Vector2(70,70)
vell = Vector2(7,0)

redrect = redcar.get_rect(center=pos)
redangle = 0  

bluerect = bluecar.get_rect(center=pos)
blueangle = 0

ballx = 575
bally = 400




run = True
while run:
    ball = pygame.draw.circle(screen, [0,0,0],[ballx,bally],30)
    pygame.display.flip()
    for event in pygame.event.get():
    if event.type == pygame.QUIT:
        run = False






    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        redangle += 5
        vel.rotate_ip(-5)
        redcar = pygame.transform.rotate(REDCAR_ORIGINAL, redangle)

        redrect = redcar.get_rect(center=pos)
    elif keys[pygame.K_RIGHT]:
        redangle -= 5
        vel.rotate_ip(5)
        redcar = pygame.transform.rotate(REDCAR_ORIGINAL, redangle)
        redrect = redcar.get_rect(center=pos)


    if keys[pygame.K_a]:
        blueangle += 5
        vell.rotate_ip(-5)
        bluecar = pygame.transform.rotate(BLUECAR_ORIGINAL, blueangle)
        bluerect = bluecar.get_rect(center=pos)
    elif keys[pygame.K_d]:
        blueangle -= 5
        vell.rotate_ip(5)
        bluecar = pygame.transform.rotate(BLUECAR_ORIGINAL, blueangle)
        bluerect = bluecar.get_rect(center=poss)




    pos += vel
    redrect.center = pos  

    poss += vell
    bluerect.center = poss

    bgImg = pygame.image.load("Football_pitch.png")
    screen.blit(bgImg, (0,0))

    screen.blit(redcar, redrect)

    screen.blit(bluecar, bluerect)






    pygame.display.flip()
    clock.tick(60)

pygame.quit()    

回答1:


You could use masks for the collision detection and set the velocity of the ball to the velocity of the colliding player.

Create pygame.mask.Mask objects (with pygame.mask.from_surface) for the ball, the red and the blue car (when the cars rotate, you have to create new masks).

In the while loop, calculate the offsets between the cars and the ball and then call the overlap method to see if they overlap. It returns the point of collision if the masks collide, otherwise None. So if overlap doesn't return None, you can set the velocity of the ball to the velocity of the player and it will move in the same direction. Scale the velocity vector to kick the ball.

import pygame
from pygame.math import Vector2


pygame.init()
screen = pygame.display.set_mode((1150, 800))
clock = pygame.time.Clock()
# Images.
BG_IMG = pygame.Surface((1150, 800))
BG_IMG.fill((30, 120, 30))
BLUECAR_ORIGINAL = pygame.Surface((50, 30), pygame.SRCALPHA)
pygame.draw.polygon(
    BLUECAR_ORIGINAL, (0, 0, 255), [(0, 30), (50, 20), (50, 10), (0, 0)])
bluecar = BLUECAR_ORIGINAL
REDCAR_ORIGINAL = pygame.Surface((50, 30), pygame.SRCALPHA)
pygame.draw.polygon(
    REDCAR_ORIGINAL, (255, 0, 0), [(0, 0), (50, 10), (50, 20), (0, 30)])
redcar = REDCAR_ORIGINAL

BALL = pygame.Surface((30, 30), pygame.SRCALPHA)
pygame.draw.circle(BALL, [250,250,250], [15, 15], 15)
# Ball variables.
ball_pos = Vector2(575, 400)
ballrect = BALL.get_rect(center=ball_pos)
ball_vel = Vector2(0, 0)
# Car variables.
pos_red = Vector2(470, 370)
vel_red = Vector2(3, 0)
redrect = redcar.get_rect(center=pos_red)
redangle = 0
pos_blue = Vector2(70,70)
vel_blue = Vector2(3,0)
bluerect = bluecar.get_rect(center=pos_red)
blueangle = 0
# Masks.
mask_blue = pygame.mask.from_surface(bluecar)
mask_red = pygame.mask.from_surface(redcar)
mask_ball = pygame.mask.from_surface(BALL)

run = True
while run:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        redangle += 5
        vel_red.rotate_ip(-5)
        redcar = pygame.transform.rotate(REDCAR_ORIGINAL, redangle)
        redrect = redcar.get_rect(center=redrect.center)
        # We need a new mask after the rotation.
        mask_red = pygame.mask.from_surface(redcar)
    elif keys[pygame.K_RIGHT]:
        redangle -= 5
        vel_red.rotate_ip(5)
        redcar = pygame.transform.rotate(REDCAR_ORIGINAL, redangle)
        redrect = redcar.get_rect(center=redrect.center)
        mask_red = pygame.mask.from_surface(redcar)

    if keys[pygame.K_a]:
        blueangle += 5
        vel_blue.rotate_ip(-5)
        bluecar = pygame.transform.rotate(BLUECAR_ORIGINAL, blueangle)
        bluerect = bluecar.get_rect(center=bluerect.center)
        mask_blue = pygame.mask.from_surface(bluecar)
    elif keys[pygame.K_d]:
        blueangle -= 5
        vel_blue.rotate_ip(5)
        bluecar = pygame.transform.rotate(BLUECAR_ORIGINAL, blueangle)
        bluerect = bluecar.get_rect(center=bluerect.center)
        mask_blue = pygame.mask.from_surface(bluecar)

    # Move the cars.
    pos_red += vel_red
    redrect.center = pos_red
    pos_blue += vel_blue
    bluerect.center = pos_blue
    # Move the ball.
    ball_vel *= .99  # Friction.
    ball_pos += ball_vel
    ballrect.center = ball_pos

    # Red car collision.
    # We need the offset between the redrect and the ballrect.
    offset_red = redrect[0] - ballrect[0], redrect[1] - ballrect[1]
    # Pass the offset to the `overlap` method. If the masks collide,
    # overlap will return a single point, otherwise `None`.
    overlap_red = mask_ball.overlap(mask_red, offset_red)
    # Blue car collision.
    offset_blue = bluerect[0] - ballrect[0], bluerect[1] - ballrect[1]
    overlap_blue = mask_ball.overlap(mask_blue, offset_blue)

    if overlap_red and overlap_blue:  # Both collide with the ball.
        # Not sure what should happen here.
        ball_vel = vel_red + vel_blue * 1.4
    elif overlap_red:  # Red collides with the ball.
        ball_vel = Vector2(vel_red) * 1.4
    elif overlap_blue:  # Blue collides with the ball.
        ball_vel = Vector2(vel_blue) * 1.4

    # Drawing.
    screen.blit(BG_IMG, (0, 0))
    screen.blit(BALL, ballrect)
    screen.blit(redcar, redrect)
    screen.blit(bluecar, bluerect)
    pygame.display.flip()
    clock.tick(60)

pygame.quit()



回答2:


I've written a new python lib for collision detection between concave/convex polygons, and circles. It works very well, and is pretty efficient. It's pretty simple to use, and there are examples in the repository, as well as documentation.

https://github.com/QwekoDev/collision



来源:https://stackoverflow.com/questions/51705239/collision-detection-between-a-polygon-and-a-circle

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!