简体   繁体   中英

A* Pathfinding over multiple grids

I'm attempting to implement A* pathfinding around a cube, the cube is made up of 6 grids and to keep it simple I have 4 methods GetXPlus, GetXMinus, GetYPlus, GetYMinus. Each method checks to see if the next tile is within the current grid space, if its not it switches to the appropriate grid.

The problem I'm having is when attempting to get a tile from a grid that is flipped the other way from the current grid, the tile returned is on the opposite side. Is there a way or approach which would allow me to avoid writing unique logic for every single origin grid and direction?

To help articulate my problem, In this I have originated from the (purple) grid and are using the GetXPlus method :

在此输入图像描述

A snippit of my current implementation (each grid is 64 by 64):

public Tile GetXPlus( int currentX, int currentY )
{
    var newX = currentX + 1;
    var tile = GetTile( newX , currentY );

    if( newX > 64 ) //Get adjacent XPlus Grid 
    { 
        currentGrid = SetCurrentGrid( XPlusGridIndex );
        tile = GetTile( newX - 64, currentY );
    }

    return tile;
}

Background

This implementation originated from an excellent answer to a different question suggested here: https://gamedev.stackexchange.com/questions/53866/pathfinding-on-a-uneven-planetary-surface

I would suggest that you go even further than suggested by the previous answer. Create a cube that represents all tiles, and that you cache the neighbours of every tile. As the relations between tiles are fixed, it will safe you a lot of time.

Afterwords you can use a double[,,] or int[,,,] to keep track of your processed tiles, and based on that add neighbours to your Queue<Tile> .

If needed you can implement GetDirection(Tile tile) too. That function only have to search at the directions dictionary.

public class Cube { private Tile[,,] tiles;

    public Cube(int size)
    {
        tiles = new Tile[size, size, 6];

        // initialize.
        for (var side = 0; side < 6; side++)
        {
            for (var x = 0; x < size; x++)
            {
                for (var y = 0; y < size; y++)
                {
                    tiles[x, y, side] = new Tile(x, y, side);
                }
            }
        }

        // set directions & neighbors
        for (var side = 0; side < 6; side++)
        {
            for (var x = 0; x < size; x++)
            {
                for (var y = 0; y < size; y++)
                {
                    // todo: implement.
                }
            }
        }
    }

    public Tile this[int x, int y, int side]
    {
        get
        {
            return tiles[x, y, side];
        }
    }
}

public class Tile
{
    private Dictionary<DirectionType, Tile> directions = new Dictionary<DirectionType, Tile>();

    private Tile[] neighbors = new Tile[4];

    public Tile(int x, int y, int side)
    {
        this.X = x;
        this.Y = y;
        this.Side = side;
    }

    public int X { get; private set; }
    public int Y { get; private set; }
    public int Side { get; private set; }

    public Tile this[DirectionType dir]
    {
        get
        {
            return directions[dir];
        }
    }



    public Tile[] Neighbors { get { return neighbors; } }
}

public enum DirectionType
{
    // delta: +1, 0
    e,
    // delta: 0, +1
    n,
    // delta: -1, 0
    w,
    // delta: 0, -1
    s,
    // delta: 0, 0
    X
}

You could use functions mapping from one 3d coordinate space composed of "X coordinate", "Y Coordinate" and "Tile" to another.

Given an order:

enum TileBorder
{
    Left   = 0,
    Top    = 1,
    Right  = 2,
    Bottom = 3
}

you can store these transitions in arrays of your Tile class:

class Tile
{
    public Tile[] Neighbors { get; set; }
    public Func<int, int, int>[] XTransitions { get; set; }
    public Func<int, int, int>[] YTransitions { get; set; }

    public void GetXPlus(int x, int y, out int newX, out int newY, out Tile newTile)
    {
        x++;
        if (x <= 64)
        {
            newX = x;
            newY = y;
            newTile = this;
        }
        else
        {
            newX = XTransitions[(int)TileBorder.Right](x, y);
            newY = YTransitions[(int)TileBorder.Right](x, y);
            newTile = Neighbors[(int)TileBorder.Right];
        }
    }
    // ...
}

Then you only need to pay a little attention when you set the structure up. For example, this is how you could set up the green tile, assuming your coordinates run from 1 to 64 inclusive.

Tile pink   = new Tile();
Tile green  = new Tile();
Tile orange = new Tile();
Tile purple = new Tile();
Tile blue   = new Tile();

green.Neighbors = new Tile[] 
{ 
    /* left */   orange, 
    /* top */    pink,
    /* right */  blue,
    /* bottom */ purple 
};

green.XTransitions = new Func<int, int, int>[] 
{
    /* left */   (x, y) => 1, 
    /* top */    (x, y) => x,
    /* right */  (x, y) => 64,
    /* bottom */ (x, y) => x 
};

green.YTransitions = new Func<int, int, int>[] 
{
    /* left */   (x, y) => 65 - y, 
    /* top */    (x, y) => 64,
    /* right */  (x, y) => 65 - y,
    /* bottom */ (x, y) => 1
};

Note that the tile transition function is just a lookup, but to be fully flexible you could also use functions of the types:
Func<int, int, Tile, int> for the x coordinate,
Func<int, int, Tile, int> for the y coordinate, and
Func<int, int, Tile, Tile> for the tile.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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