0
\$\begingroup\$
[Range(5, 50)]
    public int waypointsCount = 10;

    private System.Random random = new System.Random();

    // Use this for initialization
    void Start ()
    {
        var Nodes = UnityEngine.GameObject.FindGameObjectsWithTag("Node");
    }

I want to get a random number of gameobjects from Nodes according to the number in waypointsCount. So if for example the number is 10 then I want to get 10 random gameobjects from the array nodes. And the gameobjects should not be duplicates.

\$\endgroup\$
2
  • \$\begingroup\$ Is using System.Random a requirement, or is it OK to use Unity's built-in Random class? \$\endgroup\$
    – DMGregory
    Commented Aug 20, 2017 at 17:34
  • \$\begingroup\$ I think using also the built in class is fine too. I want later to make manipulations on this selected random gameobjects. But the random i think the built in class is fine. \$\endgroup\$ Commented Aug 20, 2017 at 17:40

2 Answers 2

0
\$\begingroup\$

Use the greatest tricks of all time:

using System.Collections.Generic;

[Range(5, 50)]
public int waypointsCount = 10;

void Start ()
{
    var Nodes = UnityEngine.GameObject.FindGameObjectsWithTag("Node");

    HashSet<GameObject> randomObjects = new HashSet<GameObject>();

    while(randomObjects.Count < waypointsCount){
        randomObjects.Add(Nodes[UnityEngine.Random.Range(0, Nodes.Length)]);
    }

    // use randomObjects
}

Here are HashSet docs. When you add an objects to a HashSet, the system checks if the object is already there. Only if it isn't - the object is added. That's why sets are used to maintain uniqueness in programming languages.

The script will choose a random GameObject from nodes, until randomGameobjects HashSet's size is equal to waypointsCount. Even if it chooses an already existing waypoint/node - it won't add it to the collection (because it's a HashSet - it does what it does).

\$\endgroup\$
7
\$\begingroup\$

You can do this faster than a Hashset with a simple linear scan:

var randomObjects = new GameObject[waypointsCount]; 

for(i = 0; i < waypointsCount; i++) {
    // Take only from the latter part of the list - ignore the first i items.
    int take = Random.Range(i, Nodes.Length);
    randomObjects[i] = Nodes[take];

    // Swap our random choice to the beginning of the array,
    // so we don't choose it again on subsequent iterations.
    Nodes[take] = Nodes[i];
    Nodes[i] = randomObjects[i];
}

This is effectively a Fisher-Yates shuffle (also called a Knuth shuffle) that terminates early.

Doing it this way has a couple of advantages:

  • we can build the output array in-place (even just using the same memory as the input Nodes array if we want to), without setting aside extra memory for the hash structure for membership tests.

  • if we want 49 items from a list of 50, we finish in exactly 49 iterations, every time. The Hashset method will take on average 3-4 times longer in this scenario, because its probability of choosing a duplicate and retrying goes up the longer our output list gets in proportion to the input. To pick the last random item in this scenario, the Hashset would need to try 25 times on average, since the list only contains 2 valid non-duplicate choices out of 50. The linear scan guarantees that we pick each item in a single try while avoiding duplicates through the re-ordering trick.

\$\endgroup\$
3
  • \$\begingroup\$ I tried to destroy the selected randomObjects but it's not destroying them. Why ? I added this line after the line Nodes[i] = randomObjects[i]; This: Destroy(randomObjects[i]); And i tried also DestroyImmediate but nothing. Tried then out the loop after the loop and also in the Update but nothing. And it's getting to the line Destroy/DestroyImmediate. The game is running but it's not destroying anything all the objects still in the scene/game view. \$\endgroup\$ Commented Aug 20, 2017 at 20:26
  • \$\begingroup\$ That sounds like a different question that's worth asking separately. It's unlikely to have anything to do with the random selection itself (you can confirm this by putting your destroy in a different loop, for example). Try to isolate the problem to a minimum reproducible sample - since Destroy is usually pretty well-behaved, the observations you describe are surprising and we'll need some help reproducing the issue. \$\endgroup\$
    – DMGregory
    Commented Aug 20, 2017 at 20:33
  • \$\begingroup\$ I will make a new question with the full two scripts. It's strange. \$\endgroup\$ Commented Aug 20, 2017 at 20:54

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .