Как правильно сделать генерацию структур на карте в 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 шт):
Никакие флаги не нужны, читаемость кода это не повышает, совсем.
< 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;
}
Чё там будет поставлено в этих тайлах, совсем другая история из совершенно другого метода или даже класса.
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);
}
}
}
Решил так, спасибо за помощь и идею с листом. Знаю что можно лучше, но пока так.