10. Procedural generation

Spawning Items

Inside (on flat floor)

using UnityEngine;
using System.Collections.Generic;

public class SpawnItemsRoom : MonoBehaviour
{
    // Name of the folder containing item prefabs
    public string folderName;
    public int numberOfItemsToSpawn;

    private List<GameObject> itemPrefabs = new List<GameObject>();

    private void Start()
    {
        LoadItemPrefabs();
        SpawnItems();
    }

    private void LoadItemPrefabs()
    {
        GameObject[] prefabs = Resources.LoadAll<GameObject>(folderName);
        itemPrefabs.AddRange(prefabs);
    }

    private void SpawnItems()
    {
        Bounds spawnBounds = GetComponent<Renderer>().bounds;

        for (int i = 0; i < numberOfItemsToSpawn; i++)
        {
            // Generate a random position within the room area
            Vector3 randomPosition = new Vector3(
                Random.Range(spawnBounds.min.x, spawnBounds.max.x),
                spawnBounds.min.y + 0.3f,
                Random.Range(spawnBounds.min.z, spawnBounds.max.z)
            );

            // Instantiate a random rock prefab
            int randomIndex = Random.Range(0, itemPrefabs.Count);
            GameObject selectedPrefab = itemPrefabs[randomIndex];
            Quaternion randomRotation = Quaternion.Euler(0f, Random.Range(0f, 360f), 0f);
            GameObject newRock = Instantiate(selectedPrefab, randomPosition, randomRotation);
            newRock.transform.parent = transform; // Set the current GameObject as the rock's parent
        }
    }
}

Outside (on uneven terrain)

using UnityEngine;
using System.Collections.Generic;

public class SpawnItemsTerrain : MonoBehaviour
{
    // Name of the folder containing item prefabs
    public string folderName;
    public int numberOfItemsToSpawn;

    private float raycastMaxDistance;
    private List<GameObject> itemPrefabs = new List<GameObject>();

    private void Start()
    {
        // Calculate the maximum distance for raycasting (cube height)
        raycastMaxDistance = GetComponent<Renderer>().bounds.max.y - GetComponent<Renderer>().bounds.min.y;
        LoadItemPrefabs();
        SpawnItems();
    }

    private void LoadItemPrefabs()
    {
        GameObject[] prefabs = Resources.LoadAll<GameObject>(folderName);
        itemPrefabs.AddRange(prefabs);
    }

    private void SpawnItems()
    {
        for (int i = 0; i < numberOfItemsToSpawn; i++)
        {
            // Get random position within the spawn area cube
            Vector3 randomPosition = GetRandomPosition();

            // Cast a ray downwards from the random position
            RaycastHit hit;
            if (Physics.Raycast(randomPosition, Vector3.down, out hit, raycastMaxDistance))
            {
                // Instantiate a random rock prefab at the hit point
                int randomIndex = Random.Range(0, itemPrefabs.Count);
                GameObject selectedPrefab = itemPrefabs[randomIndex];
                Quaternion randomRotation = Quaternion.Euler(0f, Random.Range(0f, 360f), 0f);
                GameObject newRock = Instantiate(selectedPrefab, hit.point, randomRotation);
                newRock.transform.parent = transform; // Set the current GameObject as the rock's parent
            }
        }
    }

    private Vector3 GetRandomPosition()
    {
        Bounds spawnBounds = GetComponent<Renderer>().bounds;

        float randomX = Random.Range(spawnBounds.min.x, spawnBounds.max.x);
        float randomZ = Random.Range(spawnBounds.min.z, spawnBounds.max.z);

        return new Vector3(randomX, spawnBounds.max.y + 2f, randomZ);
    }
}
< ^ >