[SOLVED] Storing previous value in Random.Range

I am randomly setting game objects active and inactive in my scene.
How ever sometimes i get the same objects to turn on because random.range gets the same number.
How can I save the previous value so that I can make it full random without repeating the previous combinations of game objects active?

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class randomx : MonoBehaviour {
    public GameObject[] assets;
    public GameObject[] assets2;
    public GameObject[] assets3;
    private int currentIndex;
    private int current2;
    private int current3;

    void Start(){
//   
    }
        void Randomize() {
        // Random range
        int newIndex = Random.Range (0, assets.Length);
        int newIndex2 = Random.Range (0, assets2.Length);
        int newIndex3 = Random.Range (0, assets3.Length);
        Debug.Log (newIndex);
        //int previous;
        //previous =  newIndex = Random.Range (0, assets.Length);
   
        // Deactivate old gameobject
        assets [currentIndex].SetActive (false);
        assets2 [current2].SetActive (false);
        assets3 [current3].SetActive (false);
        // Activate new gameobject
        currentIndex = newIndex;
        current2 = newIndex2;
        current3 = newIndex3;
        foreach (GameObject shapes in assets) {
            if (shapes.activeInHierarchy == true)
                shapes.SetActive (false);
                assets [currentIndex].SetActive (true);

            }
        foreach (GameObject shapes2 in assets2) {
            if (shapes2.activeInHierarchy == true)
                shapes2.SetActive (false);
            assets2 [current2].SetActive (true);
        }
        foreach (GameObject shapes3 in assets3) {
            if (shapes3.activeInHierarchy == true)
                shapes3.SetActive (false);
            assets3 [current3].SetActive (true);
        }
    }
    // Update is called once per frame
    void Update () {
        if (Input.GetKeyDown (KeyCode.Space)) {
            Randomize ();

   
    }
}
}

you can randomize/shuffle the arrays first, so then can just pick next array item…

examples,

int newIndex;
do
{
     newIndex = Random.Range(0,assets.Length);
}while (newIndex == currentIndex);

Just do the above for all 3 indices. Note because your started with currentIndex,current2, and current 3 as uninitialzied and thus set to 0, the first time through the loop you can never pick Object 0 for any of the 3 things to turn on.

If this is a problem you can fix it in 2 ways:

  1. In Start randomly turn on 3 objects and set currentIndex, current2 and current3 to these objects.
  2. If you need them all to be off to start do something like this:
void Start()
{
     currentIndex = -1;
     current2 = -1;
     current3 = -1;
}

void Randomize()
{
       // Code to get New indexes

       if (currentIndex >=0)
       {
                assets[currentIndex].SetActive(false);
                assets[current2].SetActive(false);
                assets[current3].SetActive(false);
       }
      // rest of the Randomize code
}

I sometimes still get the previous gameobject active as the last input?
heres how I implemented your solution

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class randomx : MonoBehaviour {
    public GameObject[] assets;
    public GameObject[] assets2;
    public GameObject[] assets3;
    private int currentIndex;
    private int current2;
    private int current3;

    void Start(){
        currentIndex = -1;
        current2 = -1;
        current3 = -1;
//   
    }


        void Randomize() {
        int newIndex;
        int newIndex2;
        int newIndex3;
        do {
            newIndex = Random.Range (0, assets.Length);
            newIndex2    = Random.Range (0, assets2.Length);
            newIndex3 = Random.Range(0,assets3.Length);
        } while (newIndex == currentIndex && newIndex2 == current2 && newIndex3 == current3);

        if (currentIndex >= 0) {
            assets [currentIndex].SetActive (false);
            assets [current2].SetActive (false);
            assets [current3].SetActive (false);
        }


   
        //int newIndex = Random.Range (0, assets.Length);
//        int newIndex2 = Random.Range (0, assets2.Length);
//        int newIndex3 = Random.Range (0, assets3.Length);
//   
//
//        //assets2 [current2].SetActive (false);
//        //assets3 [current3].SetActive (false);
//         Activate new gameobject
        currentIndex = newIndex;
        current2 = newIndex2;
        current3 = newIndex3;
        foreach (GameObject shapes in assets) {
            if (shapes.activeInHierarchy == true)
                shapes.SetActive (false);
            assets [currentIndex].SetActive (true);
        }
        foreach (GameObject shapes2 in assets2) {
            if (shapes2.activeInHierarchy == true)
                shapes2.SetActive (false);
            assets2 [current2].SetActive (true);
        }
        foreach (GameObject shapes3 in assets3) {
            if (shapes3.activeInHierarchy == true)
                shapes3.SetActive (false);
            assets3 [current3].SetActive (true);
        }
    }

       

    // Update is called once per frame
    void Update () {
        if (Input.GetKeyDown (KeyCode.Space)) {
            Randomize ();

   
        }
    }



}

Advice against a while loop:

  • might get stuck forever in case of error
  • might try 3 million times because of sheer bad luck
  • smells unpredictable und unstable

Better: ShuffleBag
Create a collection from your source data and shuffle it. Then take out each piece until it is empty. Then handle the special case of refilling, refshuffling and making sure that the first on in the collection is not the last picked one from the previous one. Check out different implementations on the web. :wink:

1 Like

Well its because the idea was to have 3 separate while loops. In your case you only repicking if all 3 indexes happen to hit the last one (which is highly unlikely). You’d need to change those to || (ors). However that is still bad because if even currentIndex == oldIndex then you’d pick all 3 again needlessly. Your best bet if you implement the while implementation is to have 3 separate ones.

That being said we can implement a better solution with something like a ShuffleBag. I’m assuming you don’t actually want to iterate every object at least once before doing it again. That is: 1 4, 2,5,0,3 then start over. Its ok to go : 3,5,2,1,5,3 etc. A ShuffleBag would pick each item once and then restart.

What we can do is this:

    void Randomize()
    {
        int newIndex;
        int newIndex2;
        int newIndex3;
        if (currentIndex < 0)
        {
            newIndex = Random.Range(0, assets.Length);
            newIndex2 = Random.Range(0, assets2.Length);
            newIndex3 = Random.Range(0, assets3.Length);
        }
        else
        {
            assets[currentIndex].SetActive(false);
            assets[current2].SetActive(false);
            assets[current3].SetActive(false);
            newIndex = Random.Range(0, assets.Length-1);
            newIndex2 = Random.Range(0, assets2.Length-1);
            newIndex3 = Random.Range(0, assets3.Length-1);
            if (newIndex >= currentIndex)
                newIndex++;
            if (newIndex2 >= current2)
                newIndex++;
            if (newIndex3 >= current3)
                newIndex++;
        }

The idea here is say there are 10 objects (0->9). If last time around we picked 5, then currentIndex = 5. So we only choose a number from 0-8. if its 0-4 then we keep that number if its 5-8 we add one to it so we get 6-9. This gives us numbers from 0-4,6-9 skipping 5 which was our last object and we don’t want it.

1 Like

Yes shufflebag was definitely the best solution to my specific problem.
Thank you. :slight_smile: