在 pygame 中使用碰撞点函数时,线被检测为矩形的对角线
Line is detected as diagonal of rectangle while using collidepoint function in pygame
我正在从事基于图形的项目。我正在画线来表示边缘。我的目标是在单击特定行时更改行的颜色。问题是 pygame 将线视为虚拟矩形的对角线。因此,即使我没有点击直线但鼠标位置位于虚拟矩形的投影区域中,使用 collidepoint() 时事件也会被检测为碰撞。我只想在实际行上单击鼠标时才检测到这一点。
我是 pygame 的新手,如果有其他我可以使用的函数或库,请告诉我。这是我项目的示例代码。
import pygame
pygame.init()
screen = pygame.display.set_mode((1200,700))
running = True
red = 0
green = 255
blue = 210
while running:
screen.fill((red,green,blue))
line = pygame.draw.line(screen, green, (50,50), (400,400),10)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
if line.collidepoint(event.pos):
red = 255
green = 0
blue = 0
if event.type == pygame.MOUSEBUTTONUP:
red = 0
green = 255
blue = 210
pygame.display.update()
pygame.draw.line
returns a pygame.Rect
object that defines the axis aligned bounding rectangle surrounding the line. collidepoint
测试点是否在矩形内。
你必须使用不同的方法。编写一个函数来计算点到直线的最短距离:
dist = abs(dot(normalized(NV), P - LP))
,其中NV
是直线的法向量, LP
是直线上的点,P
是需要计算距离的点
import math
def distance_point_line(pt, l1, l2):
nx, ny = l1[1] - l2[1], l2[0] - l1[0]
nlen = math.hypot(nx, ny)
nx /= nlen
ny /= nlen
vx, vy = pt[0] - l1[0], pt[1] - l1[1]
dist = abs(nx*vx + ny*vy)
return dist
与使用pygame.math.Vector2
相同的功能:
def distance_point_line(pt, l1, l2):
NV = pygame.math.Vector2(l1[1] - l2[1], l2[0] - l1[0])
LP = pygame.math.Vector2(l1)
P = pygame.math.Vector2(pt)
return abs(NV.normalize().dot(P -LP))
测试鼠标指针是否在线条定义的矩形内,距离是否小于线条宽度的一半:
if (line_rect.collidepoint(event.pos) and
distance_point_line(event.pos, (50,50), (400,400)) < 5):
# [...]
解释:
我使用了从点到线的 Dot product 距离。一般来说,2 个向量的点积等于 余弦 之间的夹角2 个向量乘以两个向量的大小(长度)。
dot( A, B ) == | A | * | B | * cos( angle_A_B )
因此,2 Unit vectors 的点积等于两个向量夹角的 余弦 ,因为单位向量的长度为 1 .
uA = normalize( A )
uB = normalize( B )
cos( angle_A_B ) == dot( uA, uB )
因此,归一化法向量与直线 (NV) 和直线上一点的向量 (LP[=70=) 的点积])到必须计算距离的点(P)是点到直线的最短距离
最小示例:
import pygame
import math
pygame.init()
screen = pygame.display.set_mode((1200,700))
def distance_point_line(pt, l1, l2):
NV = pygame.math.Vector2(l1[1] - l2[1], l2[0] - l1[0])
LP = pygame.math.Vector2(l1)
P = pygame.math.Vector2(pt)
return abs(NV.normalize().dot(P -LP))
color = (255, 255, 255)
running = True
while running:
screen.fill((0, 0, 0))
line_rect = pygame.draw.line(screen, color, (50,50), (400,400), 10)
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
if (line_rect.collidepoint(event.pos) and
distance_point_line(event.pos, (50,50), (400,400)) < 5):
color = (255, 0, 0)
if event.type == pygame.MOUSEBUTTONUP:
color = (255, 255, 255)
我正在从事基于图形的项目。我正在画线来表示边缘。我的目标是在单击特定行时更改行的颜色。问题是 pygame 将线视为虚拟矩形的对角线。因此,即使我没有点击直线但鼠标位置位于虚拟矩形的投影区域中,使用 collidepoint() 时事件也会被检测为碰撞。我只想在实际行上单击鼠标时才检测到这一点。
我是 pygame 的新手,如果有其他我可以使用的函数或库,请告诉我。这是我项目的示例代码。
import pygame
pygame.init()
screen = pygame.display.set_mode((1200,700))
running = True
red = 0
green = 255
blue = 210
while running:
screen.fill((red,green,blue))
line = pygame.draw.line(screen, green, (50,50), (400,400),10)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
if line.collidepoint(event.pos):
red = 255
green = 0
blue = 0
if event.type == pygame.MOUSEBUTTONUP:
red = 0
green = 255
blue = 210
pygame.display.update()
pygame.draw.line
returns a pygame.Rect
object that defines the axis aligned bounding rectangle surrounding the line. collidepoint
测试点是否在矩形内。
你必须使用不同的方法。编写一个函数来计算点到直线的最短距离:
dist = abs(dot(normalized(NV), P - LP))
,其中NV
是直线的法向量, LP
是直线上的点,P
是需要计算距离的点
import math
def distance_point_line(pt, l1, l2):
nx, ny = l1[1] - l2[1], l2[0] - l1[0]
nlen = math.hypot(nx, ny)
nx /= nlen
ny /= nlen
vx, vy = pt[0] - l1[0], pt[1] - l1[1]
dist = abs(nx*vx + ny*vy)
return dist
与使用pygame.math.Vector2
相同的功能:
def distance_point_line(pt, l1, l2):
NV = pygame.math.Vector2(l1[1] - l2[1], l2[0] - l1[0])
LP = pygame.math.Vector2(l1)
P = pygame.math.Vector2(pt)
return abs(NV.normalize().dot(P -LP))
测试鼠标指针是否在线条定义的矩形内,距离是否小于线条宽度的一半:
if (line_rect.collidepoint(event.pos) and
distance_point_line(event.pos, (50,50), (400,400)) < 5):
# [...]
解释:
我使用了从点到线的 Dot product 距离。一般来说,2 个向量的点积等于 余弦 之间的夹角2 个向量乘以两个向量的大小(长度)。
dot( A, B ) == | A | * | B | * cos( angle_A_B )
因此,2 Unit vectors 的点积等于两个向量夹角的 余弦 ,因为单位向量的长度为 1 .
uA = normalize( A )
uB = normalize( B )
cos( angle_A_B ) == dot( uA, uB )
因此,归一化法向量与直线 (NV) 和直线上一点的向量 (LP[=70=) 的点积])到必须计算距离的点(P)是点到直线的最短距离
最小示例:
import pygame
import math
pygame.init()
screen = pygame.display.set_mode((1200,700))
def distance_point_line(pt, l1, l2):
NV = pygame.math.Vector2(l1[1] - l2[1], l2[0] - l1[0])
LP = pygame.math.Vector2(l1)
P = pygame.math.Vector2(pt)
return abs(NV.normalize().dot(P -LP))
color = (255, 255, 255)
running = True
while running:
screen.fill((0, 0, 0))
line_rect = pygame.draw.line(screen, color, (50,50), (400,400), 10)
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
if (line_rect.collidepoint(event.pos) and
distance_point_line(event.pos, (50,50), (400,400)) < 5):
color = (255, 0, 0)
if event.type == pygame.MOUSEBUTTONUP:
color = (255, 255, 255)