Here's one way to do it, using a custom struct to store object-weight pairs:
public class Spawner : MonoBehaviour
// Here's where we define our weighted object structure,
// and flag it Serializable so it can be edited in the Inspector.
public struct Spawnable
public GameObject gameObject;
public float weight;
// Now expose an array of these to be populated in the Inspector.
public Spawnable spawnList;
// Track the total weight used in the whole array.
// Update the total weight when the user modifies Inspector properties,
// and on initialization at runtime.
_totalSpawnWeight = 0f;
foreach(var spawnable in spawnList)
_totalSpawnWeight += spawnable.weight;
// As Problematic points out below, OnValidate isn't called
// in a built executable. But in that case we don't need to react
// to a user fiddling with the Inspector mid-game, so it suffices
// to run this code once during Awake:
// Spawn an item randomly, according to the relative weights.
public void Spawn()
// Generate a random position in the list.
float pick = Random.value * _totalSpawnWeight;
int chosenIndex = 0;
float cumulativeWeight = spawnList.weight;
// Step through the list until we've accumulated more weight than this.
// The length check is for safety in case rounding errors accumulate.
while(pick > cumulativeWeight && chosenIndex < spawnList.length - 1)
cumulativeWeight += spawnList[chosenIndex].weight;
// Spawn the chosen item.
Instantiate(spawnList[chosenIndex].gameObject, transform.position, transform.rotation);
This shows up in the Inspector like so:
You can make this look neater by creating a custom PropertyDrawer or Editor, but out of the box this gets the core functionality.
Note that your weights don't have to sum to 1 or 100 or any other particular value. They're taken as portions relative to the total. As long as they're non-negative they should "just work."