How can i spawn random number of prefabs in random positions with gap of 10 inside a rectangle area ?
Not the terrain but inside a rectangle area i built with another script. Rectangle of cubes.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RandomSpawnObjects : MonoBehaviour
{
public Terrain terrain;
public GameObject[] prefabs;
public int prefabsNumber;
public int positionArea;
public int gap = 3;
// Use this for initialization
void Start ()
{
var walls = GetComponent<Walls>();
var spawnArea = walls.wallsSize;
prefabsNumber = Random.Range(5, 30);
positionArea = Random.Range(3, 30);
for (int i = 0; i < prefabsNumber; i++)
{
Instantiate(prefabs[i], prefabs[i].transform.position, Quaternion.identity);
}
}
// Update is called once per frame
void Update ()
{
}
}
walls.wallsSize give me the size for example 100 so the area will be 100*100 like 100 width and 100 height. But not sure how to get it calculate the area from walls.wallsSize
Then what i want to do is to Instantiate random number of prefabs in random positions within the walls size area and gap/space between the prefabs of 10 for example and if a prefab is randomly Instantiated near one of the walls also keep gap of 10 from the wall/s
Also making each prefab a random rotation angle is good option.
Hey,
Sorry but i don't really get what you mean. Can you clarify a bit more what exactly do you want ?
Yes.
I have one script that create a rectangle:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Walls : $$anonymous$$onoBehaviour
{
public Vector3 scaleFactor;
public float wallsSize;
public bool scaleDown = false;
private bool changepos = false;
private void Update()
{
if (scaleDown == false)
{
if (transform.localScale.x != wallsSize && transform.localScale.z != wallsSize)
{
transform.localScale += new Vector3(scaleFactor.x, scaleFactor.y, scaleFactor.z);
}
}
if (scaleDown == true)
{
if (changepos == false)
{
transform.position = new Vector3(transform.position.x + wallsSize,0, transform.position.z + wallsSize);
changepos = true;
}
if (transform.localScale.x != -wallsSize && transform.localScale.z != -wallsSize)
{
transform.localScale -= new Vector3(scaleFactor.x, scaleFactor.y, scaleFactor.z);
}
}
}
}
This walls script is working fine.
Then i have the second script that with it i want to spawn objects inside the walls area:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RandomSpawnObjects : $$anonymous$$onoBehaviour
{
public Terrain terrain;
public GameObject[] prefabs;
public int prefabsNumber;
public int positionArea;
public int gap = 3;
// Use this for initialization
void Start ()
{
var walls = GetComponent<Walls>();
var spawnArea = walls.wallsSize;
prefabsNumber = Random.Range(5, 30);
positionArea = Random.Range(3, 30);
for (int i = 0; i < prefabsNumber; i++)
{
Instantiate(prefabs[i], prefabs[i].transform.position, Quaternion.identity);
}
}
// Update is called once per frame
void Update ()
{
}
}
I will give example using Paint of what i mean. The first image is just a red rectangle that the script walls:
And this image is what i want it to do in the second script RandomSpawnObjects spawning gameobjects inside the rectangle area: Drawn them in black:
Answer by Yuvii · Oct 11, 2017 at 11:56 AM
Ok, i think i get it.
So you have to spawn your assets randomly inside the rectangle. First you have to determine the minX, axX, minY and maxY of your rectangle. Then when a simple Instantiate function, you spawn them randomly inside these coordinates.
Instantiate(*your object*, new Vector2(Random.Range(minX, maxX), Random.Range(minY, maxY)), *your quaternion*);
that way the instatiated prefabs will be in the rectangle, or at least the pivot of the prefabs. To have a random rotation, just use Quaternion.Euler(0.0, 0.0, Random.Range(0.0, 360.0) as a quaternion if of course it's the z value that you want to randomize. Saw in this topic.
And now if you want your instantiated prefabs to have a gap of 10 between each, then use an random integer and multiply it by 10
int randomIntX = Random.Range (Mathf.RoundToInt(minX/10), Mathf.RoundToInt(maxX/10));
int randomIntY = Random.Range (Mathf.RoundToInt(minY/10), Mathf.RoundToInt(maxY/10));
Instantiate(*your object*, new Vector2(randomIntX, randomIntY) * 10, *your quaternion*);
Tell me if it works that way :)
Not sure how to find the $$anonymous$$X, axX, $$anonymous$$Y and maxY of the rectangle. Any leading clue please ?
And now if you want your instantiated prefabs to have a gap of 10 between each, then use an random integer and multiply it by 10
Note, that this only guarantees a gap of 10 between the center points of the objects. This is probably not what the OP wants. He probably wants a gap of 10 between the volumes (i.e. the colliders) of the objects.
Answer by NoBrainer-David · Oct 11, 2017 at 03:00 PM
There are two parts to your question. I will try to answer them after another.
1. How do you ensure that the obstacle spawning is done after the wall generation?
This can be done by writing a coordinating MonoBehavior, SpawnSequence. SpawnSequence will hold a reference to your wall generator and your obstacle generator. The generators will no longer use the Start method to start doing their stuff. Instead, SpawnSequence will call the spawning functions in the order you choose.
A bare-bones example would look something like this:
public class SpawnSequence : MonoBehaviour
{
public EnvironmentSpawner environmentSpawner;
public ObstacleSpawner obstacleSpawner;
public void Start()
{
environmentSpawner.SpawnEnvironment();
obstacleSpawner.SpawnObstacles(environmentSpawner.GetEnvironmentBounds());
}
}
public class EnvironmentSpawner : MonoBehaviour
{
public Vector3 spawnAreaSize;
public void SpawnEnvironment()
{
// Spawn the walls here
}
public Bounds GetEnvironmentBounds()
{
return new Bounds(transform.position, spawnAreaSize);
}
}
public class ObstacleSpawner : MonoBehaviour
{
public void SpawnObstacles(Bounds bounds)
{
}
}
2. How do you ensure that the obstacles are at least 10m apart?
In your comments above you posted a function isSpaceClear that casts a sphere at the candidate point and checks for overlaps. This ensures that the distance between the candidate point and any collider is not smaller than the desired gap (red line in the diagram). However, you want to calculate the closest distance between two colliders (the purple line in the diagram above). This is not impossible, but might require some thought, as there are many types of colliders and each combination might need a unique way of calculating the distance between them. This can be mitigated by making some assumptions. For example, you could assume that your obstacles may only contain one Collider and that it may only be of one type (for example BoxCollider would probably work fine as a start).
Then, spawning the obstacles one after another and checking the distance condition after each is the right way to do it. However, you have to keep in mind that it might not be possible to spawn the desired amount of obstacles in a given space. By specifying a spawn area of size 0, for example, no obstacle will be able to be spawned. This will result in an infinite loop, freezing Unity.
I would suggest keeping track of the number of times you retried a spawn iteration and just giving up entirely after maybe a thousand retries or so.
You're right. It came to my $$anonymous$$d about the distance after spawning the object after I left and overlooked the infinite loop case. $$anonymous$$y bad, it's a faulty answer!
Hey, don't be too hard on yourself. I thought the overall approach was well thought out! Adding the objects one by one, putting the condition checking into its own method and using Physics.SphereCast, those were very good choices, in my opinion! It just needed a little tuning. Did you delete it?