Randomly spawning objects

Let’s say I tag a group of empty GameObjects with a tag of ‘spawn’.

Then I would use FindObjectsWithTag(spawn); in function start() to get all the points.

Could I then call a function inside start() that would randomly select a certain number of them (say, 5/10) and spawn an item at their location?

var collector = GameObject; 

function start() { 
collector = GameObject.Find("Item"); 
FindObjectsWithTag(spawn); 
spawnitems; 
} 

function spawnitems() { 

//Do stuff to select points here 
//at selected points, instantiate collector 

}

Could you elaborate on how I would use Random.Range(0,len(mySpawnpoints)); in this type of case? Am I heading the right direction with the above or am I way off the mark? Thanks for your help.

to call a function you need the () after it even if there are no parameters. FindGameObjectsWithTag needs to be caught by a variable; right now it’s just a faucet running without a cup to fill up. Depending on how you intend to use your spawning function this could be either inside the function, or outside the function and then passed as a parameter.

As to the function itself, you could use this:

spawns = FindGameObjectsWithTag("spawn");
for (j=0;j<10;j++) {
var thisSpawnPoint : GameObject = spawns[Random.Range(0,spawns.length)];
Instantiate(spawnedItem, thisSpawnPoint.transform.position, thisSpawnPoint.transform.rotation);
}

I should just point out that StarManta’s method can spawn two objects at the same position, which may or may not be what you want.

