简体   繁体   中英

How can I find the North, East, South, West and diagonal neighbours in a 2D array?

I'm working on a 2D procedurally generated Unity game, and I'm wondering how I can get all the neighbours in the four cardinal directions (N, E, S, W) as well as the four intercardinal directions (NE, SE, SW, NW).

Example of what I'm trying to achieve:

在此处输入图片说明

If we think of the cell coordinates as row and column , you get the neighbors by looking in the row above, the same row, and the row below, at the column before, the same column, and the column after the cell we're searching.

To get these values we simply set minRow = cell.Row - 1 , maxRow = cell.Row + 1 , minCol = cell.Col - 1 and maxCol = cell.Col + 1 (of course we have to check the bounds of the grid to be sure we don't walk off any of the sides, and we don't return the cell whose Row and Col are the same as the cell we're examining), and we return all the items from the grid with these coordinates.

For example:

private static List<T> GetNeighbors<T>(int cellRow, int cellCol, T[,] grid)
{
    var minRow = cellRow == 0 ? 0 : cellRow - 1;
    var maxRow = cellRow == grid.GetUpperBound(0) ? cellRow : cellRow + 1;
    var minCol = cellCol == 0 ? 0 : cellCol - 1;
    var maxCol = cellCol == grid.GetUpperBound(1) ? cellCol : cellCol + 1;

    var results = new List<T>();

    for (int row = minRow; row <= maxRow; row++)
    {
        for (int col = minCol; col <= maxCol; col++)
        {
            if (row == cellRow && col == cellCol) continue;
            results.Add(grid[row, col]);
        }
    }

    return results;
}

In practice, it might look something like:

private static void Main()
{
    var grid = GetSquareGrid(10);

    var neighbors = GetNeighbors(4, 5, grid);

    Console.Write($"Neighbors of [4,5] are: ");
    Console.Write(string.Join(",", neighbors.Select(n => $"[{n.X},{n.Y}]")));

    GetKeyFromUser("\n\nDone! Press any key to exit...");
}

private static Point[,] GetSquareGrid(int size)
{
    var result = new Point[size, size];

    for (int row = 0; row < size; row++)
    {
        for (int col = 0; col < size; col++)
        {
            result[row, col] = new Point(row, col);
        }
    }

    return result;
}

Output:

上面代码中控制台输出的图像

Assuming you're using a unity tilemap here's how you do it

public static List<TileBase> GetNeighbours(Tilemap tilemap,  Vector3Int original)
{
    var tiles = new List<TileBase>();
    for (int x=-1;x<=1;++x)
    {
        for (int y=-1;y<=1;++y)
        {
            var point = new Vector3Int(original.x + x, original.y + y, 0);
            if (
                tilemap.cellBounds.Contains(point) &&
                x!=0 || y!=0
            )
            {
                tiles.Add(tilemap.GetTile(point));
            }
        }
    }
    return tiles;
}

You mention in a commen that you're trying to do this with a 2D int array, so I'm assuming you have your tiles labeled with integers. Let's assume you have an array int[,] that contains all of your ints. I'm also going to assume you know the x,y position of the tile you want to find neighbors.

Assuming your array looks like this, with indices as labeled:

[0,0] [1,0] [2,0] [3,0]
[0,1] [1,1] [2,1] [3,1]
[0,2] [1,2] [2,2] [3,2]
[0,3] [1,3] [2,3] [3,3]

If your array is flipped, you'll need to flip some of the below logic, but it will still apply.

To get the tile to the west, it's x-1. To get the tile to the east, it's x+1. The caveat being that x cannot be greater than the width of the 2D array, let's label it int widthOfArray . The same is true for north and south, with the limit being heightOfArray

Let's put that into practice:

//Assumption: [x,y] is the current array position that you want to find neighbors
//east and west are going to dictate your x index for finding the neighbor.
westIndex = Mathf.Clamp(x - 1, 0f, widthOfArray);
eastIndex = Mathf.Clamp(x + 1, 0f, widthOfArray);
//north and south are going to dictate your y index for finding the neighbor.
northIndex = Mathf.Clamp(y - 1, 0f, heightOfArray);
southIndex = Mathf.Clamp(y + 1, 0f, heightOfArray);

int northPoint     = array[x, northIndex];
int northEastPoint = array[eastIndex, northIndex];
int eastPoint      = array[eastIndex, y];
int southEastPoint = array[eastIndex, southIndex];
int southPoint     = array[x, southIndex];
//...etc

So you have a 2D array:

var map = new int[20, 20];

First we need the width and height (assuming your array arranges data in the form map[x, y] ):

// + 1 because GetUpperBound returns the highest addressable index, not the length in that dimension
var width = map.GetUpperBound(0) + 1;
var height = map.GetUpperBound(1) + 1;

Now we have this we can get neighbours. I've only done west, northwest, east, and southeast, but I'm sure you get the idea:

int searchX = 5;
int searchY = 5;

// I'm using -1 as a "doesn't exist" value, although you could also look at using nullable int (i.e. int? westNeighbour = null)
int westNeighbour = -1;
int northwestNeighbour = -1;
int eastNeighbour = -1;
int southeastNeighbour = -1;

if (searchX - 1 >= 0) // would this neighbour be on the map?
{
    westNeighbour = map[searchX - 1, searchY];
}

if (searchX - 1 >= 0 && searchY - 1 >= 0) // would this neighbour be on the map?
{
    northwestNeighbour = map[searchX - 1, searchY - 1];
}

if (searchX + 1 < width) // would this neighbour be on the map?
{
    eastNeighbour = map[searchX + 1, searchY];
}

if (searchX + 1 < width && searchY + 1 < height) // would this neighbour be on the map?
{
    southeastNeighbour = map[searchX + 1, searchY + 1];
}

Note that we are checking if the new position will still be in the bounds of the array so as not to get indexing errors.

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