I have a method that is supposed to generate a certain number of Vector3 at a distance not less than specified.
// Generate random point based on plane area
public List<Vector3> GeneratePositions(int numberOfPositions, float minDistanceBetweenPositions)
{
float entireArea = 0f;
List<AreasWeight> areasWeights = new List<AreasWeight>();
List<Vector3> positions = new List<Vector3>();
foreach (GeneratorPlane plane in GeneratorPlanes.GetCollectionAsList())
{
entireArea = plane.GetArea();
}
foreach (GeneratorPlane plane in GeneratorPlanes.GetCollectionAsList())
{
float weight = plane.GetArea() / entireArea;
int numOfPositionsInArea = Mathf.RoundToInt(numberOfPositions * weight);
areasWeights.Add(new(plane, weight, numOfPositionsInArea));
}
foreach (AreasWeight areaWeight in areasWeights)
{
for (int i = 0; i < areaWeight.NumOfPointsInArea; i )
{
Vector3 generatedPoint = areaWeight.Plane.GetRandomPointOnPlane();
foreach (Vector3 position in positions)
{
int attempts = 1;
while ((position - generatedPoint).magnitude < minDistanceBetweenPositions)
{
generatedPoint = areaWeight.Plane.GetRandomPointOnPlane();
attempts ;
if (attempts > 2000)
{
Debug.Log("Can't generate all positions.");
break;
}
}
}
positions.Add(generatedPoint);
}
}
return positions;
}
Get random point method:
public Vector3 GetRandomPointOnPlane()
{
float xPosition = Random.Range(Mathf.Min(DownPoint.x, DownPointHelper.x), Mathf.Max(DownPoint.x, DownPointHelper.x));
float zPosition = Random.Range(Mathf.Min(DownPoint.z, UpPointHelper.z), Mathf.Max(DownPoint.z, UpPointHelper.z));
return new(xPosition, DownPoint.y 0.002f, zPosition);
}
But when i Instantiate objects based on these Vector3. Objects still have a distance less than the specified. What am i doing wrong?
CodePudding user response:
For what I can see the issue should be around the range of values you allow on GetRandomPointOnPlane()
(position - generatedPoint).magnitude < minDistanceBetweenPositions
shouldn't allow you to keep generatedPoint
with distances less than minDistanceBetweenPositions
between any other position, but if you try 2000 times you actually keep the last generatedPosition
.
So if you can miss some positions, you could try using a bool
to manage what positions are kept.
for (int i = 0; i < areaWeight.NumOfPointsInArea; i )
{
Vector3 generatedPoint = areaWeight.Plane.GetRandomPointOnPlane();
bool positionFound = true;
foreach (Vector3 position in positions)
{
int attempts = 1;
while ((position - generatedPoint).magnitude < minDistanceBetweenPositions)
{
generatedPoint = areaWeight.Plane.GetRandomPointOnPlane();
attempts ;
if (attempts > 2000)
{
Debug.Log("Can't generate all positions.");
//here you break to the next position keeping an unwanted value
positionFound = false;
break;
}
}
}
if(positionFound)
{
positions.Add(generatedPoint);
}
}
CodePudding user response:
I found a solution. The problem was a bad loop structure. When the algorithm confirmed that the distance was too small and generated a new one, it did not check whether the generated position had a gap from the previous positions on the list. It only confirmed that the gap was preserved and the program continued to execute.
I moved the code that makes sure that the distances are saved to the public List<Vector3> GeneratePositions(int numberOfPositions, float minDistanceBetweenPositions)
method in the GeneratorPlane class. I also added a private Vector3 PickRandomPos()
method to it, just to return the generated position.
Methods in the public class GeneratorPlane
:
public Vector3 GetRandomPointOnPlane(List<Vector3> alreadyGeneratedPoints, float minDistnaceBetweenPositions)
{
if (alreadyGeneratedPoints.Count != 0)
{
int attemps = 1;
bool pointFound = false;
Vector3 posToReturn = new();
while (!pointFound)
{
pointFound = true;
posToReturn = PickRandomPos();
foreach (Vector3 position in alreadyGeneratedPoints)
{
if (Vector3.Distance(position, posToReturn) < minDistnaceBetweenPositions)
{
pointFound = false;
attemps ;
if (attemps > 2000)
{
Debug.LogError("Points cannot be generated. Too little available space");
return Vector3.zero;
}
break;
}
}
}
return posToReturn;
}
else
{
Debug.Log("First point generated");
return PickRandomPos();
}
}
private Vector3 PickRandomPos()
{
float xPosition = Random.Range(Mathf.Min(DownPoint.x, DownPointHelper.x), Mathf.Max(DownPoint.x, DownPointHelper.x));
float zPosition = Random.Range(Mathf.Min(DownPoint.z, UpPointHelper.z), Mathf.Max(DownPoint.z, UpPointHelper.z));
return new(xPosition, DownPoint.y 0.002f, zPosition);
}
Method to generate and return a certain number of items:
public List<Vector3> GeneratePositions(int numberOfPositions, float minDistanceBetweenPositions)
{
float entireArea = 0f;
List<AreasWeight> areasWeights = new();
List<Vector3> positions = new();
foreach (GeneratorPlane plane in PlanesGenerator.GetCollectionAsList())
{
entireArea = plane.GetArea();
}
foreach (GeneratorPlane plane in PlanesGenerator.GetCollectionAsList())
{
float weight = plane.GetArea() / entireArea;
int numOfPositionsInArea = Mathf.RoundToInt(numberOfPositions * weight);
areasWeights.Add(new(plane, weight, numOfPositionsInArea));
}
foreach (AreasWeight areaWeight in areasWeights)
{
for (int i = 0; i < areaWeight.NumOfPointsInArea; i )
{
Vector3 generatedPoint = areaWeight.Plane.GetRandomPointOnPlane(positions, minDistanceBetweenPositions);
positions.Add(generatedPoint);
}
}
return positions;
}