Pygameで定義済みのトラックマスクを使用してビーム衝突検出を実装しようとしています。私の最終的な目標は、AI車のモデルに、乗っているトラックを見るビジョンを与えることです。
これは私のビームをマスクしてオーバーラップを見つけようとする現在のコードです:
_import math
import sys
import pygame as pg
RED = (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):
# compute beam final point
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))
# draw a single beam to the beam surface based on computed final point
pg.draw.line(beam_surface, BLUE, (250, 250), (x_dest, y_dest))
beam_mask = pg.mask.from_surface(beam_surface)
# find overlap between "global mask" and current beam mask
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)
_
コードスニペットで何が起こるかを説明しましょう。 1つずつ_beam_surface
_にビームを描画し、そこからマスクを作成し、1つの長方形と円(gifの黒色)で定義された背景マスクとの重なりを見つけます。 「ヒットポイント」(両方のマスク間のオーバーラップポイント)がある場合は、ヒットポイントとマウスの位置を結ぶ線で描画します。
角度_<0,90>
_の場合は正常に機能します:
しかし、範囲_<90,360>
_の角度では機能しません:
Pygameの overlap()
ドキュメントはこれを伝えています:
左上隅から始めて、最初の行のビット0からW-1((0、0)から(W-1、0))をチェックし、次の行((0、1)から(W-1 、1))。この列ブロック全体がチェックされると、次の列ブロックに続きます(W to 2 * W-1)。
つまり、このアプローチは、ビームがほぼ左上隅からマスクに当たった場合にのみ機能します。それをすべての状況で機能させる方法について何かアドバイスはありますか?これは一般的にこの問題を解決するための良いアプローチですか?
光線軸のxおよびyコンポーネントが正の方向を指している場合、アプローチはうまく機能しますが、負の方向を指している場合は失敗します。ご指摘のとおり、これは pygame.mask.Mask.overlap の動作方法が原因です。
左上隅から始めて、最初の行のビット0からW-1((0、0)から(W-1、0))をチェックし、次の行((0、1)から(W-1 、1))。この列ブロック全体がチェックされると、次の列ブロックに続きます(W to 2 * W-1)。
アルゴリズムを機能させるには、光線が常に正の方向を指すようにする必要があります。したがって、レイが負のx方向を指している場合は、マスクとレイを垂直に反転し、レイが負のy方向を指している場合は、レイを水平に反転します。
pygame.transform.flip()
topを使用して、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))
光線の方向に応じてflippedマスクを取得します。
flip_x = c < 0
flip_y = s < 0
filpped_mask = flipped_masks[flip_x][flip_y]
flippedターゲットポイントを計算します。
x_dest = 250 + 500 * abs(c)
y_dest = 250 + 500 * abs(s)
flippedオフセットを計算します。
offset_x = 250 - pos[0] if flip_x else pos[0] - 250
offset_y = 250 - pos[1] if flip_y else pos[1] - 250
flippedレイとマスクの最も近い交点を取得し、unflipthe交点:
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)
例を参照してください。
import math
import sys
import pygame as pg
RED = (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):
c = math.cos(math.radians(angle))
s = math.sin(math.radians(angle))
flip_x = c < 0
flip_y = s < 0
filpped_mask = flipped_masks[flip_x][flip_y]
# compute beam final point
x_dest = 250 + 500 * abs(c)
y_dest = 250 + 500 * abs(s)
beam_surface.fill((0, 0, 0, 0))
# draw a single beam to the beam surface based on computed final point
pg.draw.line(beam_surface, BLUE, (250, 250), (x_dest, y_dest))
beam_mask = pg.mask.from_surface(beam_surface)
# find overlap between "global mask" and current beam mask
offset_x = 250 - pos[0] if flip_x else pos[0] - 250
offset_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 = 499 - hit[0] if flip_x else hit[0]
hy = 499 - hit[1] if flip_y else hit[1]
hit_pos = (hx, hy)
pg.draw.line(surface, BLUE, pos, hit_pos)
pg.draw.circle(surface, GREEN, hit_pos, 3)
#pg.draw.circle(surface, (255, 255, 0), mouse_pos, 3)
surface = pg.display.set_mode((500, 500))
#mask_surface = pg.image.load("../assets/mask.png")
mask_surface = pg.Surface((500, 500), pg.SRCALPHA)
mask_surface.fill((255, 0, 0))
pg.draw.circle(mask_surface, (0, 0, 0, 0), (250, 250), 100)
pg.draw.rect(mask_surface, (0, 0, 0, 0), (170, 170, 160, 160))
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]]
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, 359, 30):
draw_beam(surface, angle, mouse_pos)
pg.display.update()
clock.tick(30)
いいえ、アルゴリズムはさらに改善できます。光線は常にbeam_surface
の右下の象限に描画されます。したがって、他の3つの象限は不要になり、beam_surface
のサイズを250x250に縮小できます。光線の開始は(250、250)ではなく(0、0)にあり、オフセットの計算はわずかに調整する必要があります。
beam_surface = pg.Surface((250, 250), pg.SRCALPHA)
def draw_beam(surface, angle, pos):
c = math.cos(math.radians(angle))
s = math.sin(math.radians(angle))
flip_x = c < 0
flip_y = s < 0
filpped_mask = flipped_masks[flip_x][flip_y]
# compute beam final point
x_dest = 500 * abs(c)
y_dest = 500 * abs(s)
beam_surface.fill((0, 0, 0, 0))
# draw a single beam to the beam surface based on computed final point
pg.draw.line(beam_surface, BLUE, (0, 0), (x_dest, y_dest))
beam_mask = pg.mask.from_surface(beam_surface)
# find overlap between "global mask" and current beam mask
offset_x = 499-pos[0] if flip_x else pos[0]
offset_y = 499-pos[1] if flip_y else pos[1]
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 = 499 - hit[0] if flip_x else hit[0]
hy = 499 - hit[1] if flip_y else hit[1]
hit_pos = (hx, hy)
pg.draw.line(surface, BLUE, pos, hit_pos)
pg.draw.circle(surface, GREEN, hit_pos, 3)