How do I restrict one random prefab to be used only once but placed randomly with a bunch of prefabs of arrays on top of other object?

using System.Collections.Generic;
using UnityEngine;

public class LevelRoomsScript : MonoBehaviour
{
    [SerializeField]
    private GameObject[] memberWoodArray = null;
    [SerializeField]
    private GameObject[] memberRoomPrefabArray = null;
    
    void Start()
    {
    
        foreach (GameObject localWood in memberWoodArray)
        {
            int localNumRoomPrefab = memberRoomPrefabArray.Length;
            int localRoomIndex = Random.Range(0, localNumRoomPrefab);
            
            GameObject localRoomPrefab = memberRoomPrefabArray[localRoomIndex];
            
            Instantiate(localRoomPrefab, localWood.transform.position, Quaternion.identity);
        }
    }
}
2

There are 2 answers

0
derHugo On

You rather want to "shuffle" the array once and then iterate the shuffled array e.g. using Linq OrderBy and using Random.value as order like

using System.Linq;

...

void Start()
{
    if(memberRoomPrefabArray.Length < memberWoodArray.Length)
    {
        Debug.LogError($"Not enough prefabs available for {memberWoodArray.Length} unique spawns!", this);
        return;
    }

    // as the method names suggest this will be a randomized array of the available prefabs
    var shuffledPrefabs = memberRoomPrefabArray.OrderBy(m => Random.value).ToArray();
    for (var i = 0; i < memberWoodArray.Length; i++)
    {    
        // Since the array is already shuffled we can now go by consecutive order
        Instantiate(suffledPrefabs[i], memberWoodArray[i].transform.position, Quaternion.identity);
    }
}
0
Mortyr On

The way I understand your question is that you want to instantiate each element in memberRoomPrefabArray at most once. You could create a temporary list that is a copy of memberRoomPrefabArray and remove each element that is instantiated before the next loop cycle.

void Start()
{
    List<GameObject> temp = new List<GameObject>(memberRoomPrefabArray);
    
    foreach (GameObject localWood in memberWoodArray)
    {
        int localRoomIndex = Random.Range(0, temp.Count);
        
        Instantiate(temp[localRoomIndex], localWood.transform.position, Quaternion.identity);
        
        temp.RemoveAt(localRoomIndex);
    }
}

You might want to add some checks like if (temp.Count == 0) { break; } if it's possible for memberRoomPrefabArray to be shorter than memberWoodArray.

Edit: Changed Random.Range(0, temp.Count - 1) to Random.Range(0, temp.Count) since, apparently, it's only maximally inclusive with floats and not integers.