简体   繁体   English

A * Pathfinding - Java,Slick2D库

[英]A* Pathfinding - Java, Slick2D Library

So I use Slick2D and I am making a game. 所以我使用Slick2D,我正在制作游戏。 It has a TiledMap and entities (as any other game) and I want a way to use A*. 它有一个TiledMap和实体(和任何其他游戏一样),我想要一种使用A *的方法。 I don't really know how to use it because I can't find an explanation. 我真的不知道如何使用它,因为我找不到解释。

Just for those who don't use Slick, it already has AStarPathFinding and TiledMap classes which I use. 对于那些不使用Slick的人来说,它已经有了我使用的AStarPathFinding和TiledMap类。

Here is a simple example of how the A-star path finding in Slick2D works. 这是一个简单的例子,说明Slick2D中的A-star路径查找是如何工作的。 In a real game you would probably have a more realistic implementation of the TileBasedMap interface which actually looks up the accessibility in whatever map structure your game uses. 在真实的游戏中,你可能会有一个更真实的TileBasedMap界面实现,它实际上可以在游戏使用的任何地图结构中查找可访问性。 You may also return different costs based on for example your map terrain. 您还可以根据地图地形返回不同的费用。

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

}

In your game you may also wish to make your path finding character class implement the Mover interface so that you can pass that as a user data object instead of null to the findPath call. 在您的游戏中,您可能还希望使您的路径查找字符类实现Mover接口,以便您可以将其作为用户数据对象而不是null传递给findPath调用。 This will make that object available from the blocked and cost methods through ctx.getMover() . 这将通过ctx.getMover()blockedcost方法中获取该对象。 That way you can have some movers that ignore some, otherwise blocking, obstacles etc. (Imagine a flying character or an amphibious vehicle that can move in water or above otherwise blocking walls.) I hope this gives a basic idea. 这样你可以让一些动作者忽略一些,否则阻挡,障碍等等。(想象一下飞行角色或两栖车辆可以在水中移动,否则阻挡墙壁。)我希望这给出了一个基本的想法。

EDIT I now noticed that you mentioned specifically that you are using the TiledMap class. 编辑我现在注意到你特别提到你正在使用TiledMap类。 This class does not implement the TileBasedMap interface and cannot be directly used with the A-star implementation in Slick2D. 此类不实现TileBasedMap接口,不能直接与Slick2D中的A-star实现一起使用。 (A Tiled map does not by default have any concept of blocking which is key when performing path finding.) Thus, you will have to implement this yourself, using your own criteria for when a tile is blocking or not and how much it should cost to traverse them. (默认情况下,平铺地图没有任何阻塞概念,这是执行路径查找时的关键。)因此,您必须自己实施此操作,使用您自己的标准来确定切片是否阻塞以及花费多少穿越他们。

EDIT 2 编辑2

There are several ways that you could define the concept of a tile being blocking . 有几种方法可以定义tile被阻塞的概念。 A couple of relative straight forward ways are covered below: 下面介绍几种相对直接的方法:

Separate blocking layer 单独的阻挡层

In the Tiled map format you can specify multiple layers. 在Tiled地图格式中,您可以指定多个图层。 You could dedicate one layer for your blocking tiles only and then implement the TileBasedMap according to something like this: 您可以只为您的阻止切片专门设置一个图层,然后根据以下内容实现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) {}

}

Tile property based blocking 基于Tile属性的阻塞

In the Tiled map format each tile type may optionally have user defined properties . 在Tiled地图格式中,每个图块类型可以可选地具有用户定义的属性 You could easily add a blocking property to the tiles which are supposed to be blocking and then check for that in your TileBasedMap implementation. 您可以轻松地将blocking属性添加到应该阻塞的切片中,然后在TileBasedMap实现中检查它。 For example: 例如:

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

}

Other options 其他选择

There are many other options. 还有很多其他选择。 For example, instead of having a set layer id as blocking you could set properties for the layer itself that can indicate if it is a blocking layer or not. 例如,您可以设置图层本身的属性,而不是将设置图层ID设置为阻止,这可以指示它是否为阻挡图层。

Additionally, all of the above examples just take blocking vs. non-blocking tiles into consideration. 此外,所有上述示例都考虑了阻塞与非阻塞磁贴 Of course you may have blocking and non-blocking objects on the map as well. 当然,您也可能在地图上有阻塞和非阻塞对象。 You could also have other players or NPCs etc. that are blocking. 你也可能有阻挡的其他玩家或NPC等。 All of this would need to be handled somehow. 所有这些都需要以某种方式处理。 This should however get you started. 然而,这应该让你开始。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM