3

なのでSlick2Dを使ってゲームを作っています。(他のゲームと同様に) TiledMap とエンティティがあり、A* を使用する方法が必要です。説明が見つからないので使い方がよくわかりません。

Slick を使用していない人のために説明すると、私が使用する AStarPathFinding クラスと TiledMap クラスが既に含まれています。

4

1 に答える 1

16

これは、Slick2D での A スター パス検索がどのように機能するかの簡単な例です。TileBasedMap実際のゲームでは、ゲームが使用するマップ構造のアクセシビリティを実際に検索する、より現実的なインターフェースの実装がおそらくあるでしょう。マップの地形などに基づいて異なるコストを返すこともできます。

import org.newdawn.slick.util.pathfinding.AStarPathFinder;
import org.newdawn.slick.util.pathfinding.Mover;
import org.newdawn.slick.util.pathfinding.Path;
import org.newdawn.slick.util.pathfinding.PathFindingContext;
import org.newdawn.slick.util.pathfinding.TileBasedMap;


public class AStarTest {

    private static final int MAX_PATH_LENGTH = 100;

    private static final int START_X = 1;
    private static final int START_Y = 1;

    private static final int GOAL_X = 1;
    private static final int GOAL_Y = 6;

    public static void main(String[] args) {

        SimpleMap map = new SimpleMap();

        AStarPathFinder pathFinder = new AStarPathFinder(map, MAX_PATH_LENGTH, false);
        Path path = pathFinder.findPath(null, START_X, START_Y, GOAL_X, GOAL_Y);

        int length = path.getLength();
        System.out.println("Found path of length: " + length + ".");

        for(int i = 0; i < length; i++) {
            System.out.println("Move to: " + path.getX(i) + "," + path.getY(i) + ".");
        }

    }

}

class SimpleMap implements TileBasedMap {
    private static final int WIDTH = 10;
    private static final int HEIGHT = 10;

    private static final int[][] MAP = {
        {1,1,1,1,1,1,1,1,1,1},
        {1,0,0,0,0,0,1,1,1,1},
        {1,0,1,1,1,0,1,1,1,1},
        {1,0,1,1,1,0,0,0,1,1},
        {1,0,0,0,1,1,1,0,1,1},
        {1,1,1,0,1,1,1,0,0,0},
        {1,0,1,0,0,0,0,0,1,0},
        {1,0,1,1,1,1,1,1,1,0},
        {1,0,0,0,0,0,0,0,0,0},
        {1,1,1,1,1,1,1,1,1,0}
    };

    @Override
    public boolean blocked(PathFindingContext ctx, int x, int y) {
        return MAP[y][x] != 0;
    }

    @Override
    public float getCost(PathFindingContext ctx, int x, int y) {
        return 1.0f;
    }

    @Override
    public int getHeightInTiles() {
        return HEIGHT;
    }

    @Override
    public int getWidthInTiles() {
        return WIDTH;
    }

    @Override
    public void pathFinderVisited(int x, int y) {}

}

あなたのゲームでは、パス検索文字クラスにインターフェイスを実装させて、呼び出しMoverの代わりにユーザー データ オブジェクトとして渡すことができるようにすることもできます。これにより、およびメソッドから を介してそのオブジェクトを使用できるようになります。そうすれば、ブロックや障害物などを無視するいくつかのムーバーを作成できます (水の中やブロックしている壁の上を移動できる空飛ぶキャラクターや水陸両用車を想像してください)。nullfindPathblockedcostctx.getMover()

編集TiledMapクラス を使用していることに具体的に言及したことに気付きました。このクラスはTileBasedMapインターフェースを実装しておらず、Slick2D の A-star 実装で直接使用することはできません。(タイル マップには、既定では、パス検索を実行するときに重要なブロックの概念がありません。) したがって、タイルがブロックされているかどうか、およびコストがどれくらいかについて、独自の基準を使用して、これを自分で実装する必要があります。それらを横断します。

編集2

タイルがブロックされているという概念を定義するには、いくつかの方法があります。比較的簡単な方法をいくつか以下に示します。

遮断層を分離

タイル マップ形式では、複数のレイヤーを指定できます。1 つのレイヤーをブロッキング タイル専用にして、次のTileBasedMapように実装することができます。

class LayerBasedMap implements TileBasedMap {

    private TiledMap map;
    private int blockingLayerId;

    public LayerBasedMap(TiledMap map, int blockingLayerId) {
        this.map = map;
        this.blockingLayerId = blockingLayerId;
    }

    @Override
    public boolean blocked(PathFindingContext ctx, int x, int y) {
        return map.getTileId(x, y, blockingLayerId) != 0;
    }

    @Override
    public float getCost(PathFindingContext ctx, int x, int y) {
        return 1.0f;
    }

    @Override
    public int getHeightInTiles() {
        return map.getHeight();
    }

    @Override
    public int getWidthInTiles() {
        return map.getWidth();
    }

    @Override
    public void pathFinderVisited(int arg0, int arg1) {}

}

タイル プロパティ ベースのブロック

タイル マップ形式では、各タイル タイプにオプションでユーザー定義のプロパティを設定できます。blockingブロックしているはずのタイルにプロパティを簡単に追加し、TileBasedMap実装でそれを確認できます。例えば:

class PropertyBasedMap implements TileBasedMap {

    private TiledMap map;
    private String blockingPropertyName;

    public PropertyBasedMap(TiledMap map, String blockingPropertyName) {
        this.map = map;
        this.blockingPropertyName = blockingPropertyName;
    }

    @Override
    public boolean blocked(PathFindingContext ctx, int x, int y) {
        // NOTE: Using getTileProperty like this is slow. You should instead cache the results. 
        // For example, set up a HashSet<Integer> that contains all of the blocking tile ids. 
        return map.getTileProperty(map.getTileId(x, y, 0), blockingPropertyName, "false").equals("true");
    }

    @Override
    public float getCost(PathFindingContext ctx, int x, int y) {
        return 1.0f;
    }

    @Override
    public int getHeightInTiles() {
        return map.getHeight();
    }

    @Override
    public int getWidthInTiles() {
        return map.getWidth();
    }

    @Override
    public void pathFinderVisited(int arg0, int arg1) {}

}

その他のオプション

他にも多くのオプションがあります。たとえば、レイヤ ID をブロッキングとして設定する代わりに、レイヤ自体のプロパティを設定して、それがブロッキング レイヤであるかどうかを示すことができます。

さらに、上記の例はすべて、ブロッキング タイルと非ブロッキングタイルを考慮しているだけです。もちろん、マップ上にブロック オブジェクトと非ブロック オブジェクトがある場合もあります。また、他のプレイヤーや NPC などがブロックしている可能性もあります。これらすべてを何らかの方法で処理する必要があります。ただし、これで開始できます。

于 2012-03-16T21:49:39.313 に答える