Как правильно сделать генерацию структур на карте в Unity?

Пытаюсь сделать генерацию структур на карте 500 x 500 для Top-Down RPG в Unity, карту храню в виде матрицы с двумя полями, в одном из которых должно храниться ID структуры, а в другом можно ли в этом поле размещать структуру. Структуры должны генерироватся на расстоянии не меньше чем Radius, но при этом они все равно генерируются слишком близко пытаюсб понять в чем проблемма, но пока безуспешно. Первая башня Вторая башня Фрагмент карты

using UnityEngine;

public class GenerateTowers : MonoBehaviour
{   
    public struct Field{
        public int objectID;
        public bool reserved;
    }

    public const int MapSize = 500;
    public const int Radius = 25;
    public Field[,] map = new Field[MapSize,MapSize];
    public GameObject tower;



    bool IndexOnMap(int i, int j){
        if((MapSize > i) && (i >= 0) && (MapSize > j) && (j >= 0)){
            return true;
        }else{
            return false;
        }
        
    }

    void FillReserve(ref Field[,] map, Vector3 SpawnPosition){
        int x = (int)SpawnPosition.x;
        int y = (int)SpawnPosition.y;
        for(int i = y-Radius; i <= y+Radius; i++){
            for(int j = x-Radius; j <= x+Radius; j++){
                if(IndexOnMap(i,j)){
                    map[i,j].reserved = true;
                }
            }
        }
    }

    Vector3 randomPosition(ref Field[,] map, int i){
        int x = Random.Range(0, MapSize);
        int y = Random.Range(0, MapSize);
            
        if (map[x,y].reserved){
            Debug.Log(x+" "+y+" Reserved iterration:"+i);
            return randomPosition(ref map, i);
        }else{
            Debug.Log(x+" "+y+" Not reserved iterration:"+i);
            FillReserve(ref map, new Vector3(x, y, 0));
            return new Vector3(x, y, 0);
        }
    }
    
    private void CreateGameObject(ref Field[,] map, GameObject CloneObject,Vector3 SpawnPosition){
        Instantiate(CloneObject, SpawnPosition, Quaternion.identity);
    }




    void Start()
    {   
        for(int i = 0; i < 70; i++){
            CreateGameObject(ref map, tower, randomPosition(ref map, i));
        }
    }
}

Ответы (2 шт):

Автор решения: Yaroslav

Никакие флаги не нужны, читаемость кода это не повышает, совсем.

  • < 0 : нельзя ничего поставить (например -1)

  • 0 : ничего не стоит

  • > 0 : чем-то занята (некий id)

private const int FailTileRollLimit = 100;

public List<Vector2Int> GetFreeTiles (int[,] map, float tileSize, float minRadius, int tilesLimit = -1)
{
    Vector2Int mapSize = new Vector2Int(map.GetLength(0), map.GetLength(1));
    int failCounter = -1;
    float sqrRadius = minRadius * minRadius;
    List<Vector2Int> tiles = new List<Vector2Int>();
    do
    {
        // увеличевается каждый раз, когда итерация прервана continue
        failCounter++;
        Vector2Int t = new Vector2Int(Random.Range(0, mapSize.x), Random.Range(0, mapSize.y));
        // подходит ли для расстановки.
        if (map[t.x, t.y] != 0)
            continue;
        // достаточно ли удалён от других выбранных тайлов.
        for (int i = 0; i < tiles.Count; i++)
        {
            if (tiles[i] == t)
                continue;
            Vector2 delta = (Vector2)(tiles[i] - t) * tileSize;
            // сравниваем квадрат расстояния с квадратом радиуса,
            // что бы не считать квадратный корень (magnitude).
            // Это всё равно бессполезно, а операция грузная, особенно в цикле.
            if (delta.sqrMagnitude < sqrRadius)
                continue;
        }
        failCounter = -1;
        tiles.Add(t);
    }
    // если не смог выбрать тайл FailTileRollLimit раз, то заканчивает расстановку.
    // если достиг указанного лимита, то заканчивает расстановку.
    while (failCounter < FailTileRollLimit && tiles.Count != tilesLimit);
    return tiles;
}

Чё там будет поставлено в этих тайлах, совсем другая история из совершенно другого метода или даже класса.

→ Ссылка
Автор решения: Plazma
using System.Collections.Generic;
using UnityEngine;

public class CloneObject : MonoBehaviour
{   
    public struct Field{
        public int objectID;
        public bool reserved;
    }


    public Field[,] map = new Field[MapSize,MapSize];
    List<Vector3> freeTiles;
    public const int MaxFailCount = 50;
    public const int MapSize = 500;
    public const int Radius = 25;
    public GameObject tower;







    bool IndexOnMap(int i, int j){
        if((MapSize > i) && (i >= 0) && (MapSize > j) && (j >= 0)){
            return true;
        }else{
            return false;
        }
        
    }

    List<Vector3> RandomPositions(ref Field[,] map){
        List<Vector3> FreeTiles = new List<Vector3>();

        int fails = 0;
        while(fails <= MaxFailCount){
            int x = Random.Range(0, MapSize);
            int y = Random.Range(0, MapSize);
            
            if (!map[x,y].reserved){
                Debug.Log(x+" "+y+" "+map[x,y].reserved);
                for(int i = y-Radius; i <= y+Radius; i++){
                    for(int j = x-Radius; j <= x+Radius; j++){
                        if(IndexOnMap(j,i)){
                            map[j,i].reserved = true;
                        }
                    }
                }
                FreeTiles.Add(new Vector3(x,y,0));
            }else{
                fails += 1;
                continue;
            }
        }
        return FreeTiles;
    }
    
    





    void Start()
    {   
        freeTiles = RandomPositions(ref map);


        for(int i = 0; i < MapSize; i++){
            for(int j = 0; j < MapSize; j++){
                if(IndexOnMap(j,i)){
                    map[j,i].reserved = false;
                    map[i,j].objectID = 0;
                }
            }
        }


        foreach(var item in freeTiles){
            Instantiate(tower, item, Quaternion.identity);
        }
    }




}

Решил так, спасибо за помощь и идею с листом. Знаю что можно лучше, но пока так.

→ Ссылка