A * Pathfinding - Java, библиотека Slick2D - PullRequest
3 голосов
/ 16 марта 2012

Итак, я использую Slick2D и создаю игру. У него есть TiledMap и сущности (как и любая другая игра), и я хочу использовать A *. Я действительно не знаю, как использовать это, потому что я не могу найти объяснение.

Просто для тех, кто не использует Slick, у него уже есть классы AStarPathFinding и TiledMap, которые я использую.

1 Ответ

17 голосов
/ 17 марта 2012

Вот простой пример того, как работает поиск A-звезды в Slick2D.В реальной игре у вас, вероятно, была бы более реалистичная реализация интерфейса 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, чтобы вы могли передавать его как объект пользовательских данных вместо null в findPathвызов.Это сделает этот объект доступным из методов blocked и cost через ctx.getMover().Таким образом, у вас могут быть некоторые двигатели, которые игнорируют некоторые, иначе блокирующие, препятствия и т. Д. (Представьте себе летающего персонажа или корабль-амфибию, который может двигаться в воде или над другими, блокирующими стены.) Я надеюсь, что это дает основную идею.

РЕДАКТИРОВАТЬ Теперь я заметил, что вы специально упомянули, что используете класс TiledMap.Этот класс не реализует интерфейс TileBasedMap и не может напрямую использоваться с реализацией A-star в Slick2D.(По умолчанию мозаичная карта не имеет понятия блокирования , который является ключевым при выполнении поиска пути.) Таким образом, вам придется реализовать это самостоятельно, используя свои собственные критерии для случая, когда плитка блокирует или нети сколько это должно стоить, чтобы пройти их.

РЕДАКТИРОВАТЬ 2

Есть несколько способов, которыми вы можете определить концепцию плитки как блокирование .Несколько относительных прямых путей описаны ниже:

Отдельный слой блокировки

В формате мозаичной карты вы можете указать несколько слоев.Вы можете выделить один слой только для своих блокирующих плиток, а затем реализовать 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) {}

}

Другие опции

Есть много других опций.Например, вместо того, чтобы установить идентификатор слоя в качестве блокирующего, вы можете установить свойства для самого слоя, которые могут указывать, является ли он блокирующим слоем или нет.

Кроме того, во всех приведенных выше примерах учитываются только блокирующие и неблокирующие тайлы .Конечно, у вас могут быть блокирующие и неблокирующие объекты на карте.Вы также можете иметь других игроков или NPC и т. Д., Которые блокируют.Все это должно быть как-то обработано.Это должно однако начать вас.

...