お互いを見ることができるユニットをグループ化する最速の方法は?


12

私が使用している2Dゲームでは、ゲームエンジンは、ユニットごとに、そのビュー範囲にある他のユニットのリストを提供できます。

グループ内のユニットをソートするための確立されたアルゴリズムがあるかどうかを知りたいのですが、各グループは互いに「接続」されているすべてのユニットによって定義されます(他のユニットを介しても)。

例は、質問をよりよく理解するのに役立つかもしれません(E =敵、O =自分のユニット)。まず、ゲームエンジンから取得するデータ:

E1 can see E2, E3, O5
E2 can see E1
E3 can see E1
E4 can see O5
E5 can see O2
E6 can see E7, O9, O1
E7 can see E6
O1 can see E6
O2 can see O5, E5
O5 can see E1, E4, O2
O9 can see E6

次に、次のようにグループを計算する必要があります。

G1 = E1, E2, E3, E4, E5, O2, O5
G2 = O1, O9, E6, E7

視野に可換プロパティがあると安全に仮定できます:[AがBを見る場合、BはAを見る]。

明確にするために、ゲームエンジン情報の各行でループする単純な実装を既に作成しましたが、それを見ると、深く研究され、さまざまな確立されたアルゴリズム(おそらくいくつかのツリーのような構造を介して?)。私の問題は、有用なGoogleヒットを返した問題を説明する方法が見つからなかったことです。

よろしくお願いします!


1
この質問は一般的で、stackoverflow、または多分数学(セット理論?)でより良い答えを得ることができると思います。私のポイントは、ゲーム開発固有のものではありません。
TorのValamo

1
@Tor-おそらく真実ですが、それがゲームのためであることを知っているという事実は、人々が問題により具体的な答えを作ることを可能にするかもしれません。
ロバートフレイザー

空間ハッシュと可視性マップのスピンを使用して、いくつかの巧妙なことを行うことができると思います-それについて考える必要があります。
ジョナサンディキンソン

回答:


7

「見ることができる」関係が対称であるため、「Aが見ることができる」が「Bが見ることができる」ことを意味する場合、計算するグループは「見ることができる」関係によって定義されるグラフの接続コンポーネントです。他の人が指摘したように、これらを計算するための簡単なアルゴリズムは次のとおりです。

while ungrouped units remain:
    let u = arbitrary ungrouped unit
    let g = new group
    let s = temporary stack
    assign u to g
    push u onto s
    while s is not empty:
        let v = topmost unit in s
        remove v from s
        for each unit w that v can see:
            if w is ungrouped:
                assign w to g
                push w onto s
            end if
        end for
    end while
 end while

s上記のスタックの代わりに、「新しい要素を追加」および「一部の要素を削除して返す」操作を効率的に実装するキューまたはその他のコレクションを使用できます。)

あなたの「見ることができる」関係である場合はない対称、あなたはあなたのグループになりたいかどうかを決定する必要がまたは連結成分を。弱く接続されたコンポーネントの場合、上記のアルゴリズムはそのまま機能しますが、行for each unit w that v can seeをに置き換える必要がありfor each unit w that can see v, or that v can seeます。以下のために強連結成分には、アルゴリズムのいずれか(使用することができますKosarajuさんTarjanのGabowのリンクWikipediaのページに記載)を。

非対称リレーションの場合、リレーションまたはその強い連結コンポーネントの推移閉包を計算することもできます。これには、Floyd–Warshallアルゴリズムを使用できます。(少し)詳細については、SOに関するこの回答を参照しください。


追伸 上記のノートにリンクしたWikipediaの記事のように、可視性の関係が変わると、グループを動的に更新する方が効率的かもしれません。私はウィキペディアで言及されている高度な(?)アルゴリズムに精通していませんが、少なくとも毎回グループを最初から再計算する以上の何かをまとめるのは難しくありません。

