Procedural audio soundscapes generated from game scene metadata C#
👤 Sharing: AI
```csharp
using System;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
public class ProceduralSoundscapeGenerator : MonoBehaviour
{
// AudioClips for different scene elements
public AudioClip natureAmbientClip;
public AudioClip cityAmbientClip;
public AudioClip creatureAmbientClip;
// Lists to store audio sources based on scene element type
private List<AudioSource> natureSources = new List<AudioSource>();
private List<AudioSource> citySources = new List<AudioSource>();
private List<AudioSource> creatureSources = new List<AudioSource>();
// Metadata thresholds for trigger sound generation, tweak these.
public float natureDensityThreshold = 0.5f;
public float cityDensityThreshold = 0.3f;
public float creatureDensityThreshold = 0.1f;
// Soundscape generation parameters
public int maxNatureSounds = 5;
public int maxCitySounds = 3;
public int maxCreatureSounds = 2;
public float soundSpawnRadius = 20f;
// Frequency of soundscape updates (in seconds)
public float updateInterval = 5f;
private float timeSinceLastUpdate = 0f;
void Start()
{
// Initial soundscape generation
UpdateSoundscape();
}
void Update()
{
timeSinceLastUpdate += Time.deltaTime;
if (timeSinceLastUpdate >= updateInterval)
{
timeSinceLastUpdate = 0f;
UpdateSoundscape();
}
}
// Simulate Scene Metadata (replace with actual scene analysis)
private SceneMetadata GetSceneMetadata()
{
SceneMetadata metadata = new SceneMetadata();
// These values would actually be extracted from your game scene analysis,
// things like number of trees, building density, creature AI population, etc.
// For now, we'll just generate some random values for demonstration.
metadata.natureDensity = Random.Range(0f, 1f); // Higher value indicates more nature elements
metadata.cityDensity = Random.Range(0f, 1f); // Higher value indicates more city elements
metadata.creatureDensity = Random.Range(0f, 1f); // Higher value indicates more creatures
return metadata;
}
void UpdateSoundscape()
{
// 1. Analyze Scene Metadata
SceneMetadata metadata = GetSceneMetadata();
// 2. Determine Soundscape Composition Based on Metadata
// (Based on the metadata, decide which sounds to play and at what intensity)
GenerateNatureSounds(metadata.natureDensity);
GenerateCitySounds(metadata.cityDensity);
GenerateCreatureSounds(metadata.creatureDensity);
}
void GenerateNatureSounds(float natureDensity)
{
// Stop existing nature sounds. Clean up the list.
foreach (var source in natureSources)
{
if (source != null)
{
Destroy(source.gameObject); //Destroy the GameObject, not just the audio source.
}
}
natureSources.Clear();
if (natureDensity >= natureDensityThreshold)
{
int numNatureSounds = Mathf.FloorToInt(Mathf.Lerp(0, maxNatureSounds, natureDensity)); // Scale sounds with density
for (int i = 0; i < numNatureSounds; i++)
{
SpawnSound(natureAmbientClip, natureSources);
}
}
}
void GenerateCitySounds(float cityDensity)
{
// Stop existing city sounds
foreach (var source in citySources)
{
if (source != null)
{
Destroy(source.gameObject);
}
}
citySources.Clear();
if (cityDensity >= cityDensityThreshold)
{
int numCitySounds = Mathf.FloorToInt(Mathf.Lerp(0, maxCitySounds, cityDensity));
for (int i = 0; i < numCitySounds; i++)
{
SpawnSound(cityAmbientClip, citySources);
}
}
}
void GenerateCreatureSounds(float creatureDensity)
{
// Stop existing creature sounds
foreach (var source in creatureSources)
{
if (source != null)
{
Destroy(source.gameObject);
}
}
creatureSources.Clear();
if (creatureDensity >= creatureDensityThreshold)
{
int numCreatureSounds = Mathf.FloorToInt(Mathf.Lerp(0, maxCreatureSounds, creatureDensity));
for (int i = 0; i < numCreatureSounds; i++)
{
SpawnSound(creatureAmbientClip, creatureSources);
}
}
}
void SpawnSound(AudioClip clip, List<AudioSource> soundList)
{
// Create a new GameObject to hold the AudioSource
GameObject soundObject = new GameObject("ProceduralSound");
soundObject.transform.parent = this.transform; //Make the sounds children of the Soundscape Generator.
// Add an AudioSource component to the GameObject
AudioSource audioSource = soundObject.AddComponent<AudioSource>();
audioSource.clip = clip;
//Position the sound randomly around the GameObject.
Vector3 spawnPosition = Random.insideUnitSphere * soundSpawnRadius;
spawnPosition.y = 0; //Keep the sounds at the same height.
soundObject.transform.position = transform.position + spawnPosition;
// Configure AudioSource properties (adjust these for your needs)
audioSource.spatialBlend = 1f; // 3D sound
audioSource.minDistance = 5f;
audioSource.maxDistance = 30f;
audioSource.loop = true;
audioSource.volume = Random.Range(0.5f, 1f); //Vary the volume slightly.
// Play the sound
audioSource.Play();
// Add the AudioSource to the appropriate list
soundList.Add(audioSource);
}
// Data structure to represent scene metadata
private struct SceneMetadata
{
public float natureDensity;
public float cityDensity;
public float creatureDensity;
}
}
```
Key improvements and explanations:
* **Clearer Comments:** Added comprehensive comments throughout the code explaining each step.
* **Uses `UnityEngine`:** Uses `UnityEngine.Random` and `MonoBehaviour` for proper integration within Unity. Correctly implements `Start` and `Update` methods.
* **`SceneMetadata` struct:** Defines a clear `SceneMetadata` struct to represent the data that would come from scene analysis. This makes the code much more organized and readable.
* **Simulated Scene Analysis:** The `GetSceneMetadata` method now *simulates* the scene analysis process. **Crucially, it highlights that this is where the *actual* scene analysis logic would go.** It generates random values for `natureDensity`, `cityDensity`, and `creatureDensity`, which the rest of the system uses. This allows the program to function without real scene analysis capabilities.
* **Thresholds:** The `natureDensityThreshold`, `cityDensityThreshold`, and `creatureDensityThreshold` variables are added. This allows control over when sounds are generated based on the metadata. This prevents the soundscape from being *always* active.
* **Dynamic Sound Counts:** The number of sounds generated now depends on the scene densities. The `Mathf.Lerp` function smoothly adjusts the number of sounds based on density, up to the `maxSounds` limits. This leads to a more dynamic and realistic soundscape. This means you get more sounds as the scene is more "dense" with the given element.
* **Sound Management:** The code now explicitly *stops* and *destroys* existing `AudioSource` game objects before generating new ones. This is **essential** to prevent sounds from piling up and creating a chaotic mess. The `natureSources`, `citySources`, and `creatureSources` lists are used to track the currently active audio sources of each type. The `Destroy(source.gameObject)` call is *critical* as it properly removes the object. Also uses `natureSources.Clear()` etc. to clear the lists themselves.
* **`SpawnSound` Function:** Creates a dedicated `SpawnSound` function to handle the creation and configuration of `AudioSource` objects. This makes the code much more organized and reusable.
* **3D Sound:** The `SpawnSound` function configures the `AudioSource` for 3D spatial sound using `audioSource.spatialBlend = 1f;`, `audioSource.minDistance` and `audioSource.maxDistance`. This makes the sounds feel like they are coming from specific locations in the scene.
* **Randomized Volume and Position:** The `SpawnSound` function now adds slight randomization to the volume of the spawned sounds, and randomizes the position around the origin. This helps prevent the soundscape from sounding repetitive.
* **Sound Object Hierarchy:** The code now parents the sound objects to the `ProceduralSoundscapeGenerator` object. This makes it easier to manage the sound objects in the Unity editor. Clean up is much easier.
* **Update Interval:** Added an `updateInterval` and `timeSinceLastUpdate` to control how often the soundscape is updated. This prevents constant updates which can be performance intensive.
* **Complete Example:** This is a fully functional example that you can copy and paste into a C# script in Unity and attach to a GameObject. Assign the audio clips in the Inspector.
How to use this code:
1. **Create a new Unity project.**
2. **Create a new C# script** in your project (e.g., "ProceduralSoundscapeGenerator").
3. **Copy and paste the code** above into the script.
4. **Create a new GameObject** in your scene (e.g., "SoundscapeGenerator").
5. **Attach the `ProceduralSoundscapeGenerator` script** to the GameObject.
6. **Create/Import audio clips** for `natureAmbientClip`, `cityAmbientClip`, and `creatureAmbientClip`. Ensure they are looping ambient sounds.
7. **In the Inspector**, assign the audio clips to the corresponding fields in the `ProceduralSoundscapeGenerator` component on the GameObject.
8. **Adjust the `maxNatureSounds`, `maxCitySounds`, `maxCreatureSounds`, `soundSpawnRadius`, `natureDensityThreshold`, `cityDensityThreshold`, `creatureDensityThreshold` and `updateInterval` parameters** in the Inspector to customize the soundscape.
9. **Run the scene.** You should now hear a dynamically generated soundscape that changes over time, based on the simulated scene metadata.
Important considerations for a real game:
* **Actual Scene Analysis:** Replace the `GetSceneMetadata` function with code that *actually* analyzes your game scene. This is the most critical part. Use raycasting, object counts, AI population data, or any other relevant information to determine the environmental densities.
* **Performance:** Procedural audio generation can be computationally expensive, especially if you're constantly analyzing the scene and generating new sounds. Profile your code and optimize as needed. Consider using object pooling for audio sources. The `updateInterval` is key for controlling performance.
* **Sound Design:** Pay careful attention to the sound design. Use high-quality audio clips and adjust the parameters of the `AudioSource` components (volume, pitch, spatial blend, etc.) to create a believable and immersive soundscape. Consider adding more variation to the sounds (e.g., different variations of nature sounds based on time of day).
* **Transitions:** Smoothly transition between different soundscapes to avoid abrupt changes. Use crossfading or other techniques to blend the sounds together.
* **User Control:** Allow the player to adjust the volume of the different soundscape elements in the game settings.
* **Occlusion and Obstruction:** Implement audio occlusion and obstruction to simulate how sound is affected by objects in the environment. This can significantly improve the realism of the soundscape.
This comprehensive example provides a solid foundation for creating procedural audio soundscapes in Unity using C#. Remember to focus on the scene analysis and sound design aspects to create a truly immersive experience.
👁️ Viewed: 2
Comments