我尝试在Pygame中实现光束与预定义轨道掩码的碰撞检测。我的最终目标是赋予AI车辆模型视觉能力,使其能够看到它行驶的轨道:
这是我当前的代码,我向掩码发射光束并尝试找到重叠部分:
import mathimport sysimport pygame as pgRED = (255, 0, 0)GREEN = (0, 255, 0)BLUE = (0, 0, 255)pg.init()beam_surface = pg.Surface((500, 500), pg.SRCALPHA)def draw_beam(surface, angle, pos): # 计算光束的终点 x_dest = 250 + 500 * math.cos(math.radians(angle)) y_dest = 250 + 500 * math.sin(math.radians(angle)) beam_surface.fill((0, 0, 0, 0)) # 根据计算的终点在光束表面上绘制单个光束 pg.draw.line(beam_surface, BLUE, (250, 250), (x_dest, y_dest)) beam_mask = pg.mask.from_surface(beam_surface) # 查找“全局掩码”与当前光束掩码之间的重叠 hit = mask.overlap(beam_mask, (pos[0] - 250, pos[1] - 250)) if hit is not None: pg.draw.line(surface, BLUE, mouse_pos, hit) pg.draw.circle(surface, GREEN, hit, 3)surface = pg.display.set_mode((500, 500))mask_surface = pg.image.load("../assets/mask.png")mask = pg.mask.from_surface(mask_surface)clock = pg.time.Clock()while True: for e in pg.event.get(): if e.type == pg.QUIT: pg.quit() sys.exit() mouse_pos = pg.mouse.get_pos() surface.fill((0, 0, 0)) surface.blit(mask_surface, mask_surface.get_rect()) for angle in range(0, 120, 30): draw_beam(surface, angle, mouse_pos) pg.display.update() clock.tick(30)
让我们描述一下代码片段中发生的事情。我逐一向beam_surface
绘制光束,从中制作掩码,并查找与由一个矩形和一个圆圈定义的背景掩码(gif中的黑色部分)的重叠。如果存在“命中点”(两个掩码之间的重叠点),我会用一条连接命中点和鼠标位置的线来绘制它。
对于角度<0,90>
,它工作正常:
但对于角度范围<90,360>
,它不工作:
Pygame的overlap()
文档说明如下:
从左上角开始,它检查第一行的位0到W – 1(从(0, 0)到(W – 1, 0)),然后继续检查下一行(从(0, 1)到(W – 1, 1))。一旦整个列块被检查完,它会继续检查下一个(从W到2 * W – 1)。
这意味着这种方法只在光束大致从左上角击中掩码时才有效。你对如何使其在所有情况下都工作有何建议?这通常是解决此问题的好方法吗?
回答:
你的方法在光束轴的x和y分量指向正方向时工作正常,但如果指向负方向则失败。正如你指出的,这是由pygame.mask.Mask.overlap的工作方式引起的:
从左上角开始,它检查第一行的位0到W – 1(从(0, 0)到(W – 1, 0)),然后继续检查下一行(从(0, 1)到(W – 1, 1))。一旦整个列块被检查完,它会继续检查下一个(从W到2 * W – 1)。
为了使算法工作,你必须确保光束始终指向正方向。因此,如果光束指向负x方向,则垂直翻转掩码和光束;如果光束指向负y方向,则水平翻转光束。
使用pygame.transform.flip()
创建4个掩码。不翻转,水平翻转,垂直翻转以及垂直和水平翻转:
mask = pg.mask.from_surface(mask_surface)mask_fx = pg.mask.from_surface(pg.transform.flip(mask_surface, True, False))mask_fy = pg.mask.from_surface(pg.transform.flip(mask_surface, False, True))mask_fx_fy = pg.mask.from_surface(pg.transform.flip(mask_surface, True, True))flipped_masks = [[mask, mask_fy], [mask_fx, mask_fx_fy]]
确定光束的方向:
c = math.cos(math.radians(angle))s = math.sin(math.radians(angle))
根据光束的方向获取翻转的掩码:
flip_x = c < 0flip_y = s < 0filpped_mask = flipped_masks[flip_x][flip_y]
计算翻转的目标点:
x_dest = 250 + 500 * abs(c)y_dest = 250 + 500 * abs(s)
计算翻转的偏移量:
offset_x = 250 - pos[0] if flip_x else pos[0] - 250offset_y = 250 - pos[1] if flip_y else pos[1] - 250
获取翻转的光束与掩码的最接近交点,并取消翻转交点:
hit = filpped_mask.overlap(beam_mask, (offset_x, offset_y))if hit is not None and (hit[0] != pos[0] or hit[1] != pos[1]): hx = 500 - hit[0] if flip_x else hit[0] hy = 500 - hit[1] if flip_y else hit[1] hit_pos = (hx, hy) pg.draw.line(surface, BLUE, mouse_pos, hit_pos) pg.draw.circle(surface, GREEN, hit_pos, 3)
查看示例: repl.it/@Rabbid76/PyGame-PyGame-SurfaceLineMaskIntersect-2