【问题标题】:Collision detection between an ellipse and a circle椭圆和圆之间的碰撞检测
【发布时间】:2020-09-28 18:45:41
【问题描述】:

我想在椭圆和圆之间进行碰撞检测。我这样做的方式是:

  1. 计算椭圆中心到圆的角度
  2. 计算点在该角度的椭圆中的位置
  3. 检查与该点的碰撞

但是,我遇到了一个小问题。当我计算角度时,它似乎偏离了 90 度。我做了一个肮脏的修复,简单地添加 1.5 弧度来解释 90 度,它有点工作,但存在不一致并且在某些角度不能正常工作,特别是在 0.7 和 -2.6 弧度左右。下面是代码(所有的冲突都在Ellipse类的collision方法中)

import pygame
from math import sin, cos, atan2, radians
pygame.init()

SW = 1200
SH = 600
WIN = pygame.display
D = WIN.set_mode((SW, SH))

class Circle:
    def __init__(self, radius):
        self.x = 0
        self.y = 0
        self.radius = radius

    def update(self, pos):
        self.x = pos[0]
        self.y = pos[1]

    def draw(self, display):
        pygame.draw.circle(display, (255, 0, 0), (int(self.x), int(self.y)), self.radius, 2)

circle = Circle(30)

class Ellipse:
    def __init__(self, centre, rx, ry):
        self.centre = centre
        self.collided = False
        self.rx = rx
        self.ry = ry
        
    def draw(self, display):
        angle = 0
        while angle < 6.28:
            angle += 0.001
            x = self.centre[0] + sin(angle)* self.rx
            y = self.centre[1] + cos(angle)* self.ry
            if self.collided:
                display.set_at((int(x), int(y)), (255, 0, 0))
            else:
                display.set_at((int(x), int(y)), (0, 0, 255))
        pygame.draw.circle(D, (0, 255, 0), (int(self.centre[0]), int(self.centre[1])), 5)

    def collision(self, circle):
        #angle to the circle
        dx = circle.x - self.centre[0]
        dy = circle.y - self.centre[1]
        angle = atan2(-dy, dx)
        print(angle)

        #where the point lies in the ellipse at that angle
        x = sin(angle + 1.5)* self.rx + self.centre[0] 
        y = cos(angle + 1.5)* self.ry + self.centre[1]
        #print(x, y)

        #drawing the point just to make sure its working
        # (debugging)
        pygame.draw.circle(D, (0, 255, 0), (int(x), int(y)), 5)

        # distance between the point we just
        # calculated and the circle's centre
        distance = ((x-circle.x)**2 + (y-circle.y)**2)**0.5
        #print(distance)

        #collision condition
        if distance < circle.radius:
            self.collided = True
        else:
            self.collided = False
        
ellipse = Ellipse([600, 300], 300, 200)

while True:
    events = pygame.event.get()
    mousePos = pygame.mouse.get_pos()
    for event in events:
        if event.type == pygame.QUIT:
            pygame.quit()
    D.fill((255, 255, 255))

    circle.update(mousePos)
    circle.draw(D)
    ellipse.draw(D)
    ellipse.collision(circle)
    
    pygame.display.flip()