One way around this is to have an array of integers the same length as the number of spawn points, with each integer set to its position in the array (arr[0] = 0; arr[1] = 1; // …etc…). Scramble this array with a loop something like the following:-

for (i = 0; i < arr.Length; i++) {
    var temp = arr[i];
    var swapIndex = Random.Range(0, arr.Length);
    arr[i] = arr[swapIndex];
    arr[swapIndex] = temp;
}

Do this every time you need a random selection of points, and just use the first five integers from the array as indices into your spawn points array. There are more efficient ways to go, but this should be fine unless you have a very large number of spawn points.

I would like to only like to spawn one object per position as per this code…should I use it in place of StarManta’s method or in conjunction with it, and could you give me a slightly more involved example of how I would implement the array function with the spawning? I am sorry, I am new to coding and am not quite far enough along to really understand this as well as I should. Thanks for your help.

You would start with an array of integers the same length as the number of spawn points. You then need to set each element of the array to be the same as its index:-

for (i = 0; i < numSpawnPoints; i++) {
    arr[i] = i;
}

So, arr[0] will contain 0, arr[1] will contain 1, etc. Now, instead of referring to a spawn point directly by its index:-

for (i = 0; i < desiredNumObjects; i++) {
    // -- Instantiation code that creates newItem --
    newItem.transform.position = spawnPoints[i].position;
}

You would use:-

for (i = 0; i < desiredNumObjects; i++) {
    // -- Instantiation code that creates newItem --
    newItem.transform.position = spawnPoints[arr[i]].position;
}

Since the elements of the “arr” array have the same value as their array indices, these two pieces of code do exactly the same thing. However, if you scramble the “arr” array first, then you will get a random choice of spawn points. The code in the other post accomplishes the scrambling.

There is also another completely different method you can use if the items you want to spawn are all the same or picked at random (like trees, say):-

var numObjectsNeeded = 5;

for (i = 0; i < numSpawnPoints; i++) {
    var numLeftToTry = numSpawnPoints - i;
    var chanceToChoose = numObjectsNeeded / numLeftToTry;

    if (Random.value < chanceToChoose) {
        // Instantiate object at spawnPoints[i].position.

        numObjectsNeeded -= 1;
    }
}

(I’m not sure that is any clearer than the first method, but I thought I’d mention it just in case.)

var collector : GameObject; 

function start() { 
collector = GameObject.Find("Gem"); 
spawnitems(); 
} 

function spawnitems() { 
spawns = GameObject.FindGameObjectsWithTag("Respawn"); 
for (j=0;j<10;j++) { 
var thisSpawnPoint : GameObject = spawns[Random.Range(0,spawns.length)]; 
Instantiate(collector, thisSpawnPoint.transform.position, thisSpawnPoint.transform.rotation); 
} 
}

This is what I have so far. I have tagged all the empty GameObjects with the Respawn tag. The collector variable references the object called ‘Gem’, which is what I want spawned. How do I change the script so that only 5 objects are spawned (out of 10 spawn points)? Right now, this compiles, but the script doesn’t actually do anything when I run it. Attached is my test scene, anyone mind helping me figure this out? I am sure it is simpler than I am making it…but I can’t seem to grasp it.

79693–3068–$spawntest_110.unitypackage (445 KB)

You could modify your spawnItems function to this:-

function spawnitems() { 
    var spawns = GameObject.FindGameObjectsWithTag("Respawn");

    // Debug.Log(spawns.Length);

    var numPointsNeeded = 5;
    
    for (j=0;j<10;j++) {
        var numLeftToTry = 10 - j;
        var chance = numPointsNeeded /  numLeftToTry;
        
        if (Random.value < chance) {
            var thisSpawnPoint : GameObject = spawns[j];
            Instantiate(collector, thisSpawnPoint.transform.position, thisSpawnPoint.transform.rotation);
            numPointsNeeded--;
        }
    } 
}

The basic approach you were using looks OK. If the code you started with was doing nothing at all, then there might have been a problem finding the spawn points in the first place. If you notice, there is a commented-out Debug.Log command in the code above. Un-comment this, and it will tell you how many spawn points were found. If this number is zero, it indicates you have a problem finding the points. Check the tag name’s spelling, uppercase vs. lowercase, etc, just to be sure.

Ok, I may be getting closer…at least I have some errors I can see!

var collector : GameObject; 
var numPointsNeeded = 5; 
var numLeftToTry = 10;

function Update() { 
collector = GameObject.Find("Gem"); 
spawnitems(); 
} 

function spawnitems() { 
    var spawns = GameObject.FindGameObjectsWithTag("Respawn"); 

     Debug.Log(spawns.Length); 
    
    for (j=0;j<10;j++) { 
        var numLeftToTry =- j; 
        var chance = numPointsNeeded /  numLeftToTry; 
        
        if (Random.value < chance) { 
            var thisSpawnPoint : GameObject = spawns[j]; 
            Instantiate(collector, thisSpawnPoint.transform.position, thisSpawnPoint.transform.rotation); 
            numPointsNeeded--; 
        } 
    } 
}

As you can see, I moved everything to function Update(). I don’t know if this is the right thing to do, but nothing at all was happening when everything was in function Start()…

This is the log message I get:

10
UnityEngine.Debug:Log(Object)
Spawn:spawnitems() (at Assets/Scripts/Spawn.js:13)
Spawn:Update() (at Assets/Scripts/Spawn.js:7)

Immediately followed by:

DivideByZeroException: Division by zero
Spawn.spawnitems () (at Assets/Scripts/Spawn.js:17)
Spawn.Update () (at Assets/Scripts/Spawn.js:7)

This repeats over and over. I have checked and made sure that all the empty GameObjects are tagged as ‘Respawn’, and they are.

The code shouldn’t be in the Update function - it will get called every frame (ie, many times per second).

Inside the loop, numLeftToTry needs to be set to 10 - j, but you are setting it to -j. When j is equal to 0 (at the start of the loop), you will be dividing by zero, hence the error.

OK! That worked.

Now for a couple more questions:

When I run, it spawns the objects. If I stop and then run again, it spawns them in the same locations. How would I modify this to pick different locations each time (basically, I want the locations to be different each time the level is ran and the items are spawned, so you have to go thru it differently each time)?

If I wanted this to spawn a total of 5 items (out of 10 points), but only spawn them one at a time (have a new one spawn when the player collides with the one already on the board), how would I do that?

Thanks so much for your help, this is the last thing I needed to finish my first working project!

The random number generator is initialised with a value called a “seed”. If the same seed is used twice, the sequence of numbers will be the same in both cases. I think Unity resets the seed to the same value each time the game is started and this is probably what is causing your problem. If you run your spawning code several times within the running of the game then it should generate different random positions each time.

If you want to generate the points one at a time, then it is probably easier just to pick one randomly using Random.range and just keep trying if it happens to pick the place where the player is standing:-

var previousSpawnPoint;
var newSpawnPoint = spawnPoints[Random.Range(0, numSpawnPoints)];

while (newSpawnPoint == previousSpawnPoint) {
    newSpawnPoint = spawnPoints[Random.Range(0, numSpawnPoints)];
}

previousSpawnPoint = newSpawnPoint;

Is there a way to randomize the seed variable prior to running the function?

EDIT:

I know this is straight JS, but would something like this work? How would I adapt it to Unity?

function get_random()
{
    var ranNum= Math.floor(Math.random()*5);
    return ranNum;
}

(Replacing 5 with the number of possible results)

Unless you specifically set Random.seed, then calls to Random will be different every time you run; no need to do anything.

–Eric