これの半分は簡単です。異なるグループの2つのユニットがそれらの間に視線を獲得した場合、グループをマージします。互いの視界を失ったユニットに対処するのはもう少し難しいです。単純ではあるがおそらく最適ではない解決策の1つは、影響を受けるグループ内のユニットに対してグループ化アルゴリズムを再実行することです。可視性の変更が一度に1組のユニットで発生した場合、それに対して行うことができるいくつかの最適化があります。

  • ユニットが他のユニットを1つしか見ることができず、ユニットを見失った場合は、前のグループから削除して新しいグループに割り当ててください。
  • それ以外の場合、影響を受けるユニットの1つから開始し、他のユニットの可視性グラフでA *検索を実行できます(たとえば、直線距離をヒューリスティックとして使用)。見つかった場合、グループは解散しませんでした。そうしないと、検索でアクセスしたユニットのセットが新しいグループを形成します。
  • 2つのユニットのどちらがグループの小さい半分に属している可能性が高いかを推測し、分割されている場合は、そのユニットから検索を開始できます。可能性の1つは、他のユニットの数が少ない直接見ることができるユニットから常に開始することです。

4

あなたが持っているのは接続性グラフです。一般的に、接続されたノード(つまり、文字)をグループ化する最良の方法は、グラフ検索アルゴリズムを使用することです。深さ優先、幅優先のいずれか。あなたがしているのは、他のすべてのノードから到達可能なノードのリストを作成することです。グラフが無向である限り(AがBに見える場合、BはAに見える)、これは正常に機能します。

特定の場合にこれを改善するためのアルゴリズムがいくつかあります。たとえば、キャラクターが動かない場合(および地形も動かないため、不動のキャラクターが表示されたままになっている場合)、接続性グラフを更新するために再度テストしないことを選択できます。

ただし、一般的には、フレームごとに可視性を再テストする必要があります。可能性は、可視性グループを見つけるためのグラフ探索よりも遅くなることです。


3
専門用語を追加するだけです。検索しようとしているのはグラフの接続コンポーネントであり、標準アルゴリズムは次のとおりです。(1)すべてのノードをリストに入れる、(2)ノードを選択する、(3)検索するBFS / DFSを使用して接続されたすべてのノード、(4)見つかったすべてのノードをリストから削除、(5)ノードがなくなるまで繰り返します。
ネイサンリード

3

標準的なグラフ接続の問題のようです。これには何らかのアルゴリズムがあり、次のようになります。

remaining units = all units
for each unit in remaining units:
    current group = create a new group
    add this unit to current group
    for each unit visible to this unit:
        if unit is in a group already:
            merge current group into that existing group
            set current group as that existing group
        else:
            remove that unit from remaining units
            add that unit to current group

階層的クラスタリングのように、ツリーを介してこれを実装することが可能であると期待していますが、より速く動作することを疑います-ツリーはO(log N)になる傾向がありますが、上記のチェックのほとんどはO(1)として実装できます。


興味深いことに、階層クラスタリングアプローチは次のようなものです。ユニットごとにグループを作成します。次に、お互いを見ることができるユニットのペアごとに、それらが異なるグループに属している場合、グループを1つにマージし、もう1つを破棄します。
キロタン

これが、私がOPでナイーブ実装と呼んだものです。思ったほど悪くないかもしれないことを知っているのは良いことです!:)
mac

ツリーとしてそれを行う方法は、パス圧縮でユニオンセットを使用することです。それはあまりナイーブではなく、実際には最適です。
ピーターテイラー

2

これはグラフの接続性の問題であると答えた他のすべての人に同意しますが、ここで必要なのは、関連するすべてのユニットから生成されたドロネー三角形分割グラフであることを指摘させていただきます。これにより、生成するグラフ内で互いに最も近いユニットのみが接続されます。グラフの交差(非平面性)により、互いに離れすぎたユニットがグラフ内で誤って接続されるため、他の方法でこれを行うのは非常に困難です。

上記は、ほとんどの自由移動FPSのように連続スペースを使用している場合にのみ適用されます。ただし、ユニットが移動する基になるグリッド(平面グラフ)が既にある場合は、代わりにそれを使用して接続を評価できます。

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