【问题标题】:A* Pathfinding - Java, Slick2D LibraryA* 寻路 - Java、Slick2D 库
【发布时间】:2012-03-16 17:44:05
【问题描述】:

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

对于那些不使用 Slick 的人来说,它已经有我使用的 AStarPathFinding 和 TiledMap 类。

【问题讨论】:

    标签: java path-finding a-star slick2d


    【解决方案1】:

    这是一个简单的例子,说明 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 调用。这将使该对象可从blockedcost 方法通过ctx.getMover() 使用。这样你就可以让一些移动器忽略一些,否则会阻塞,障碍物等。(想象一个飞行角色或两栖车辆,可以在水中或在其他阻塞的墙壁上方移动。)我希望这能给出一个基本的想法。

    编辑 我现在注意到您特别提到您正在使用TiledMap 类。该类没有实现TileBasedMap接口,不能直接与Slick2D中的A-star实现一起使用。 (默认情况下,Tiled 地图没有任何 blocking 概念,这是执行路径查找时的关键。)因此,您必须自己实现这一点,使用自己的标准来判断 tile 何时阻塞或不是以及遍历它们应该花费多少。

    编辑 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 设置为阻塞。

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

    【讨论】:

    • 好的,谢谢。再问一个问题,我可以让 SimpleMap 使用 TiledMap 创建数组。那么它们将是相同的并且会起作用吗?
    • @IvanDonat 数组只是一个例子。您需要自行决定如何确定 TiledMap 中的图块是否阻塞。例如,您可以在您的 TiledMap 中有一个仅包含阻塞项的特定图层。然后你可以通过检查TiledMap.getTileId(x,y,blockingLayerId) 是否返回零来实现blocking 方法。或者,您可以为阻塞磁贴设置一个 磁贴属性(使用编辑器),然后检查磁贴是否具有该属性。我会用一些例子来更新我的答案,但还有其他方法
    • @IvanDonat 在那里,我现在用更多示例更新了我的答案。希望对您有所帮助。
    • 非常感谢!顺便说一下,我使用了之前的阻塞属性,所以我很熟悉它:D
    • 当我使用它时,它总是在您使用路径内的任何内容时返回 null。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-06
    相关资源
    最近更新 更多