2Dタイルゲームで視界面積をすばやく計算する方法は?


24

タイルのマトリックスがあり、そのタイルのいくつかにはオブジェクトがあります。プレイヤーに見えるタイルと見えないタイルを計算したいので、非常に効率的に行う必要があります(したがって、大きなマトリックス(100x100)と多くのオブジェクトがある場合でも十分に高速に計算されます)。

Bresenhamのラインアルゴリズムを使用してそれを実行しようとしましたが、時間がかかりました。また、いくつかのエラーが発生しました:

----XXX-        ----X**-     ----XXX-
-@------        -@------     -@------
----XXX-        ----X**-     ----XXX-
(raw version)   (Besenham)   (correct, since tunnel walls are 
                              still visible at distance)

(@ is the player, X is obstacle, * is invisible, - is visible)

私はこれができると確信しています-結局のところ、NetHack、Zangbandがあり、彼らはすべてこの問題に何らかの形で対処しました:)

これにはどのアルゴリズムを推奨できますか?


必要に応じて、次のように可視を定義します。タイルは、タイルの少なくとも一部(コーナーなど)がプレーヤータイルの中心に障害物と交差しない直線で接続できる場合に表示されます。


1
おっと、私の間違い、NetHackは視線をいじっていませんでした:)
Rogach

一部の古いアイデアはfadden.com/tech/fast-los.htmlで見つけることができますが、それはCPUがかなり遅く、浮動小数点計算が最も避けられる時代に遡ります。
色あせた

回答:


10

visibleの定義は次のとおりです。

タイルの少なくとも一部(コーナーなど)がプレーヤータイルの中心に障害物と交差しない直線で接続できる場合、タイルは表示されます。

プレーヤータイルからの光線をトレースし、シーンと交差させることで、この概念を非常に文字通り実装できます。プレーヤーが直接見ることができるタイルにのみ関心があるため、レイが障害物に当たる(または特定の距離のしきい値を超える)と、各反復から中断します。プロセスを分割します。

  1. アルゴリズムに与える精度のレベルを指定します。これは、トレースするレイの数になります。
  2. 360度の円全体を選択した精度で除算して、各光線間で回転する角度を確認します。
  3. 0度から開始し、手順2で決定した量だけ増分し、プレーヤータイルの中心を原点とし、現在の角度で決定される方向でレイを作成します。
  4. プレーヤータイルから始まる各レイについて、障害タイルに当たるまでレイの方向に沿って歩きます。そのタイルを表示可能なタイルリストに追加し、次のレイに進みます。また、衝突が見つからない場合に「あきらめる」ために最大距離を追加することもできます。

これは3つの光線の例を示す写真です。暗い色のタイルは、各光線の「結果」、つまり衝突が発生した場所です。ただし、円の周りでこれを繰り返す必要があります。

ここに画像の説明を入力してください

パフォーマンスのために、最大距離と光線の数を微調整します。少なすぎると、タイルを見逃してしまい、多すぎるとパフォーマンスが低下します。また、光線が最も遠くまで行かなければならないほど、「誤差」が大きくなり、精度が必要になります。

編集

レイキャスティングに関する次のチュートリアル、特にステップ3とステップ4をチェックして、アルゴリズムの共通部分ビットの実装に役立ててください。

http://www.permadi.com/tutorial/raycast/rayc7.html


固定距離(たとえば0.3ポイント)だけ各光線に沿って「歩く」べきですか、それとも各光線に対してBesenhamのアルゴリズムのようなものを実行する必要がありますか?
-Rogach

一定の距離だけ進んだ場合、見逃したタイルの問題が発生します。レイキャスティングに関するこのチュートリアルを確認してください。そのリソースも編集して回答に入れます。基本的に、水平衝突と垂直衝突を別々にチェックします。
デヴィッドゴーベイア

1
アルゴリズムは優れていますが、1タイル幅の長いトンネルで正しく動作するには大量の光線が必要です。
HolyBlackCat

@HolyBlackCat-これは、すべての方向に均等な角度で光線を送信する場合にのみ当てはまります。ただし、これらの光線のほとんどを送信せずに、シーンのラインエンドでのみ投光することができます。ここに良い説明があります:redblobgames.com/articles/visibility
Rogach

8

むしろ、視線光線ではなく影光線を投じたいと思います。

これがあなたのビューエリア(潜在的に見えるエリア)だとしましょう

######################
#####.............####
###................###
##..................##
#....................#
#....................#
#..........@.........#
#....................#
#....................#
##..................##
###................###
#####.............####
######################

#ブロックは表示されませんが、。見える

障害物Xを配置しましょう。

######################
#####.............####
###................###
##.....X.....XXX....##
#......X.......X.....#
#...X.XX.............#
#...X......@.........#
#...X..........X.....#
#...XXXXXX...........#
##..................##
###....X...........###
#####.............####
######################

