2

ゴースト AI ではなく、パックマン自身の iPhone 用のパックマン AI を作成しようとしています。私は経路探索に A* を使用しており、壁を避けてゲーム ボード上の 2 つのタイル間の最短経路を計算する非常に単純なアプリを実行しています。

したがって、1 つの関数を実行して 2 点間のパスを計算するのは簡単です。関数が goalNode に到達したら、各タイルの「parentNode」プロパティを介してパスを逆方向にたどり、必要なアニメーションを作成できます。しかし、実際のゲームでは、状態は常に変化しているため、パスとアニメーションも変化する必要があります。私はゲームプログラミングが初めてなので、これを実装する最良の方法がよくわかりません。

バックグラウンドで実行され、goalNode と、ゲームの現在の状態を考慮してそこへの最適なパスを常に計算する NSOperation を作成する必要がありますか? このスレッドは、特定の時点でメイン スレッドに通知し、情報を提供する必要もあります。問題は何ですか?

どの時点でメインスレッドに通知する必要がありますか?

メインスレッドに通知するデータは何ですか?

...または私はすべて一緒に離れていますか?

どんなガイダンスも大歓迎です。

4

3 に答える 3

1

pacman AI について私が提案するのは、フラッド フィル アルゴリズムを使用して、グリッド上のすべてのタイルへの最短経路と合計距離を計算することです。これは A* よりもはるかに単純なアルゴリズムであり、実際には A* よりも最悪のケースが優れています。

パフォーマンスの比較をもう少し詳しく説明するために、A* の最悪のケースを想像してみてください。行き止まりのため、最終目的地に到達する前に、グリッド上のすべてのタイルを探索する必要があります。この理論的なケースは、ボード上に多くの行き止まりがある場合に発生する可能性がありますが、実際のパックマン ボードではほとんどありません。フラッド フィルの最悪のケースは、最良のケースと同じで、マップ上のすべてのタイルを 1 回だけ訪れます。違いは、フラッド フィルの反復ステップは A* 反復の場合よりも単純であるため (ヒューリスティックやノード ヒープがないなど)、すべてのノードへのアクセスは A* よりもフラッド フィルの方が高速です。

実装は非常に簡単です。グリッドをグラフとして想像すると、各タイルがノードであり、隣接するタイル間に壁のない各エッジがグラフのエッジである場合、グラフの幅優先トラバーサルを実行して、どのノードに到達したかを追跡します。から、そこにたどり着くまでに探索したノードの数。ノードを訪問したときにそのノードを訪問済みとしてマークし、ノードを 2 回訪問することはありません。

開始するための疑似コードを次に示します。

openlist = [ start_node ]
do
    node = openlist.remove_first()
    for each edge in node.edges
        child = node.follow_edge(edge)
        if not child.has_been_visited
            child.has_been_visited = true
            child.cost = node.cost + 1
            child.previous = node
            openlist.add(child)
while openlist is not empty

pacman をどこかに移動させる方法を理解するには、目的のノードから開始し、.previous ポインターを先頭までたどり、リストを逆順にします。

これの良いところは、マップ上の任意のタイルに到達するためのコストについて一定時間クエリを実行できることです。たとえば、各パワー ペレットをループして、どれが最も近く、どのようにそこに到達するかを計算できます。

ゴーストが「攻撃」モードのときにパックマンに戻るための最速の方法を知るために、これを使用することもできます!

また、各ゴーストからのフラッド フィルを考慮して、最も近いゴーストとの距離を各タイルに格納することもできます。ノードが最大コスト (8 マス?) より大きい場合は、ノードをオープン リストに追加せずに、探索する最大距離を制限できます。次に、後で A* を実行した場合、ゴーストがどれだけ近いかに基づいて、各タイルのコストにバイアスをかけることができます。しかし、それはあなたが質問で求めていたものを少し超えています。

フレームごとにインラインで実行したり、必要に応じてマルチスレッドで実行したりできるほど高速である必要があります。簡単にするために、メインのゲーム シミュレーション スレッド (UI スレッドではないことに注意) で実行することをお勧めします。

パフォーマンスに関する 1 つのヒント: フレームごとに「has_been_visited」フラグを調べてクリアするのではなく、フレームごとにインクリメントする検索カウンターを単純に持つことができます。そのようなもの:

openlist = [ start_node ]
do
    node = openlist.remove_first()
    for each edge in node.edges
        child = node.follow_edge(edge)
        if child.last_search_visit != FRAME_NUMBER
            child.last_search_visit = FRAME_NUMBER
            child.cost = node.cost + 1
            child.previous = node
            openlist.add(child)
while openlist is not empty

そして、フレームごとに FRAME_NUMBER をインクリメントします。

幸運を!

于 2012-05-15T03:46:40.010 に答える
0

マップ内のすべてのポイントのペア間の距離を事前に計算することをお勧めします。これは、マップ内にn個の通過可能なポイントがあるn^2/2スペースを取ります。このリンクによると、ボード上には240個のペレットがあります。これは、距離を照会できるポイントの組み合わせが約57kあることを意味します。これはかなり小さく、圧縮して(ここを参照)スペースを節約できます。

次に、実行時に、可能な移動とその場所に到達するまでの距離を確認する以外は、実際の計算を行う必要はありません。

于 2012-05-15T03:54:28.687 に答える
0

少し関係ありませんが、ASIPathFinder フレームワークを見たことがありますか? より高度なパスファインディングが必要な場合に役立ちます。

于 2010-08-09T23:46:59.940 に答える