【问题讨论】:

    标签: python math pygame collision-detection ellipse


    【解决方案1】:

    第一个错误是,1.5不等于pi/2

    from math import pi
    

    x = sin(angle + 1.5)* self.rx + self.centre[0]
    y = cos(angle + 1.5)* self.ry + self.centre[1]

    x = sin(angle + pi/2)* self.rx + self.centre[0] 
    y = cos(angle + pi/2)* self.ry + self.centre[1]
    

    其次,用角度计算椭圆上的点有点复杂。查看How to get a point on an ellipse's outline given an angle?Calculating a Point that lies on an Ellipse given an Angle问题的答案:

    from math import pi, sin, cos, atan2, radians, copysign, sqrt
    
    class Ellipse:
        # [...]
    
        def pointFromAngle(self, a):
            c = cos(a)
            s = sin(a)
            ta = s / c  ## tan(a)
            tt = ta * self.rx / self.ry  ## tan(t)
            d = 1. / sqrt(1. + tt * tt)
            x = self.centre[0] + copysign(self.rx * d, c)
            y = self.centre[1] - copysign(self.ry * tt * d, s)
            return x, y
    
        def collision(self, circle):
            # [...]
    
            #where the point lies in the ellipse at that angle
            x, y = self.pointFromAngle(angle)
    
            # [...]
    

    最小示例: repl.it/@Rabbid76/PyGame-IntersectCircleEllipse

    import math
    import pygame
    
    class Circle:
        def __init__(self, center_x, center_y, radius):
            self.center = center_x, center_y
            self.radius = radius
        def update(self, center_x, center_y):
            self.center = center_x, center_y
        def draw(self, surface):
            pygame.draw.circle(surface, (255, 0, 0), (round(self.center[0]), round(self.center[1])), self.radius, 3)
    
    class Ellipse:
        def __init__(self, center, vertex):
            self.center = center
            self.collided = False
            self.vertex = vertex
        def draw(self, surface):
            bounding_rect = pygame.Rect(0, 0, self.vertex[0] * 2, self.vertex[1] * 2)
            bounding_rect.center = round(self.center[0]), round(self.center[1])
            pygame.draw.ellipse(surface, (0, 255, 0), bounding_rect, 3)
        def pointFromAngle(self, a):
            c = math.cos(a)
            s = math.sin(a)
            ta = s / c  ## tan(a)
            tt = ta * self.vertex[0] / self.vertex[1]  ## tan(t)
            d = 1. / math.sqrt(1. + tt * tt)
            x = self.center[0] + math.copysign(self.vertex[0] * d, c)
            y = self.center[1] - math.copysign(self.vertex[1] * tt * d, s)
            return x, y
    
    def intersect_circle_ellipse(circle, ellipse):
        dx = circle.center[0] - ellipse.center[0]
        dy = circle.center[1] - ellipse.center[1]
        angle = math.atan2(-dy, dx)
        x, y = ellipse.pointFromAngle(angle)
        distance = math.hypot(x - circle.center[0], y-circle.center[1])
        return distance <= circle.radius, (x, y) 
    
    pygame.init()
    window = pygame.display.set_mode((500, 300))
    circle = Circle(0, 0, 30)      
    ellipse = Ellipse(window.get_rect().center, (150, 100))
    run = True
    while run:
        events = pygame.event.get()
        mousePos = pygame.mouse.get_pos()
        for event in events:
            if event.type == pygame.QUIT:
                run = False
                
        circle.update(*mousePos)
        isect = intersect_circle_ellipse(circle, ellipse)
    
        window.fill((255, 255, 255))
        circle.draw(window)
        ellipse.draw(window) 
        color = (255, 0, 255) if isect[0] else (0, 0, 255)
        pygame.draw.circle(window, color, (round(isect[1][0]), round(isect[1][1])), 5)
        pygame.display.flip()
    
    pygame.quit()
    exit()
    

    【讨论】:

    • 感谢您的回答。我明白为什么您发布的解决方案会起作用,但我不明白为什么我的解决方案不起作用,因为绘制的点显然一直在椭圆上。我很困惑。
    • @hippozhipos 您已将sin(angle)cos(angle) 缩放了不同的长度。注意向量 (cos(angle) * a, sin(angle) * b) 的角度与向量 (cos(angle), sin(angle ))。如果 a == b 也是一样。如果 a == b 那么椭圆是一个圆。
    • 哦,我明白了 - 有点。谢谢
    猜你喜欢
    • 2014-05-05
    • 1970-01-01
    • 2018-06-02
    • 1970-01-01
    • 2012-01-15
    • 2023-01-26
    • 1970-01-01
    • 1970-01-01
    • 2016-02-17
    相关资源
    最近更新 更多