ビュー領域内にあるXのリストがあり、この障害のそれぞれの背後にあるすべてのタイルを非表示としてマークします。障害が非表示としてマークされている場合、リストから削除します。

######################
#####.............####
###................###
##.....X.....XXX....##
#......X.......X.....#
#...X.XX.............#
#...X......@.........#
#...X..........X.....#
#...XXXXX*...........#
##......##..........##
###....*#..........###
#####.###.........####
######################

上の例では、一番下の壁の右端から投影された影と、この影が確認する必要がある障害のリストから隠された障害を削除する方法を見ることができます(Xは確認する必要があります; *チェック済み)

バイナリパーティシトンを使用してリストを並べ替え、cosest Xが最初にチェックされる場合、チェックを少し高速化できます。

ある種の「海戦」アルゴリズムを使用して、Xのブロックを一度に確認できます(基本的に、シャドウコーンをより広くすることができる方向にある放射状のXを探します)

[編集]

シャドウを正しく投影するには2つの光線が必要です。タイルは長方形であるため、利用可能な対称性を使用して多くの仮定を行うことができます。

光線座標は、障害タイルの周りの単純な空間分割を使用して計算できます。

スペース分割の例

各長方形の領域は、タイルのコーナーをシャドウコーンエッジとするかどうかの選択を構成します。

この推論をさらにプッシュして、隣接する複数のタイルを接続し、次のように幅の広い単一のコーンをキャストすることができます。

最初のステップは、観察者の方向に障害物がないことを確認することです。その場合、最も近い障害物が代わりに考慮されます。

最も近い障害物を選択してください

黄色いタイルが障害物である場合、そのタイルは新しい赤いタイルになります。

次に、コーンの上端を検討します。

候補タイル

青いタイルはすべて、シャドウコーンを広げる可能性のある候補です。少なくとも1つが障害物である場合、前に見たように、そのタイルの周囲の空間分割を使用してレイを移動できます。

緑のタイルは、オブザーバーがオレンジ色の線の上にある場合にのみ候補になります。

拡張チェック

同じことは、他の光線と、赤い障害物に関する観察者の他の位置を表します。

基本的な考え方は、各コーンキャスティングで可能な限り多くの領域をカバーし、チェックする障害物のリストをできるだけ速く短縮することです。


減算方式の性質のため、興味深いアプローチであり、おそらくより良いアイデアです。これを読んだ後、おそらくこの方法で実装するでしょう。
デヴィッドゴーベイア

私はこのような状況で問題を予見できます。黄色のプレイヤー、青と紫の障害物。プレーヤー紫色の障害物を見ることができるはずです(緑色の光線が示すように)。しかし、青い障害物を通過する赤い影の光線は、紫色のタイルを拒否します。しかし、見通しバージョンにはこれよりも大きな問題がある可能性があると思います。
デヴィッドゴーベイア

この問題は、「非表示」の定義に由来します。レイがタイルと交差するとき、それは(ほとんど)これを完全にはカバーしません。同じ問題は、ラインセグメントをレンダーするときのエイリアシングで解決されます。個人的には、タイルの大部分が覆われているときにタイルが隠されていると思います。隠されたタイルは完全に覆われていると定義できます。完全にカバーされているブロックのみをリストから削除します。
FxIII

@DavidGouveia-どんな大きな問題ですか?
Rogach

@DavidGouveia-私はすでにシャドウ「コーン」でアプローチを試みましたが、それは非常に非効率的でした。可視光線の精度に関しては、壁のすぐ近くに立っている場合、各方向に20タイルを表示するには5500光線で十分であり、単一のタイルだけが見える距離がはるかに大きいです。そして、より大きな距離でいくつかのタイルが欠落していることについては-まあ、誰もが完全な視力を持っているわけではありませんよね?
-Rogach

8

解決しようとしている問題は、視野(略してFOV)と呼ばれることもあります。roguelikesを例として挙げたように、RogueBasin wikiが主題について何を言っているかを見る必要があります(実装へのリンクもあります):http ://www.roguebasin.com/index.php?title=Field_of_Vision

長所と短所の異なるアルゴリズムがいくつかあります-非常に便利な比較はRogueBasinでも利用できます:http ://www.roguebasin.com/index.php?title=Comparative_study_of_field_of_view_algorithms_for_2D_grid_based_worlds


本当に良い完全な要約!
Rogach

そのWebサイトは素晴らしいリソースです。そのリンクを共有してくれてありがとう。また、A *経路探索の仕組みに関する驚くほど理解しやすい説明も含まれています:-)
uliwitness 14

回答のリンクがサイトのホームページに移動しました-roguebasin.com/index.php?title=Category:FOVは妥当な一致のようです。
色あせた


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.