椭圆和圆之间的碰撞检测
Collision detection between an ellipse and a circle
我想做一个椭圆和圆的碰撞检测。我这样做的方式是:
- 计算椭圆中心到圆的角度
- 计算点在那个角度的椭圆中的位置
- 检查与那个点的碰撞
但是,我遇到了一个小问题。当我计算角度时,它似乎偏离了 90 度。我通过简单地添加 1.5 弧度来解释 90 度来做了一个肮脏的修复,它有点工作但存在不一致并且在某些角度不能正常工作,特别是在 0.7 和 -2.6 弧度左右。这是代码(所有碰撞的东西都在 Ellipse
class 中的 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()
第一个错误是,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? and 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()
我想做一个椭圆和圆的碰撞检测。我这样做的方式是:
- 计算椭圆中心到圆的角度
- 计算点在那个角度的椭圆中的位置
- 检查与那个点的碰撞
但是,我遇到了一个小问题。当我计算角度时,它似乎偏离了 90 度。我通过简单地添加 1.5 弧度来解释 90 度来做了一个肮脏的修复,它有点工作但存在不一致并且在某些角度不能正常工作,特别是在 0.7 和 -2.6 弧度左右。这是代码(所有碰撞的东西都在 Ellipse
class 中的 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()
第一个错误是,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? and 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)
# [...]
最小示例:
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()