因此,我使用Slick2D,并且正在制作游戏。它具有TiledMap和实体(与其他任何游戏一样),我想要一种使用A *的方法。我真的不知道如何使用它,因为我找不到解释。

仅对于那些不使用Slick的用户,它已经具有我使用的AStarPathFinding和TiledMap类。

最佳答案

这是一个简单的示例,说明如何在Slick2D中找到A-star路径。在真实游戏中,您可能会更实际地实现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调用。这将使该对象可通过blockedcostctx.getMover()方法中使用。这样一来,您可以使一些推动器忽略一些障碍物,否则它们会阻塞障碍物等。(想象一下飞行角色或两栖飞行器可以在水中或在其他情况下阻挡墙壁的运动。)我希望这提供了一个基本概念。

编辑
现在我注意到您特别提到您正在使用TiledMap类。此类不实现TileBasedMap接口,并且不能直接与Slick2D中的A-star实现一起使用。 (默认情况下,平铺地图没有任何阻塞的概念,这在执行路径查找时很关键。)因此,您将必须自己执行自己的实现,使用自己的条件来确定何时平铺是否阻塞以及应花费多少费用。遍历它们。

编辑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) {}

}

其他选择

还有许多其他选择。例如,可以将图层本身的属性设置为可以指示其是否为阻止图层,而不是将图层ID设置为阻止图层。

此外,以上所有示例仅考虑了阻塞和非阻塞图块。当然,您在地图上可能还会有阻塞和非阻塞对象。您可能还会有其他播放器或NPC等被阻止。所有这些都需要以某种方式处理。但是,这应该可以帮助您入门。

08-07 21:27