<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>FMOD on DAD 424 Sound Design for Games</title><link>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/tags/fmod/</link><description>Recent content in FMOD on DAD 424 Sound Design for Games</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Tue, 21 Oct 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/tags/fmod/index.xml" rel="self" type="application/rss+xml"/><item><title>Building Dynamic Footstep Sounds in Unity with FMOD and Raycasting</title><link>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-9/automating-audio-with-parameters-2/</link><pubDate>Tue, 21 Oct 2025 00:00:00 +0000</pubDate><guid>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-9/automating-audio-with-parameters-2/</guid><description>Create adaptive footsteps in Unity with FMOD using raycasts, surface tags, and an enum-backed parameter that maps directly to FMOD.</description><content>&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>Creating immersive game soundscapes often comes down to subtle details—like the sound of footsteps adapting to different surfaces. In this tutorial, we’ll expand upon previous FMOD parameter work by integrating Unity’s physics system to detect materials underfoot. Using &lt;strong>raycasting&lt;/strong> and &lt;strong>tags&lt;/strong>, we’ll dynamically alter footstep sounds to reflect the player’s environment—be it grass, stone, puddles, or earth.&lt;/p>
&lt;p>This guide walks through each step: tagging surfaces, scripting raycasts, and linking an enum-backed FMOD parameter for a responsive, data‑driven footstep system. If you follow along from top to bottom, you’ll go from a blank scene to fully adaptive footsteps.&lt;/p>
&lt;hr>
&lt;h2 id="recap-where-we-left-off">Recap: Where We Left Off&lt;/h2>
&lt;p>In the previous session, we created a simple FMOD event that played footstep sounds through Unity animation triggers. We:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Created a footstep event&lt;/strong> in FMOD with a “Material” parameter.&lt;/li>
&lt;li>&lt;strong>Wrote a Unity C# script&lt;/strong> to instantiate, play, and destroy the event on each step.&lt;/li>
&lt;li>&lt;strong>Linked animation events&lt;/strong> to ensure footstep sounds triggered precisely in sync.&lt;/li>
&lt;/ol>
&lt;p>Now, we’ll enhance that system so footsteps automatically change depending on what surface the player is walking or running across.&lt;/p>
&lt;hr>
&lt;h2 id="step-1-setting-up-tags-in-unity">Step 1: Setting Up Tags in Unity&lt;/h2>
&lt;p>Tags let you label objects and detect them in scripts. Each surface type gets a unique tag.&lt;/p>
&lt;ol>
&lt;li>Navigate to &lt;strong>Edit → Project Settings → Tags &amp;amp; Layers&lt;/strong>.&lt;/li>
&lt;li>Under &lt;strong>Tags&lt;/strong>, click &lt;strong>+&lt;/strong> and add:
&lt;ul>
&lt;li>Earth&lt;/li>
&lt;li>Stone&lt;/li>
&lt;li>Puddle&lt;/li>
&lt;li>Grass&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>&lt;img alt="alt text" src="https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/f-25-dad-424-sound-design-for-games/lectures/week-9/automating-audio-with-parameters-2/tags.png">&lt;/p>
&lt;p>Assign these tags to relevant scene objects—terrain, stairs, platforms, etc. Apply the tag to the collider you expect the raycast to hit (often on the same GameObject that has the Mesh Collider or Box Collider).&lt;/p>
&lt;p>&lt;img alt="alt text" src="https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/f-25-dad-424-sound-design-for-games/lectures/week-9/automating-audio-with-parameters-2/tag-stone.png">
Tagging even a few surfaces is enough to test. Later, we&amp;rsquo;ll expand this system to cover more materials using Physics Materials or Terrain Textures.&lt;/p>
&lt;hr>
&lt;h2 id="step-2-script-framework">Step 2: Script Framework&lt;/h2>
&lt;p>Start by defining an enum for surface types and the core fields/methods in your player script. Add this to your player controller (or a dedicated audio component on the player).&lt;/p>
&lt;blockquote>
&lt;p>An enum is a new data type we haven&amp;rsquo;t used yet. It lets us define a set of named constants, which we can use to represent discrete values (like surface types). This makes our code more readable and less error-prone than using raw integers or strings.&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> UnityEngine;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> FMODUnity;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> FMOD.Studio;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">enum&lt;/span> MaterialType { Earth = &lt;span style="color:#ae81ff">0&lt;/span>, Stone = &lt;span style="color:#ae81ff">1&lt;/span>, Puddle = &lt;span style="color:#ae81ff">2&lt;/span>, Grass = &lt;span style="color:#ae81ff">3&lt;/span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">F_Player&lt;/span> : MonoBehaviour
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e"> [SerializeField]&lt;/span> &lt;span style="color:#66d9ef">private&lt;/span> LayerMask groundLayerMask;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e"> [SerializeField]&lt;/span> &lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">float&lt;/span> rayDistance = &lt;span style="color:#ae81ff">1.5f&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">private&lt;/span> MaterialType currentMaterial;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">private&lt;/span> RaycastHit hitInfo;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">private&lt;/span> Rigidbody rb;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Awake()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> rb = GetComponent&amp;lt;Rigidbody&amp;gt;();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Update()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Step 4 will explain this debug line&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Debug.DrawRay(transform.position, Vector3.down * rayDistance, Color.red);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> PlayRunEvent(&lt;span style="color:#66d9ef">string&lt;/span> eventPath)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#66d9ef">string&lt;/span>.IsNullOrEmpty(eventPath)) &lt;span style="color:#66d9ef">return&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MaterialCheck();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> EventInstance run = RuntimeManager.CreateInstance(eventPath);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RuntimeManager.AttachInstanceToGameObject(run, gameObject, rb);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Step 6: we&amp;#39;ll create/start the FMOD event and set the parameter&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> MaterialCheck()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Steps 3–5: raycast and set currentMaterial&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Notes:&lt;/p>
&lt;ul>
&lt;li>Enum names must match tags exactly: &lt;code>Earth&lt;/code>, &lt;code>Stone&lt;/code>, &lt;code>Puddle&lt;/code>, &lt;code>Grass&lt;/code>.&lt;/li>
&lt;li>&lt;code>groundLayerMask&lt;/code> should include only layers containing walkable geometry.&lt;/li>
&lt;li>&lt;code>rayDistance&lt;/code> defaults to 1.5m; adjust to your character height.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="step-3-raycasting-to-detect-surfaces">Step 3: Raycasting to Detect Surfaces&lt;/h2>
&lt;p>The script casts a ray downward from the player to find the surface beneath:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">if&lt;/span> (Physics.Raycast(transform.position, Vector3.down, &lt;span style="color:#66d9ef">out&lt;/span> hitInfo, rayDistance, groundLayerMask))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// hitInfo.collider is the surface collider we tagged&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;code>transform.position&lt;/code> = Player position&lt;/li>
&lt;li>&lt;code>Vector3.down&lt;/code> = Direction toward the ground&lt;/li>
&lt;li>&lt;code>rayDistance&lt;/code> = Ray length (default 1.5m)&lt;/li>
&lt;li>&lt;code>groundLayerMask&lt;/code> = Limit detection to your environment layers (e.g., &lt;code>Environment&lt;/code>, &lt;code>WaterGeometry&lt;/code>)&lt;/li>
&lt;/ul>
&lt;p>In the Inspector, assign the &lt;code>groundLayerMask&lt;/code> to include only layers with tagged ground geometry.&lt;/p>
&lt;p>If you find gaps on stairs or steep terrain, consider a small sphere cast:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">if&lt;/span> (Physics.SphereCast(transform.position, &lt;span style="color:#ae81ff">0.1f&lt;/span>, Vector3.down, &lt;span style="color:#66d9ef">out&lt;/span> hitInfo, rayDistance, groundLayerMask))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Slightly more forgiving than a thin ray&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="step-4-visualize-the-ray">Step 4: Visualize the Ray&lt;/h2>
&lt;p>The script draws the ray each frame for easy debugging:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>Debug.DrawRay(transform.position, Vector3.down * rayDistance, Color.red);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>View this in the Scene window as you move the player to ensure correct origin and length.&lt;/p>
&lt;hr>
&lt;h2 id="step-5-tags--enum-mapping">Step 5: Tags → Enum Mapping&lt;/h2>
&lt;p>Inside &lt;code>MaterialCheck()&lt;/code> and &lt;code>Raycast&lt;/code> if statement, add this code to convert the detected tag into the enum value:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">if&lt;/span> (System.Enum.TryParse(hitInfo.collider.tag, &lt;span style="color:#66d9ef">out&lt;/span> MaterialType material))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> currentMaterial = material;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>TryParse: Attempts to map the tag (e.g., &amp;ldquo;Stone&amp;rdquo;) to &lt;code>MaterialType.Stone&lt;/code>.&lt;/li>
&lt;li>Safe update: Only assigns &lt;code>currentMaterial&lt;/code> when parsing returns true; otherwise the previous value remains.&lt;/li>
&lt;li>Optional default: If you want an explicit fallback when parsing fails, add an else:&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">if&lt;/span> (System.Enum.TryParse(hitInfo.collider.tag, &lt;span style="color:#66d9ef">out&lt;/span> MaterialType material))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> currentMaterial = material;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> currentMaterial = MaterialType.Earth; &lt;span style="color:#75715e">// default&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="step-6-fmod-parameter-setup">Step 6: FMOD Parameter Setup&lt;/h2>
&lt;p>The script sets the parameter via, which you&amp;rsquo;ll add inside &lt;code>PlayRunEvent&lt;/code> before starting the event:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>run.setParameterByName(&lt;span style="color:#e6db74">&amp;#34;Material&amp;#34;&lt;/span>, (&lt;span style="color:#66d9ef">float&lt;/span>)currentMaterial);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="step-7-full-script">Step 7: Full Script&lt;/h2>
&lt;p>Here’s the complete script with all steps combined:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> UnityEngine;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> FMODUnity;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> FMOD.Studio;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">enum&lt;/span> MaterialType
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Earth = &lt;span style="color:#ae81ff">0&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Stone = &lt;span style="color:#ae81ff">1&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Puddle = &lt;span style="color:#ae81ff">2&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Grass = &lt;span style="color:#ae81ff">3&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">F_Player&lt;/span> : MonoBehaviour
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e"> [SerializeField]&lt;/span> &lt;span style="color:#66d9ef">private&lt;/span> LayerMask groundLayerMask;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e"> [SerializeField]&lt;/span> &lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">float&lt;/span> rayDistance = &lt;span style="color:#ae81ff">1.5f&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">private&lt;/span> MaterialType currentMaterial;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">private&lt;/span> RaycastHit hitInfo;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">private&lt;/span> Rigidbody rb;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Awake()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> rb = GetComponent&amp;lt;Rigidbody&amp;gt;();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Update()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Debug.DrawRay(transform.position, Vector3.down * rayDistance, Color.red);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> PlayRunEvent(&lt;span style="color:#66d9ef">string&lt;/span> eventPath)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#66d9ef">string&lt;/span>.IsNullOrEmpty(eventPath)) &lt;span style="color:#66d9ef">return&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MaterialCheck();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> EventInstance run = RuntimeManager.CreateInstance(eventPath);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RuntimeManager.AttachInstanceToGameObject(run, gameObject, rb);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> run.setParameterByName(&lt;span style="color:#e6db74">&amp;#34;Material&amp;#34;&lt;/span>, (&lt;span style="color:#66d9ef">float&lt;/span>)currentMaterial);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> run.start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> run.release();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> MaterialCheck()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (Physics.Raycast(transform.position, Vector3.down, &lt;span style="color:#66d9ef">out&lt;/span> hitInfo, rayDistance, groundLayerMask))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Debug.Log(&lt;span style="color:#e6db74">&amp;#34;Hit material: &amp;#34;&lt;/span> + hitInfo.collider.tag);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (System.Enum.TryParse(hitInfo.collider.tag, &lt;span style="color:#66d9ef">out&lt;/span> MaterialType material))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> currentMaterial = material;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;p>&lt;em>References:&lt;/em>&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://docs.unity3d.com/ScriptReference/Physics.Raycast.html">Unity Raycasting Documentation&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.unity3d.com/ScriptReference/Vector3.html">Unity Vector3 Directions&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://fmod.com/resources/documentation-api">FMOD Studio API Reference&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.unity3d.com/ScriptReference/Color.html">Unity Color Struct&lt;/a>&lt;/li>
&lt;/ul></content></item><item><title>Driving FMOD Parameters from Unity: Teleport Sound Example</title><link>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-8/automating-audio-with-parameters-1/</link><pubDate>Mon, 13 Oct 2025 00:00:00 +0000</pubDate><guid>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-8/automating-audio-with-parameters-1/</guid><description>Introduction In this tutorial, you’ll build a reactive teleportation sound system by controlling FMOD parameters from Unity using C#. You’ll learn to:
Create and automate parameters in FMOD Instantiate and attach FMOD events in Unity Dynamically set parameter values via C# scripts Trigger sound transitions via Unity collision events FMOD Parameters are made up of a range of values that can be adjusted in real-time inside our game. This allows us to create dynamic audio that responds to gameplay events, such as a player entering a teleporter.</description><content>&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>In this tutorial, you’ll build a reactive teleportation sound system by controlling FMOD parameters from Unity using C#. You’ll learn to:&lt;/p>
&lt;ul>
&lt;li>Create and automate parameters in FMOD&lt;/li>
&lt;li>Instantiate and attach FMOD events in Unity&lt;/li>
&lt;li>Dynamically set parameter values via C# scripts&lt;/li>
&lt;li>Trigger sound transitions via Unity collision events&lt;/li>
&lt;/ul>
&lt;p>FMOD Parameters are made up of a range of values that can be adjusted in real-time inside our
game. This allows us to create dynamic audio that responds to gameplay events, such as a player entering a teleporter.&lt;/p>
&lt;hr>
&lt;h2 id="fmod-setup">FMOD Setup&lt;/h2>
&lt;p>Before diving into code, we’ll first build the &lt;strong>teleporter event&lt;/strong> in FMOD.
This event demonstrates how to use both &lt;strong>built-in&lt;/strong> and &lt;strong>user parameters&lt;/strong> to control sound behavior dynamically.&lt;/p>
&lt;h3 id="goal">Goal&lt;/h3>
&lt;p>We’re designing an event that:&lt;/p>
&lt;ul>
&lt;li>Loops a magical hum sound for a teleporter object.&lt;/li>
&lt;li>Uses the built-in &lt;strong>Distance&lt;/strong> parameter to fade out low frequencies as the listener moves away.&lt;/li>
&lt;li>Uses a custom &lt;strong>Trigger&lt;/strong> parameter to fire a secondary teleport sound when the player enters the teleporter.&lt;/li>
&lt;/ul>
&lt;p>When done, your FMOD setup will let Unity control when the teleport sound plays—while FMOD automatically handles distance-based EQ changes.&lt;/p>
&lt;hr>
&lt;h3 id="1-create-the-event">1. Create the Event&lt;/h3>
&lt;ol>
&lt;li>Open &lt;strong>FMOD Studio&lt;/strong>.&lt;/li>
&lt;li>In the &lt;strong>Assets Browser&lt;/strong>, find &lt;em>Teleporter Loop Stereo 01&lt;/em> under
&lt;code>Audio Bin → Interactables → Teleporters&lt;/code>.&lt;/li>
&lt;li>Create a new event named &lt;strong>Teleporter&lt;/strong> in the &lt;code>SFX/Interactables&lt;/code> folder.&lt;/li>
&lt;li>Drag &lt;em>Teleporter Loop Stereo 01&lt;/em> onto &lt;strong>Track 1&lt;/strong> at the beginning of the timeline.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h3 id="2-loop-region-and-asynchronous-playback">2. Loop Region and Asynchronous Playback&lt;/h3>
&lt;ol>
&lt;li>Create a &lt;strong>loop region&lt;/strong> around the first second of the timeline.
This keeps the playhead cycling near the start while the loop plays.&lt;/li>
&lt;li>Select your sound instrument and:
&lt;ul>
&lt;li>Enable &lt;strong>Async&lt;/strong> playback. This ensures the sound plays to completion even if the playhead remains in the loop region.&lt;/li>
&lt;li>Enable &lt;strong>Loop While Playing&lt;/strong>.&lt;/li>
&lt;li>Enable &lt;strong>Cut&lt;/strong> (cut playback when untriggered) — so when the loop region deactivates, playback stops immediately.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;details>
&lt;summary>Sound Instrument Settings: Async, Loop While Playing, and Cut&lt;/summary>
&lt;h3 id="async">Async&lt;/h3>
&lt;p>Controls whether a sound keeps playing after the playhead leaves its region.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>ON&lt;/strong> – plays to completion; good for one-shots like explosions.&lt;/li>
&lt;li>&lt;strong>OFF&lt;/strong> – stops when the region ends; use for loops tied to the source (e.g., footsteps, engines).&lt;/li>
&lt;/ul>
&lt;h3 id="loop-while-playing">Loop While Playing&lt;/h3>
&lt;p>Makes the instrument repeat while it remains triggered.&lt;/p>
&lt;ul>
&lt;li>Combine with &lt;strong>Async ON&lt;/strong> to loop freely until untriggered.&lt;/li>
&lt;li>Combine with &lt;strong>Async OFF&lt;/strong> to loop only within the region.&lt;/li>
&lt;/ul>
&lt;h3 id="cut">Cut&lt;/h3>
&lt;p>Determines what happens when an async loop is untriggered.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>ON&lt;/strong> – stops immediately; use for UI or speech.&lt;/li>
&lt;li>&lt;strong>OFF&lt;/strong> – plays out or fades naturally; use for ambiences.&lt;/li>
&lt;/ul>
&lt;p>&lt;em>Note:&lt;/em> In current FMOD versions, &lt;strong>Cut&lt;/strong> mainly affects async looping instruments.
For overlap control, use &lt;strong>Polyphony = 1&lt;/strong> or &lt;strong>Steal Oldest&lt;/strong> instead.&lt;/p>
&lt;hr>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Example&lt;/th>
&lt;th>Async&lt;/th>
&lt;th>Loop&lt;/th>
&lt;th>Cut&lt;/th>
&lt;th>Notes&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Explosion&lt;/td>
&lt;td>ON&lt;/td>
&lt;td>OFF&lt;/td>
&lt;td>OFF&lt;/td>
&lt;td>Rings out naturally&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Footsteps&lt;/td>
&lt;td>OFF&lt;/td>
&lt;td>ON&lt;/td>
&lt;td>ON&lt;/td>
&lt;td>Stops instantly&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Ambience&lt;/td>
&lt;td>OFF&lt;/td>
&lt;td>ON&lt;/td>
&lt;td>OFF&lt;/td>
&lt;td>Continuous loop&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>UI click&lt;/td>
&lt;td>ON&lt;/td>
&lt;td>OFF&lt;/td>
&lt;td>ON&lt;/td>
&lt;td>Prevents overlap&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;/details>
&lt;hr>
&lt;h3 id="3-add-and-automate-the-distance-parameter">3. Add and Automate the Distance Parameter&lt;/h3>
&lt;p>The &lt;strong>Distance&lt;/strong> parameter is one of FMOD’s built-in parameters.
There are six built-in parameters: Distance, Direction, Speed, Orientation, Elevation, and Cone Angle.&lt;/p>
&lt;p>Built-in parameters automatically update based on the listener’s position relative to the sound source in 3D space.&lt;/p>
&lt;p>See more information here: &lt;a href="https://www.fmod.com/docs/2.03/studio/parameters-reference.html#built-in-parameters">FMOD Built-In Parameters&lt;/a>&lt;/p>
&lt;p>&lt;strong>To add and automate Distance:&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>Click the “+” icon → choose &lt;strong>Add Parameter Sheet → New Parameter&lt;/strong>.&lt;/li>
&lt;li>Name it &lt;strong>Distance&lt;/strong> and choose the &lt;strong>Built-In&lt;/strong> type for &lt;strong>Distance&lt;/strong>.&lt;/li>
&lt;li>On your track, insert a &lt;strong>3-Band EQ&lt;/strong> effect (right-click near the track fader → Add Effect → EQ → 3 Band EQ).&lt;/li>
&lt;li>Right-click the &lt;strong>Low Gain&lt;/strong> knob on the EQ and choose &lt;em>Add Automation&lt;/em>. Then click &lt;strong>Add Curve&lt;/strong> and select &lt;strong>Distance&lt;/strong>.
&lt;ol>
&lt;li>You have now connected your Distance parameter to the low-frequency gain control on the EQ.&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>You’ll see a red automation curve appear under the track.&lt;/li>
&lt;li>Draw the curve so that as Distance increases, the low end fades out.&lt;/li>
&lt;/ol>
&lt;p>This simulates real-world behavior—low frequencies carry farther, so as the player gets closer, you regain that low-end presence.&lt;/p>
&lt;hr>
&lt;h3 id="4-create-the-custom-trigger-parameter">4. Create the Custom Trigger Parameter&lt;/h3>
&lt;p>Now we’ll add the parameter Unity will control.&lt;/p>
&lt;ol>
&lt;li>In the Parameters tab, click “+” → &lt;em>Add New Parameter&lt;/em>.&lt;/li>
&lt;li>Choose &lt;strong>Discrete&lt;/strong> type (whole-number values).&lt;/li>
&lt;li>Name it &lt;strong>Trigger&lt;/strong>.
&lt;ul>
&lt;li>Discrete values are ideal for toggling between on/off or state-based conditions.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Set its range:
&lt;ul>
&lt;li>&lt;strong>Min:&lt;/strong> 0&lt;/li>
&lt;li>&lt;strong>Max:&lt;/strong> 1&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>In your event:
&lt;ul>
&lt;li>Place the second sound (&lt;em>Teleporter Walk Through 01&lt;/em>) under the same track or on a new one.&lt;/li>
&lt;li>Go to the Loop region and open its &lt;strong>Trigger Conditions&lt;/strong> panel.&lt;/li>
&lt;li>Set it to &lt;strong>play when Trigger = 1&lt;/strong>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>For the looping hum track:
&lt;ul>
&lt;li>Open its &lt;strong>Condition Sheet&lt;/strong> and add a condition to &lt;strong>disable playback when Trigger = 1&lt;/strong>.&lt;/li>
&lt;li>Add a short fade-out automation for smooth transition.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>When Trigger switches from 0 to 1, the looping hum fades, and the teleport sound plays once.&lt;/p>
&lt;p>&lt;img src="https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/f-25-dad-424-sound-design-for-games/lectures/week-8/automating-audio-with-parameters-1/trigger-condition.png">&lt;/p>
&lt;hr>
&lt;h3 id="5-fine-tune-spatialization">5. Fine-Tune Spatialization&lt;/h3>
&lt;ol>
&lt;li>On the &lt;strong>Master Track&lt;/strong>, open the &lt;strong>Spatializer&lt;/strong> effect.&lt;/li>
&lt;li>Adjust &lt;strong>Min Distance&lt;/strong> and &lt;strong>Max Distance&lt;/strong> values so the attenuation curve fits your in-game scale (e.g., smaller values for tight interior scenes, larger for outdoor).&lt;/li>
&lt;li>Preview using the 3D Preview window in FMOD to confirm how the sound falls off with distance.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h3 id="6-save-and-build">6. Save and Build&lt;/h3>
&lt;p>Finally:&lt;/p>
&lt;ul>
&lt;li>Save the project.&lt;/li>
&lt;li>Assign your event to the &lt;strong>Master Bank&lt;/strong>.&lt;/li>
&lt;li>Build your banks (&lt;code>File → Build → Build Banks&lt;/code>).&lt;/li>
&lt;/ul>
&lt;p>You’re now ready to load this event in Unity.&lt;/p>
&lt;hr>
&lt;h3 id="7-test-in-fmod-before-unity">7. Test in FMOD Before Unity&lt;/h3>
&lt;p>Play your event in FMOD:&lt;/p>
&lt;ul>
&lt;li>Drag the &lt;strong>Distance&lt;/strong> slider to watch the EQ automation respond.&lt;/li>
&lt;li>Set &lt;strong>Trigger = 1&lt;/strong> to hear the teleport sound override the loop.&lt;/li>
&lt;/ul>
&lt;p>You should observe:&lt;/p>
&lt;ul>
&lt;li>The low-end fading out naturally as Distance increases.&lt;/li>
&lt;li>The teleport sound firing instantly when Trigger hits 1.&lt;/li>
&lt;li>The loop stopping cleanly when the parameter condition disables it.&lt;/li>
&lt;/ul>
&lt;p>This confirms everything is wired properly before moving to C# integration.&lt;/p>
&lt;hr>
&lt;h2 id="unity-scene-setup">Unity Scene Setup&lt;/h2>
&lt;ul>
&lt;li>Find the teleporter in the scene. Its the &lt;code>TransitionStart&lt;/code> game object.&lt;/li>
&lt;li>Make sure the teleporter has a &lt;strong>Box Collider&lt;/strong> (or whatever shape), set to &lt;em>Is Trigger&lt;/em>.&lt;/li>
&lt;li>Place your player (e.g. “Ellen”) in the scene. This will make our sounds easier to test.
&lt;ul>
&lt;li>Find Ellen and change her transform position to &lt;code>X: -145, Y: 21.3, Z: 43&lt;/code>.&lt;/li>
&lt;li>Alternatively, copy the position of the &lt;code>TransitionStart&lt;/code> object then paste it into Ellen’s transform, then adjust the X position by -5 to start just outside the trigger.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Disable or remove other objects so they won&amp;rsquo;t interfere during testing by unchecking their active state in the &lt;strong>Inspector&lt;/strong>.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="c-script-f_teleportercs">C# Script: &lt;code>F_Teleporter.cs&lt;/code>&lt;/h2>
&lt;p>Create a script under your &lt;code>Scripts/FMDO Scripts&lt;/code> folder named &lt;code>F_Teleporter.cs&lt;/code> and attach it to your teleporter object (the same object holding the trigger collider).
Here’s the full script:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> UnityEngine;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">F_Teleporter&lt;/span> : MonoBehaviour
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> FMOD.Studio.EventInstance tHum;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> Start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> tHum = FMODUnity.RuntimeManager.CreateInstance(&lt;span style="color:#e6db74">&amp;#34;event:/SFX/Interactable/Teleporter&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> FMODUnity.RuntimeManager.AttachInstanceToGameObject(tHum, gameObject, GetComponent&amp;lt;Rigidbody&amp;gt;());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> tHum.start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Update is called once per frame&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> OnTriggerEnter(Collider other)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Could also use other.CompareTag(&amp;#34;Player&amp;#34;) for more robust checking if the Player game object had the tag &amp;#34;player&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (other.name == &lt;span style="color:#e6db74">&amp;#34;Ellen&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> tHum.setParameterByName(&lt;span style="color:#e6db74">&amp;#34;Trigger&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>, &lt;span style="color:#66d9ef">false&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> OnDestroy()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> tHum.stop(FMOD.Studio.STOP_MODE.IMMEDIATE);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> tHum.release();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="key-points">Key Points&lt;/h3>
&lt;ul>
&lt;li>&lt;code>CreateInstance(path)&lt;/code> must match exactly the FMOD event path.&lt;/li>
&lt;li>&lt;code>AttachInstanceToGameObject(...)&lt;/code> aligns spatial 3D behavior (so position/distance is tracked).&lt;/li>
&lt;li>&lt;code>setParameterByName(&amp;quot;Trigger&amp;quot;, 1f, false)&lt;/code> changes your &lt;code>Trigger&lt;/code> parameter.
&lt;ul>
&lt;li>The &lt;code>false&lt;/code> flag means you don’t bypass seek speed — FMOD seeks based on your parameter settings.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>release()&lt;/code> frees memory and ensures no audio leaks after object destruction.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="testing--debugging">Testing &amp;amp; Debugging&lt;/h2>
&lt;ol>
&lt;li>Hit &lt;strong>Play&lt;/strong> in Unity.&lt;/li>
&lt;li>Move the listener close to the teleporter — you should hear the ambient loop with full low frequencies.&lt;/li>
&lt;li>As you back away, low frequencies fade (due to the Distance → EQ automation).&lt;/li>
&lt;li>Walk into the trigger: the teleport sound plays, and the loop stops or fades.&lt;/li>
&lt;/ol>
&lt;p>If things don’t work:&lt;/p>
&lt;ul>
&lt;li>Confirm the collider is set to &lt;em>Is Trigger&lt;/em>.&lt;/li>
&lt;li>Double-check your player object’s &lt;strong>name&lt;/strong> (or change the script to use a tag).&lt;/li>
&lt;li>Ensure the FMOD event path is correct and that the event’s bank is loaded.&lt;/li>
&lt;li>In FMOD Studio, verify your parameter mapping and that &lt;code>Trigger = 1&lt;/code> is correctly wired.&lt;/li>
&lt;/ul></content></item><item><title>Simplifying Sound Playback in Unity with FMOD’s PlayOneShot Command</title><link>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-8/using-playoneshot/</link><pubDate>Mon, 13 Oct 2025 00:00:00 +0000</pubDate><guid>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-8/using-playoneshot/</guid><description>Learn how to use FMOD’s PlayOneShot and PlayOneShotAttached commands in Unity to efficiently trigger one-off sound effects through colliders and triggers.</description><content>&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>In game audio, efficiency matters. When designing sound interactions in Unity using FMOD, there are many cases where you only need a sound to play once—like when a player picks up an item or activates a switch. Writing full event-handling code for each instance can quickly become cumbersome.&lt;/p>
&lt;p>That’s where FMOD’s &lt;strong>PlayOneShot&lt;/strong> command comes in. This function provides a fast, elegant way to trigger short, non-looping sound effects in your game world without manually managing event instances. In this guide, we’ll walk through how to use &lt;strong>PlayOneShot&lt;/strong> and &lt;strong>PlayOneShotAttached&lt;/strong>, explain how Unity’s trigger system ties in, and unpack key technical concepts like Vectors, Colliders, and Layers.&lt;/p>
&lt;hr>
&lt;h2 id="what-is-playoneshot">What Is PlayOneShot?&lt;/h2>
&lt;p>FMOD’s &lt;code>PlayOneShot&lt;/code> command allows you to play an FMOD event anywhere in your game with a single line of code. When the command is called, it automatically plays the event and then cleans up after itself when the sound finishes—no need to store or release the event manually.&lt;/p>
&lt;p>This makes it ideal for simple, one-off sound effects such as button clicks, weapon pickups, or door openings.&lt;/p>
&lt;p>For instance, if we want to trigger a short sound effect when a player picks up their melee weapon, &lt;code>PlayOneShot&lt;/code> lets us do it with minimal setup.&lt;/p>
&lt;hr>
&lt;h2 id="setting-up-the-event-in-fmod">Setting Up the Event in FMOD&lt;/h2>
&lt;ol>
&lt;li>&lt;strong>Create a new 3D Action event&lt;/strong> called &lt;code>Staff Collect&lt;/code> and add your audio file (for example, &lt;code>Switch Activated 01&lt;/code>).&lt;/li>
&lt;li>Assign the event to your &lt;strong>Master Bank&lt;/strong>.&lt;/li>
&lt;li>Organize it neatly under &lt;strong>SFX → Interactables&lt;/strong>.&lt;/li>
&lt;li>Save your project and &lt;strong>build your banks&lt;/strong>.&lt;/li>
&lt;/ol>
&lt;p>Once you’ve done that, switch back to Unity to implement the sound event.&lt;/p>
&lt;blockquote>
&lt;p>Action Events are perfect for short, non-looping sounds that play once and then stop. They are typically used for sound effects triggered by player actions or environmental events.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;img src="https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/f-25-dad-424-sound-design-for-games/lectures/week-8/using-playoneshot/image.png">&lt;/p>
&lt;hr>
&lt;h2 id="using-triggers-in-unity">Using Triggers in Unity&lt;/h2>
&lt;p>A &lt;strong>Trigger&lt;/strong> in Unity is an invisible area that detects when objects (like your player) enter or leave it. Triggers are built using colliders with the &lt;strong>Is Trigger&lt;/strong> box checked. When something passes through, Unity can perform specific actions—such as playing a sound.&lt;/p>
&lt;p>For our weapon pickup example:&lt;/p>
&lt;ul>
&lt;li>The &lt;code>StaffTakeTrigger&lt;/code> object in your scene will act as the trigger.&lt;/li>
&lt;li>It already has a &lt;strong>Sphere Collider&lt;/strong> attached with &lt;strong>Is Trigger&lt;/strong> enabled.&lt;/li>
&lt;li>When the player enters this area, Unity can detect the interaction and execute our FMOD command.&lt;/li>
&lt;/ul>
&lt;p>We’ll connect this trigger to our FMOD event using a custom script.&lt;/p>
&lt;hr>
&lt;h2 id="writing-the-trigger-script">Writing the Trigger Script&lt;/h2>
&lt;p>Go to your &lt;code>FMOD Scripts&lt;/code> folder and create a new C# script called &lt;code>F_StaffCollectTrigger&lt;/code> attach it to the &lt;code>StaffTakeTrigger&lt;/code> object.&lt;/p>
&lt;p>Delete the default &lt;code>Start&lt;/code> and &lt;code>Update&lt;/code> functions, as we won’t need them for this simple interaction.&lt;/p>
&lt;p>Inside the script, we’ll use the &lt;code>OnTriggerEnter&lt;/code> function. This Unity event function runs automatically when another object enters the trigger’s collider.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> UnityEngine;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">F_StaffCollectTrigger&lt;/span> : MonoBehaviour
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> OnTriggerEnter()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> FMODUnity.RuntimeManager.PlayOneShot(&lt;span style="color:#e6db74">&amp;#34;event:/SFX/Interactables/StaffCollect&amp;#34;&lt;/span>, transform.position);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>&lt;code>onTriggerExit&lt;/code> is another useful function that runs when an object leaves the trigger area. You can use it for sounds that should play when the player exits, like a door closing. &lt;code>onTriggerStay&lt;/code> runs continuously while an object remains within the trigger.&lt;/p>
&lt;/blockquote>
&lt;p>This is all it takes to trigger the &lt;em>Staff Collect&lt;/em> sound effect when the player enters the area.&lt;/p>
&lt;p>However, we can improve this setup by specifying &lt;em>where&lt;/em> in 3D space the sound should be played.&lt;/p>
&lt;hr>
&lt;h2 id="understanding-vectors-in-unity">Understanding Vectors in Unity&lt;/h2>
&lt;p>To position sounds accurately, we need to work with &lt;strong>Vectors&lt;/strong>—data types that store coordinates and direction.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Vector2&lt;/strong> holds &lt;em>x&lt;/em> and &lt;em>y&lt;/em> coordinates (useful for 2D games).&lt;/li>
&lt;li>&lt;strong>Vector3&lt;/strong> holds &lt;em>x&lt;/em>, &lt;em>y&lt;/em>, and &lt;em>z&lt;/em> coordinates (used in 3D games).&lt;/li>
&lt;/ul>
&lt;p>In Unity, almost every object’s position, rotation, and scale are stored as Vector3 values. To access the current position of your object, you can reference its &lt;strong>Transform&lt;/strong> component.&lt;/p>
&lt;blockquote>
&lt;p>Here, &lt;code>transform.position&lt;/code> retrieves the Vector3 position of the object this script is attached to (the trigger). This is shorthand for &lt;code>GetComponent&amp;lt;Transform&amp;gt;().position&lt;/code>.&lt;/p>
&lt;/blockquote>
&lt;p>If you want to play the sound at a different location but related to the trigger, you can create a new Vector3:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>Vector3 pos = transform.position + Vector3.up * &lt;span style="color:#ae81ff">1.5f&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>FMODUnity.RuntimeManager.PlayOneShot(&lt;span style="color:#e6db74">&amp;#34;event:/SFX/Interactables/StaffCollect&amp;#34;&lt;/span>, pos);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This example plays the sound 1.5 units above the trigger’s position.&lt;/p>
&lt;hr>
&lt;h2 id="layers-and-collision-setup">Layers and Collision Setup&lt;/h2>
&lt;p>For the trigger to work correctly, Unity needs to detect when the player enters the trigger area. This requires proper setup of &lt;strong>Layers&lt;/strong> and &lt;strong>Colliders&lt;/strong>.&lt;/p>
&lt;ol>
&lt;li>The object acting as the trigger must have a collider with &lt;strong>Is Trigger&lt;/strong> checked.&lt;/li>
&lt;li>The player object must either:
&lt;ul>
&lt;li>Have a &lt;strong>Rigidbody&lt;/strong> component, or&lt;/li>
&lt;li>Be on a layer compatible with the trigger’s layer.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>You can manage these interactions in Unity’s &lt;strong>Layer Collision Matrix&lt;/strong> under &lt;em>Edit → Project Settings → Physics&lt;/em>.
Ensure that the &lt;strong>Player&lt;/strong> layer can interact with the &lt;strong>Environment&lt;/strong> layer (or whichever layer your trigger belongs to).&lt;/p>
&lt;blockquote>
&lt;p>Tags and Layers help organize objects in your scene and control how they interact. Tags are used for identifying objects, while Layers are used for collision detection and rendering.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;img src="https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/f-25-dad-424-sound-design-for-games/lectures/week-8/using-playoneshot/physics-layers.png">&lt;/p>
&lt;p>Here you can see the Layer Collision Matrix in Unity, which defines how different layers interact with each other. See that the &lt;strong>Player&lt;/strong> layer can collide with the &lt;strong>Environment&lt;/strong> layer, allowing our trigger to detect when the player enters.&lt;/p>
&lt;hr>
&lt;h2 id="using-playoneshotattached">Using PlayOneShotAttached&lt;/h2>
&lt;p>While &lt;code>PlayOneShot&lt;/code> requires a position vector, FMOD also offers a variant called &lt;code>PlayOneShotAttached&lt;/code>. This version automatically plays an event at the location of a specific game object.&lt;/p>
&lt;p>Here’s how to use it:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> OnTriggerEnter()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> FMODUnity.RuntimeManager.PlayOneShotAttached(&lt;span style="color:#e6db74">&amp;#34;event:/SFX/Interactables/StaffCollect&amp;#34;&lt;/span>, gameObject);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;code>gameObject&lt;/code> reference points to the object this script is attached to—in this case, &lt;code>StaffTakeTrigger&lt;/code>.
This makes it an elegant alternative when you want to link the sound directly to an object’s position.&lt;/p>
&lt;p>If you want to attach the sound to another object in the scene, you can define a &lt;strong>public GameObject variable&lt;/strong> in your script and assign it through the Unity Inspector:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> GameObject Obj;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> OnTriggerEnter()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> FMODUnity.RuntimeManager.PlayOneShotAttached(&lt;span style="color:#e6db74">&amp;#34;event:/SFX/Interactables/StaffCollect&amp;#34;&lt;/span>, Obj);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This approach provides flexibility for triggering sounds from different points in your game world.&lt;/p>
&lt;hr>
&lt;h2 id="limitations-and-next-steps">Limitations and Next Steps&lt;/h2>
&lt;p>While &lt;code>PlayOneShot&lt;/code> and &lt;code>PlayOneShotAttached&lt;/code> are great for quick, lightweight sound triggers, they come with a limitation:
you can’t manipulate FMOD parameters after triggering them.&lt;/p>
&lt;p>For cases where you need to control parameters dynamically (for instance, changing pitch or volume in response to player movement), you’ll need to use full event instances instead.&lt;/p>
&lt;p>We’ll explore this in the next module: &lt;strong>Interacting with FMOD Parameters&lt;/strong>—where you’ll learn how to adjust event behavior in real time for adaptive sound design.&lt;/p>
&lt;hr>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>The &lt;code>PlayOneShot&lt;/code> and &lt;code>PlayOneShotAttached&lt;/code> commands streamline the process of triggering short, non-looping sounds in Unity. Combined with Unity’s trigger system and a solid understanding of Vectors and Layers, they allow you to quickly prototype interactive sound behaviors.&lt;/p></content></item><item><title>Challenge 3: Health Crate Audio</title><link>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/assignments/exercises/challenge-health-crate-audio/</link><pubDate>Mon, 04 Nov 2024 00:00:00 +0000</pubDate><guid>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/assignments/exercises/challenge-health-crate-audio/</guid><description>Overview In this challenge, you’ll design and implement the audio behavior for the Health Crate in Unity using FMOD. The crate should emit a looping hum before interaction and trigger a one-shot burst when the player collects health.
Objectives Integrate FMOD events with Unity using correct namespaces and instance management. Implement 3D spatialization so the audio responds to distance. Use parameters to automate playback and control transitions. Apply event lifecycle management to avoid leaks and redundant instances.</description><content>&lt;h3 id="overview">Overview&lt;/h3>
&lt;p>In this challenge, you’ll design and implement the &lt;strong>audio behavior&lt;/strong> for the Health Crate in Unity using FMOD.
The crate should emit a &lt;strong>looping hum&lt;/strong> before interaction and trigger a &lt;strong>one-shot burst&lt;/strong> when the player collects health.&lt;/p>
&lt;hr>
&lt;h3 id="objectives">Objectives&lt;/h3>
&lt;ul>
&lt;li>Integrate FMOD events with Unity using correct namespaces and instance management.&lt;/li>
&lt;li>Implement 3D spatialization so the audio responds to distance.&lt;/li>
&lt;li>Use parameters to automate playback and control transitions.&lt;/li>
&lt;li>Apply event lifecycle management to avoid leaks and redundant instances.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="criteria-12-points-total">Criteria (12 Points Total)&lt;/h3>
&lt;p>Follow the criteria below to complete the challenge:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Criteria&lt;/th>
&lt;th>Points&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>All sounds are 3D and get louder as the player approaches&lt;/td>
&lt;td>3&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>The looping hum stops once the player collects health&lt;/td>
&lt;td>3&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Instances are properly destroyed when no longer needed&lt;/td>
&lt;td>2&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>FMOD namespaces are used to shorten your code&lt;/td>
&lt;td>2&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>The one-shot triggers only when the player interacts with the health crate&lt;/td>
&lt;td>2&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Bonus:&lt;/strong> Add a delay to the one-shot that increases as the player’s health decreases&lt;/td>
&lt;td>&lt;strong>3&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h3 id="hints--resources">Hints &amp;amp; Resources&lt;/h3>
&lt;ul>
&lt;li>Audio files are in your FMOD project under:
&lt;strong>Audio Bin → Interactables → Health Boxes&lt;/strong>
&lt;ul>
&lt;li>&lt;em>Box Loop&lt;/em> (for the hum)&lt;/li>
&lt;li>&lt;em>Box Open&lt;/em> (for the one-shot)&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Lessons that may help:
&lt;ul>
&lt;li>&lt;em>Using Play-One-Shot’s&lt;/em>&lt;/li>
&lt;li>&lt;em>Automating Audio with Parameters: Part 1 &amp;amp; 2&lt;/em>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>To find the player’s health, open the &lt;strong>Damageable&lt;/strong> script on the &lt;strong>Ellen&lt;/strong> GameObject.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="deliverables">Deliverables&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Short video (1–2 minutes):&lt;/strong>
Demonstrate the crate’s audio behavior in-game.
&lt;ul>
&lt;li>Show the 3D hum changing with distance.&lt;/li>
&lt;li>Interact with the crate to trigger the one-shot.&lt;/li>
&lt;li>(Optional bonus) Demonstrate the delay effect at different health levels.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="reflection-prompt">Reflection Prompt&lt;/h3>
&lt;p>Write 100–150 words explaining:&lt;/p>
&lt;ul>
&lt;li>How you structured your FMOD event.&lt;/li>
&lt;li>How you managed start/stop logic in Unity.&lt;/li>
&lt;li>(If you completed the bonus) How your delay changes with player health.&lt;/li>
&lt;/ul></content></item><item><title>Controlling Audio From Multiple Scripts</title><link>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-11/controlling-audio-multiple-scripts/</link><pubDate>Mon, 04 Nov 2024 00:00:00 +0000</pubDate><guid>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-11/controlling-audio-multiple-scripts/</guid><description>Overview A hands-on guide to managing one FMOD event from different places in your Unity game. By the end, you’ll be able to start music in one script and safely stop or change it from another, without duplicate sounds or messy references.
Learning goals Understand when to use instance (non-static) vs class-wide (static) variables Create and manage a single FMOD EventInstance for music Trigger fades and parameter changes from other scripts Avoid common pitfalls like invalid handles and race conditions What you’ll build A MusicScript that starts a looping music event A trigger script that fades the music when the player enters a zone An optional UI control to change a music parameter (e.</description><content>&lt;h1 id="overview">Overview&lt;/h1>
&lt;p>A hands-on guide to managing one FMOD event from different places in your Unity game. By the end, you’ll be able to start music in one script and safely stop or change it from another, without duplicate sounds or messy references.&lt;/p>
&lt;h2 id="learning-goals">Learning goals&lt;/h2>
&lt;ul>
&lt;li>Understand when to use instance (non-static) vs class-wide (static) variables&lt;/li>
&lt;li>Create and manage a single FMOD EventInstance for music&lt;/li>
&lt;li>Trigger fades and parameter changes from other scripts&lt;/li>
&lt;li>Avoid common pitfalls like invalid handles and race conditions&lt;/li>
&lt;/ul>
&lt;h2 id="what-youll-build">What you’ll build&lt;/h2>
&lt;ul>
&lt;li>A MusicScript that starts a looping music event&lt;/li>
&lt;li>A trigger script that fades the music when the player enters a zone&lt;/li>
&lt;li>An optional UI control to change a music parameter (e.g., intensity or low-pass)&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h1 id="static-vs-non-static-variables-refresher">Static Vs Non-Static Variables Refresher&lt;/h1>
&lt;p>Here’s a simple example to clarify the difference between &lt;strong>non-static&lt;/strong> and &lt;strong>static&lt;/strong> data types in C# — especially in a Unity scripting context.&lt;/p>
&lt;h3 id="example-player-score">Example: Player Score&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> UnityEngine;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Player&lt;/span> : MonoBehaviour
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> playerScore = &lt;span style="color:#ae81ff">0&lt;/span>; &lt;span style="color:#75715e">// Non-static (instance-based)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> totalScore = &lt;span style="color:#ae81ff">0&lt;/span>; &lt;span style="color:#75715e">// Static (shared across all players)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> Start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> playerScore = Random.Range(&lt;span style="color:#ae81ff">1&lt;/span>, &lt;span style="color:#ae81ff">10&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> totalScore += playerScore;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="how-this-works">How this works&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Non-static (playerScore)&lt;/strong>
Each Player object keeps track of its own score.
If you have 3 Player GameObjects, each will have a different &lt;code>playerScore&lt;/code> value.&lt;/li>
&lt;li>&lt;strong>Static (totalScore)&lt;/strong>
This value is shared by all Player objects. Every time a new Player adds its score, &lt;code>totalScore&lt;/code> updates globally.
You can access it from anywhere with &lt;code>Player.totalScore&lt;/code>—no need to reference a specific Player instance.&lt;/li>
&lt;/ul>
&lt;h3 id="why-it-matters">Why it matters&lt;/h3>
&lt;ul>
&lt;li>Use &lt;strong>non-static&lt;/strong> when each instance should have its own data.
Example: individual enemies with their own health or position.&lt;/li>
&lt;li>Use &lt;strong>static&lt;/strong> when there should be only one value shared across all instances.
Example: total score, global music controller, or global game state flag.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h1 id="approach-1-instance-based-control-non-static">Approach 1: Instance-based control (non-static)&lt;/h1>
&lt;p>Use this when the music belongs to a specific GameObject in the scene and you want to wire references explicitly in the Inspector.&lt;/p>
&lt;h2 id="musicplayercs">MusicPlayer.cs&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> UnityEngine;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> FMODUnity;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">MusicPlayer&lt;/span> : MonoBehaviour
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e"> [SerializeField]&lt;/span> &lt;span style="color:#66d9ef">private&lt;/span> EventReference musicEvent;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">public&lt;/span> FMOD.Studio.EventInstance MusicInst;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> Start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MusicInst = RuntimeManager.CreateInstance(musicEvent);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MusicInst.start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> OnDestroy()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MusicInst.stop(FMOD.Studio.STOP_MODE.ALLOWFADEOUT);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MusicInst.release();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="f_teleportercs-fade-music-on-trigger">F_Teleporter.cs (fade music on trigger)&lt;/h2>
&lt;p>There are two ways to reference our &lt;code>MusicPlayer&lt;/code> here. One is to use Unity&amp;rsquo;s find methods to search for it in the scene; the other is to create a serialized field and drag the reference in the Inspector. The latter is preferred for performance and clarity.&lt;/p>
&lt;p>Method 1: Using a find method (not recommended for performance reasons)&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// Example only — prefer the serialized field approach below&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// var musicPlayer = GameObject.Find(&amp;#34;Music Object&amp;#34;).GetComponent&amp;lt;MusicPlayer&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// or&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// var musicPlayer = FindObjectOfType&amp;lt;MusicPlayer&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Method 2: Using a serialized field (preferred)&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> UnityEngine;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">F_Teleporter&lt;/span> : MonoBehaviour
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e"> [SerializeField]&lt;/span> &lt;span style="color:#66d9ef">private&lt;/span> MusicPlayer musicPlayer; &lt;span style="color:#75715e">// drag your MusicPlayer object here&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> OnTriggerEnter(Collider other)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (!other.CompareTag(&lt;span style="color:#e6db74">&amp;#34;Player&amp;#34;&lt;/span>)) &lt;span style="color:#66d9ef">return&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Check if the music instance is valid before stopping&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (musicPlayer != &lt;span style="color:#66d9ef">null&lt;/span> &amp;amp;&amp;amp; musicPlayer.MusicInst.isValid())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> musicPlayer.MusicInst.stop(FMOD.Studio.STOP_MODE.ALLOWFADEOUT);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Why choose this: it’s explicit and great for scenes with multiple independent music or ambience controllers. You control exactly which instance you’re touching by wiring it in the Inspector.&lt;/p>
&lt;p>To finish, select the GameObject with the &lt;code>F_Teleporter&lt;/code> component and drag the GameObject with the &lt;code>MusicPlayer&lt;/code> script into the &lt;code>Music Player&lt;/code> field in the Inspector.&lt;/p>
&lt;hr>
&lt;h1 id="approach-2-class-wide-control-static">Approach 2: Class-wide control (static)&lt;/h1>
&lt;p>Use this when there’s exactly one music instance for the whole scene or game and you want to access it from anywhere without passing references.&lt;/p>
&lt;h2 id="musicscriptcs">MusicScript.cs&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> UnityEngine;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> FMODUnity;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">MusicScript&lt;/span> : MonoBehaviour
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e"> [SerializeField]&lt;/span> &lt;span style="color:#66d9ef">private&lt;/span> EventReference musicEvent;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> FMOD.Studio.EventInstance MusicInst;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> Start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (!MusicInst.isValid())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MusicInst = RuntimeManager.CreateInstance(musicEvent);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MusicInst.start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> OnDestroy()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (MusicInst.isValid())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MusicInst.stop(FMOD.Studio.STOP_MODE.ALLOWFADEOUT);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MusicInst.release();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> SetMusicParam(&lt;span style="color:#66d9ef">string&lt;/span> name, &lt;span style="color:#66d9ef">float&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (MusicInst.isValid())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MusicInst.setParameterByName(name, &lt;span style="color:#66d9ef">value&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="f_teleportercs-fade-music-on-trigger-1">F_Teleporter.cs (fade music on trigger)&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> UnityEngine;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">F_Teleporter&lt;/span> : MonoBehaviour
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> OnTriggerEnter(Collider other)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (!other.CompareTag(&lt;span style="color:#e6db74">&amp;#34;Player&amp;#34;&lt;/span>)) &lt;span style="color:#66d9ef">return&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (MusicScript.MusicInst.isValid())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> MusicScript.MusicInst.stop(FMOD.Studio.STOP_MODE.ALLOWFADEOUT);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Why choose this: it’s quick and convenient when you truly have a single, global music state. Anywhere in code can reach it via the class name.&lt;/p>
&lt;hr>
&lt;h1 id="when-to-use-which">When to use which&lt;/h1>
&lt;p>Use instance-based if:&lt;/p>
&lt;ul>
&lt;li>You have multiple music or ambience controllers&lt;/li>
&lt;li>You want explicit Inspector wiring and easy prefab testing&lt;/li>
&lt;/ul>
&lt;p>Use static if:&lt;/p>
&lt;ul>
&lt;li>There is only one music instance&lt;/li>
&lt;li>Many systems need to access it without passing references&lt;/li>
&lt;li>You’re building a lightweight global music controller&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h1 id="troubleshooting-checklist">Troubleshooting checklist&lt;/h1>
&lt;ul>
&lt;li>Does your Player have the Player tag and a collider/rigidbody appropriate for trigger events?&lt;/li>
&lt;li>Is the FMOD event assigned in the Inspector and valid in FMOD Studio?&lt;/li>
&lt;li>Are you calling stop on a valid instance? Check MusicInst.isValid() before using it.&lt;/li>
&lt;li>Are you starting the event before trying to stop it? Initialize in Start and verify logs.&lt;/li>
&lt;li>For static mode, do you accidentally create multiple MusicScripts? Guard with isValid().&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h1 id="clean-up-and-lifecycle-tips">Clean-up and lifecycle tips&lt;/h1>
&lt;ul>
&lt;li>stop ends playback; release frees the handle. After stopping with ALLOWFADEOUT, release the instance when you’re truly done with it.&lt;/li>
&lt;li>If music should persist across scenes, mark the music GameObject with DontDestroyOnLoad and manage transitions centrally.&lt;/li>
&lt;li>Avoid GameObject.Find. Prefer serialized fields (instance approach) or the class name (static approach) for clarity and performance.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h1 id="quick-reference">Quick reference&lt;/h1>
&lt;ul>
&lt;li>Instance-based access: keep MusicInst non-static and pass references via the Inspector.&lt;/li>
&lt;li>Static access: make MusicInst static and reach it from anywhere with MusicScript.MusicInst.&lt;/li>
&lt;li>Always check isValid before using an EventInstance.&lt;/li>
&lt;li>Stop with ALLOWFADEOUT, then release when finished.&lt;/li>
&lt;/ul></content></item><item><title>Controlling FMOD Busses with Unity Sliders (Part 2)</title><link>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-13/controlling-fmod-busses-2/</link><pubDate>Wed, 12 Jun 2024 00:00:00 +0000</pubDate><guid>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-13/controlling-fmod-busses-2/</guid><description>In this tutorial, we will cover how to control the volume of your FMOD buses (like Music and SFX) using Unity sliders, and more importantly, how to make sure those sliders remember their positions when your player changes scenes.
Part 1: Setting the Volume The Code In your Bus Control script, create a new public function. Let’s call it VolumeLevel. This function needs to accept a float parameter, which will represent the data coming from the slider.</description><content>&lt;p>In this tutorial, we will cover how to control the volume of your FMOD buses (like Music and SFX) using Unity sliders, and more importantly, how to make sure those sliders remember their positions when your player changes scenes.&lt;/p>
&lt;h2 id="part-1-setting-the-volume">Part 1: Setting the Volume&lt;/h2>
&lt;h3 id="the-code">The Code&lt;/h3>
&lt;p>In your Bus Control script, create a new public function. Let’s call it &lt;code>VolumeLevel&lt;/code>. This function needs to accept a &lt;code>float&lt;/code> parameter, which will represent the data coming from the slider.&lt;/p>
&lt;p>Inside the function, we use the FMOD API command &lt;code>.setVolume()&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> VolumeLevel(&lt;span style="color:#66d9ef">float&lt;/span> newSliderValue)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// &amp;#39;Bus&amp;#39; is the variable where you stored your FMOD Bus path earlier&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Bus.setVolume(newSliderValue);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="understanding-fmod-volume-floats">Understanding FMOD Volume Floats&lt;/h3>
&lt;p>It is important to understand how FMOD interprets these numbers. FMOD faders use a logarithmic scale (decibels), but the API uses a linear float scale between &lt;strong>0&lt;/strong> and &lt;strong>1&lt;/strong>.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>1.0f:&lt;/strong> This equals the volume the fader was set to when you built your banks in FMOD. (If your fader was at 0dB, 1.0 = 0dB. If it was at -10dB, 1.0 = -10dB).&lt;/li>
&lt;li>&lt;strong>0.0f:&lt;/strong> This equals -infinity dB (silence).&lt;/li>
&lt;li>&lt;strong>0.5f:&lt;/strong> This is roughly -28dB (depending on the curve), effectively half the &amp;ldquo;fader distance&amp;rdquo; between the start point and silence.&lt;/li>
&lt;/ul>
&lt;h2 id="part-2-connecting-the-ui-slider">Part 2: Connecting the UI Slider&lt;/h2>
&lt;p>Now that the logic exists, we need to hook it up to the Unity UI.&lt;/p>
&lt;ol>
&lt;li>Select your &lt;strong>Slider&lt;/strong> object in the Unity Hierarchy.&lt;/li>
&lt;li>Find the &lt;strong>Slider&lt;/strong> component in the Inspector.&lt;/li>
&lt;li>Look for the &lt;strong>&amp;ldquo;On Value Changed (Single)&amp;rdquo;&lt;/strong> event box at the bottom.&lt;/li>
&lt;li>Click the &lt;strong>(+)&lt;/strong> icon to add a listener.&lt;/li>
&lt;li>Drag the GameObject containing your Bus Control script into the &amp;ldquo;Object&amp;rdquo; slot.&lt;/li>
&lt;li>In the function dropdown, look for your script name and select &lt;code>VolumeLevel&lt;/code>.&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>Crucial Step:&lt;/strong> When selecting the function, you will see it listed twice: once under &amp;ldquo;Dynamic float&amp;rdquo; and once under &amp;ldquo;Static Parameters.&amp;rdquo;&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Select the Dynamic Float version.&lt;/strong> This ensures the function receives the &lt;em>actual&lt;/em> current value of the slider as the player moves it.&lt;/li>
&lt;/ul>
&lt;h2 id="part-3-the-scene-reset-problem">Part 3: The &amp;ldquo;Scene Reset&amp;rdquo; Problem&lt;/h2>
&lt;p>If you play your game now, the sliders will work. However, you will notice a bug if you switch scenes.&lt;/p>
&lt;p>Let&amp;rsquo;s say the player lowers the volume to 50%. When they walk through a portal to Level 2, the scene reloads. FMOD is smart—it remembers the volume is at 50%. However, the &lt;strong>UI Slider&lt;/strong> is just a dumb object; it reloads from the prefab and resets visually to its default position (usually 100% or 0.9).&lt;/p>
&lt;p>Now you have a slider showing 100% volume, but the audio is playing at 50%. We need to sync them up when the scene starts.&lt;/p>
&lt;h2 id="part-4-syncing-sliders-with-getvolume">Part 4: syncing Sliders with &lt;code>.getVolume()&lt;/code>&lt;/h2>
&lt;p>To fix this, we need to tell the slider where the bus volume is actually located the moment the script starts. We do this using the &lt;code>.getVolume()&lt;/code> command.&lt;/p>
&lt;h3 id="the-challenge-with-getvolume">The Challenge with &lt;code>.getVolume()&lt;/code>&lt;/h3>
&lt;p>This command is slightly unique because it outputs &lt;strong>two&lt;/strong> distinct values. It requires two &lt;code>out&lt;/code> variables:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Volume:&lt;/strong> The value set by the &lt;code>setVolume&lt;/code> command (User input).&lt;/li>
&lt;li>&lt;strong>Final Volume:&lt;/strong> The value calculated after combining user input with FMOD modulators or automation.&lt;/li>
&lt;/ol>
&lt;p>We only care about the first one (User input).&lt;/p>
&lt;h3 id="the-solution">The Solution&lt;/h3>
&lt;p>First, update your script to include the UI namespace, or Unity won&amp;rsquo;t recognize what a &amp;ldquo;Slider&amp;rdquo; is.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> UnityEngine;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> UnityEngine.UI; &lt;span style="color:#75715e">// Don&amp;#39;t forget this!&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Next, inside your class, create a reference to the Slider and variables to hold the volume data.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">private&lt;/span> Slider slider;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">float&lt;/span> busVolume;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">float&lt;/span> finalBusVolume;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Finally, in your &lt;code>Start()&lt;/code> function, grab the slider component and sync it to the FMOD bus:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> Start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// 1. Get the slider component attached to this object&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> slider = GetComponent&amp;lt;Slider&amp;gt;();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// 2. Get the current volume from the FMOD Bus&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// We pass in our two float variables to catch the data&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Bus.getVolume(&lt;span style="color:#66d9ef">out&lt;/span> busVolume, &lt;span style="color:#66d9ef">out&lt;/span> finalBusVolume);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// 3. Update the slider visual to match the FMOD bus volume&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> slider.&lt;span style="color:#66d9ef">value&lt;/span> = busVolume;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>By using &lt;code>setVolume&lt;/code> dynamically and &lt;code>getVolume&lt;/code> on initialization, you create a seamless experience for the player.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>On Start:&lt;/strong> The script checks FMOD to see what the volume &lt;em>should&lt;/em> be and updates the slider visual.&lt;/li>
&lt;li>&lt;strong>During Gameplay:&lt;/strong> The slider updates the FMOD bus using the Dynamic Float event.&lt;/li>
&lt;li>&lt;strong>On Scene Change:&lt;/strong> Even though the UI destroys and reloads, the &lt;code>Start&lt;/code> function runs again, grabs the persistent FMOD volume, and snaps the slider back to the correct position.&lt;/li>
&lt;/ol>
&lt;p>Now your audio settings menu is robust and persistent across your entire game!&lt;/p></content></item><item><title>Creating Audio Zones and Dynamic Mixing with Snapshots</title><link>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-14/snapshots/</link><pubDate>Wed, 12 Jun 2024 00:00:00 +0000</pubDate><guid>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-14/snapshots/</guid><description>In our previous tutorial, we mastered Playback States to control when sounds play. Now, we’re going to control how they sound based on the game environment.
Have you ever noticed how audio changes when a character walks into a cave (reverb kicks in) or when they are low on health (the sound gets muffled and bass-heavy)? In FMOD, these dynamic mixing changes are handled by Snapshots.
In this guide, we will cover:</description><content>&lt;p>In our previous tutorial, we mastered &lt;strong>Playback States&lt;/strong> to control &lt;em>when&lt;/em> sounds play. Now, we’re going to control &lt;em>how&lt;/em> they sound based on the game environment.&lt;/p>
&lt;p>Have you ever noticed how audio changes when a character walks into a cave (reverb kicks in) or when they are low on health (the sound gets muffled and bass-heavy)? In FMOD, these dynamic mixing changes are handled by &lt;strong>Snapshots&lt;/strong>.&lt;/p>
&lt;p>In this guide, we will cover:&lt;/p>
&lt;ol>
&lt;li>What Snapshots are and how to create them.&lt;/li>
&lt;li>Creating a &lt;strong>Reverb Zone&lt;/strong> using Triggers.&lt;/li>
&lt;li>Handling signal routing (so your UI clicks don&amp;rsquo;t echo!).&lt;/li>
&lt;li>Creating a &lt;strong>&amp;ldquo;Low Health&amp;rdquo; filter&lt;/strong> using Parameters.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="what-is-a-snapshot">What is a Snapshot?&lt;/h2>
&lt;p>Think of a Snapshot as a &amp;ldquo;preset&amp;rdquo; for your FMOD Mixer. When active, a Snapshot can change the volume of buses, toggle effects on and off, or change effect parameters.&lt;/p>
&lt;p>There are two main types:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Overriding:&lt;/strong> Forces values to a specific state (good for absolute changes).&lt;/li>
&lt;li>&lt;strong>Blending:&lt;/strong> Adds changes on top of existing values (good for layering).&lt;/li>
&lt;/ul>
&lt;p>For this tutorial, we will focus on &lt;strong>Overriding&lt;/strong> snapshots to create distinct audio zones.&lt;/p>
&lt;hr>
&lt;h2 id="part-1-the-reverb-zone-trigger-based">Part 1: The Reverb Zone (Trigger-Based)&lt;/h2>
&lt;p>Let’s create a system where entering a tunnel applies reverb to our sound effects.&lt;/p>
&lt;h3 id="step-1-fmod-setup">Step 1: FMOD Setup&lt;/h3>
&lt;ol>
&lt;li>Go to the &lt;strong>Mixer&lt;/strong> window in FMOD.&lt;/li>
&lt;li>Create a Return Bus for Reverb if you haven&amp;rsquo;t already, and add a Reverb effect to it.&lt;/li>
&lt;li>Go to the &lt;strong>Snapshots&lt;/strong> tab and create a new &lt;strong>Overriding Snapshot&lt;/strong> named &amp;ldquo;Reverb Zone 1&amp;rdquo;.&lt;/li>
&lt;li>Deselect your snapshot for the time being.&lt;/li>
&lt;li>Select the SFX Bus and add a &lt;strong>Send&lt;/strong> to the Reverb Return Bus. Set the send level to around -6dB (adjust to taste).&lt;/li>
&lt;li>&lt;strong>Scoping:&lt;/strong> When you select the snapshot, your mixer faders will turn into yellow outlines. This means they are &amp;ldquo;scoped out&amp;rdquo; (unaffected).
&lt;ol>
&lt;li>Select the snapshot again. Mixer controls will appear with yellow outlines, meaning they are currently scoped out.&lt;/li>
&lt;li>Click the reverb send level on the SFX Bus so it becomes fully yellow, indicating it is scoped in and will be driven by the snapshot.&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>When you select the snapshot, the reverb send will be set to the value defined in the snapshot.&lt;/li>
&lt;li>&lt;strong>Modulation:&lt;/strong> To make the reverb fade in smoothly, right-click the Snapshot&amp;rsquo;s &lt;strong>Intensity&lt;/strong> dial (in the deck area) and add an &lt;strong>AHDSR Modulator&lt;/strong>. This ensures the reverb doesn&amp;rsquo;t snap on/off instantly.&lt;/li>
&lt;/ol>
&lt;h3 id="step-2-the-unity-script">Step 2: The Unity Script&lt;/h3>
&lt;p>Open Level 2 in Unity. This scene has more tunnels that we can use for testing reverb.&lt;/p>
&lt;p>In Unity, Snapshots are treated almost exactly like Events. We create an instance, play it, and stop it.&lt;/p>
&lt;p>Create a cube object with Create &amp;gt; 3D Object &amp;gt; Cube. Scale it to form a tunnel entrance and set its position so the player can walk through it. Make sure to set the cube&amp;rsquo;s collider to &lt;strong>Is Trigger&lt;/strong>. Call it &lt;code>Reverb Zone&lt;/code>. Also make sure the layer is set to &lt;code>Environment&lt;/code>.&lt;/p>
&lt;p>We&amp;rsquo;ll leave the &lt;code>Mesh Renderer&lt;/code> on so you can see the zone in the scene, but in a real game, you would likely turn this off.&lt;/p>
&lt;p>Create a script called &lt;code>F_ReverbZone&lt;/code> and attach it to the &lt;code>Reverb Zone&lt;/code> object.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> UnityEngine;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> FMODUnity;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> FMOD.Studio;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">ReverbZone&lt;/span> : MonoBehaviour
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">private&lt;/span> EventInstance reverbSnapshot;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> Start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Create the instance just like a normal event&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Note: The path starts with &amp;#34;snapshot:/&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reverbSnapshot = RuntimeManager.CreateInstance(&lt;span style="color:#e6db74">&amp;#34;snapshot:/Reverb Zone&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> OnTriggerEnter(Collider other)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (other.CompareTag(&lt;span style="color:#e6db74">&amp;#34;Player&amp;#34;&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reverbSnapshot.start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> OnTriggerExit(Collider other)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (other.CompareTag(&lt;span style="color:#e6db74">&amp;#34;Player&amp;#34;&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Allow the AHDSR to fade it out naturally&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reverbSnapshot.stop(STOP_MODE.ALLOWFADEOUT);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> OnDestroy()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reverbSnapshot.release();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="step-3-fixing-the-signal-path-the-ui-echo-problem">Step 3: Fixing the Signal Path (The UI Echo Problem)&lt;/h3>
&lt;p>If you test the game now, entering the zone will reverb &lt;em>everything&lt;/em> sent to the SFX bus—including your UI menu clicks! That’s not what we want. We need to split the signal.&lt;/p>
&lt;p>&lt;strong>In the FMOD Mixer:&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>Delete the send that goes from the &lt;strong>SFX&lt;/strong> bus to the &lt;strong>Reverb&lt;/strong> bus.&lt;/li>
&lt;li>Create two new Group Buses under your main &lt;strong>SFX&lt;/strong> bus: call them &lt;strong>In-Game&lt;/strong> and &lt;strong>UI&lt;/strong>.&lt;/li>
&lt;li>Route your footsteps, attacks, and environmental sounds into &lt;strong>In-Game&lt;/strong>.&lt;/li>
&lt;li>Route your menu sounds into &lt;strong>UI&lt;/strong>.&lt;/li>
&lt;li>Place the &lt;strong>Reverb Send&lt;/strong> on the &lt;strong>In-Game&lt;/strong> bus only.&lt;/li>
&lt;/ol>
&lt;p>Now, your UI remains crisp and dry, while the game world echoes around you.&lt;/p>
&lt;hr>
&lt;h2 id="part-2-the-distance-based-zone-automation">Part 2: The Distance-Based Zone (Automation)&lt;/h2>
&lt;p>Sometimes you don&amp;rsquo;t want a hard trigger; you want the effect to get stronger as you get closer to a specific object.&lt;/p>
&lt;h3 id="fmod-setup">FMOD Setup&lt;/h3>
&lt;ol>
&lt;li>Create a new snapshot (e.g., &amp;ldquo;Reverb Zone 2&amp;rdquo;).&lt;/li>
&lt;li>Instead of an AHDSR on the &lt;strong>Intensity&lt;/strong> dial, right-click and &lt;strong>Add Automation&lt;/strong>.&lt;/li>
&lt;li>Select the built-in &lt;strong>Distance&lt;/strong> parameter.&lt;/li>
&lt;li>Draw a curve:
&lt;ul>
&lt;li>At 5 meters, Intensity = 100%&lt;/li>
&lt;li>At 10 meters, Intensity = 0%&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h3 id="unity-script">Unity Script&lt;/h3>
&lt;p>Since this relies on distance, we must tell FMOD where the snapshot is located in 3D space.&lt;/p>
&lt;p>Create an Empty GameObject called &lt;code>Reverb Zone 2&lt;/code> and position at (-1, -3, 28.5). Create a new script and attach to the gameObject:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> Start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reverbSnapshot = RuntimeManager.CreateInstance(&lt;span style="color:#e6db74">&amp;#34;snapshot:/Reverb Zone 2&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reverbSnapshot.start(); &lt;span style="color:#75715e">// Start immediately&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Attach the instance to this object so FMOD knows its location&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RuntimeManager.AttachInstanceToGameObject(reverbSnapshot, transform, GetComponent&amp;lt;Rigidbody&amp;gt;());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> OnDestroy()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reverbSnapshot.stop(STOP_MODE.IMMEDIATE);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reverbSnapshot.release();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="part-3-the-low-health-filter-parameter-control">Part 3: The &amp;ldquo;Low Health&amp;rdquo; Filter (Parameter Control)&lt;/h2>
&lt;p>A common game mechanic is muffling audio when the player is dying. We can achieve this by controlling a Snapshot via a script parameter.&lt;/p>
&lt;h3 id="step-1-fmod-setup-1">Step 1: FMOD Setup&lt;/h3>
&lt;ol>
&lt;li>Add a &lt;strong>3-EQ Effect&lt;/strong> to your &lt;strong>In-Game&lt;/strong> bus.&lt;/li>
&lt;li>Create a Snapshot called &amp;ldquo;Low Health&amp;rdquo;.&lt;/li>
&lt;li>Scope in the &lt;strong>High&lt;/strong> and &lt;strong>Mid&lt;/strong> bands of the EQ and turn them all the way down (leaving only low frequencies).&lt;/li>
&lt;li>Right-click the Snapshot&amp;rsquo;s &lt;strong>Intensity&lt;/strong> dial and select &lt;strong>Expose as Parameter&lt;/strong>.&lt;/li>
&lt;li>Set the range of the parameter from 1 to 5.&lt;/li>
&lt;li>&lt;strong>Invert the curve:&lt;/strong>
&lt;ul>
&lt;li>When Parameter = 5 (Full Health), Intensity = 0% (No filter).&lt;/li>
&lt;li>When Parameter = 1 (Low Health), Intensity = 100% (Fully muffled).&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h3 id="step-2-linking-health-to-audio">Step 2: Linking Health to Audio&lt;/h3>
&lt;p>In your Player Controller script, you need to update the snapshot whenever health changes.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>FMOD.Studio.EventInstance healthFilter;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> Start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> healthFilter = RuntimeManager.CreateInstance(&lt;span style="color:#e6db74">&amp;#34;snapshot:/Low Health&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> healthFilter.start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> healthFilter.release(); &lt;span style="color:#75715e">// Release immediately so it cleans up when stopped&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// Call this function whenever the player takes damage or heals&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> SetHealthSnapshot() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> healthFilter.setParameterByName(&lt;span style="color:#e6db74">&amp;#34;Intensity&amp;#34;&lt;/span>, m_Damageable.currentHitPoints); &lt;span style="color:#75715e">// currentHealth should be between 1 and 5&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> OnDestroy()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> healthFilter.stop(STOP_MODE.IMMEDIATE);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>By hooking the &lt;code>UpdateAudioHealth&lt;/code> function into your existing damage logic (using Unity Events or direct method calls), your audio will dynamically react to the player&amp;rsquo;s survival status.&lt;/p>
&lt;hr>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>Snapshots are powerful tools that move beyond simple &amp;ldquo;Play/Stop&amp;rdquo; commands. By treating them as Event Instances in code, you can:&lt;/p>
&lt;ul>
&lt;li>Trigger reverb zones using &lt;strong>Colliders&lt;/strong>.&lt;/li>
&lt;li>Automate audio changes based on &lt;strong>3D Distance&lt;/strong>.&lt;/li>
&lt;li>Drive mix changes using gameplay variables like &lt;strong>Player Health&lt;/strong>.&lt;/li>
&lt;/ul>
&lt;p>With these tools, you&amp;rsquo;ve now covered the core pillars of FMOD implementation: Events, Parameters, Playback States, and Snapshots.&lt;/p></content></item><item><title>FMOD Namespaces</title><link>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-11/fmod-namespaces/</link><pubDate>Wed, 12 Jun 2024 00:00:00 +0000</pubDate><guid>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-11/fmod-namespaces/</guid><description>Key Concepts Namespaces group related types in C# and avoid name collisions. Using directives (using ...;) make types in a namespace available without full qualification. Unity and FMOD both expose APIs through namespaces that you reference from your scripts. Recap: Namespaces &amp;amp; Classes in Unity When you create a new Unity script, you’ll see using directives at the top—these specify namespaces. They let you access types from Unity’s API without fully qualifying them.</description><content>&lt;h2 id="key-concepts">&lt;strong>Key Concepts&lt;/strong>&lt;/h2>
&lt;ul>
&lt;li>Namespaces group related types in C# and avoid name collisions.&lt;/li>
&lt;li>Using directives (&lt;code>using ...;&lt;/code>) make types in a namespace available without full qualification.&lt;/li>
&lt;li>Unity and FMOD both expose APIs through namespaces that you reference from your scripts.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Recap: Namespaces &amp;amp; Classes in Unity&lt;/strong>
When you create a new Unity script, you’ll see using directives at the top—these specify namespaces. They let you access types from Unity’s API without fully qualifying them. Below that, each script defines a public class that usually inherits from &lt;code>MonoBehaviour&lt;/code>, which provides lifecycle methods like &lt;code>Start()&lt;/code> and &lt;code>Update()&lt;/code>. &lt;code>MonoBehaviour&lt;/code> lives in the &lt;code>UnityEngine&lt;/code> namespace.&lt;/p>
&lt;p>You can view the definition of classes like &lt;code>MonoBehaviour&lt;/code> in Visual Studio using “Go To Definition,” which helps to reveal their structure and included namespaces.&lt;/p>
&lt;p>Block-scoped and file-scoped namespaces:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// Block-scoped (common in Unity projects)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">namespace&lt;/span> UnityEngine
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">MonoBehaviour&lt;/span> : Behaviour
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// class contents&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>File-scoped (C# 10+):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">namespace&lt;/span> UnityEngine;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">MonoBehaviour&lt;/span> : Behaviour
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// class contents&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="working-with-namespaces">&lt;strong>Working with Namespaces&lt;/strong>&lt;/h2>
&lt;p>Create a new script in Unity called &lt;code>MyScript.cs&lt;/code> to explore namespaces.&lt;/p>
&lt;p>If you remove a using directive (for example, &lt;code>using UnityEngine;&lt;/code>), references like &lt;code>MonoBehaviour&lt;/code> will no longer resolve and you’ll get compiler errors. You can also declare your own namespaces to organize custom scripts.&lt;/p>
&lt;p>For example, using a traditional namespace declaration:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">namespace&lt;/span> MyNamespace {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">MyClass&lt;/span> { }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>To use &lt;code>MyClass&lt;/code>, either add &lt;code>using MyNamespace;&lt;/code> to the top of another script or fully qualify it as &lt;code>MyNamespace.MyClass&lt;/code>.&lt;/p>
&lt;p>&lt;code>MonoBehaviour&lt;/code> derives from the &lt;code>Behaviour&lt;/code> class (also in &lt;code>UnityEngine&lt;/code>). Use “Go To Definition” in your IDE to trace the inheritance chain and see which namespaces types come from.&lt;/p>
&lt;h2 id="fmod-namespaces-in-unity">&lt;strong>FMOD Namespaces in Unity&lt;/strong>&lt;/h2>
&lt;p>FMOD commands for game audio, like &lt;code>PlayOneShot&lt;/code>, rely on namespaces just like Unity does. Example component:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> FMODUnity;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> UnityEngine;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">PlayEvent&lt;/span> : MonoBehaviour
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e"> [SerializeField]&lt;/span> &lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> eventPath; &lt;span style="color:#75715e">// or use EventReference&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> Start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Positional one-shot&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RuntimeManager.PlayOneShot(eventPath, transform.position);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Or, to attach to a GameObject:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// RuntimeManager.PlayOneShotAttached(eventPath, gameObject);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Here:&lt;/p>
&lt;ul>
&lt;li>&lt;code>FMODUnity&lt;/code> is the FMOD Unity integration namespace.&lt;/li>
&lt;li>&lt;code>RuntimeManager&lt;/code> is a class within &lt;code>FMODUnity&lt;/code>.&lt;/li>
&lt;li>&lt;code>PlayOneShot&lt;/code>/&lt;code>PlayOneShotAttached&lt;/code> are methods on &lt;code>RuntimeManager&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>To see how these work, open the FMOD integration scripts in your project (for example, &lt;code>RuntimeManager.cs&lt;/code>) and inspect how namespaces and classes are organized. Declaring &lt;code>using FMODUnity;&lt;/code> lets you call &lt;code>RuntimeManager.PlayOneShot(...)&lt;/code> without repeatedly writing the full namespace.&lt;/p>
&lt;p>Go and find your Unity project’s &lt;code>FMODUnity&lt;/code> folder (usually under &lt;code>Assets/Plugins/FMOD/src&lt;/code>) to explore the FMOD classes and their namespaces.&lt;/p>
&lt;h3 id="understanding-playoneshot">Understanding PlayOneShot&lt;/h3>
&lt;p>Common overloads you’ll use in Unity:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// Positional one‑shot at a world position&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>RuntimeManager.PlayOneShot(&lt;span style="color:#66d9ef">string&lt;/span> eventPath, Vector3 position);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>RuntimeManager.PlayOneShot(EventReference eventRef, Vector3 position);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// One‑shot attached to and following a GameObject&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>RuntimeManager.PlayOneShotAttached(&lt;span style="color:#66d9ef">string&lt;/span> eventPath, GameObject go);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>RuntimeManager.PlayOneShotAttached(EventReference eventRef, GameObject go);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Notes:&lt;/p>
&lt;ul>
&lt;li>Fire‑and‑forget: these methods return &lt;code>void&lt;/code>. You can’t stop or change parameters afterwards. For control, use &lt;code>FMOD.Studio.EventInstance&lt;/code> or a &lt;code>StudioEventEmitter&lt;/code>.&lt;/li>
&lt;li>Position vs attached: &lt;code>PlayOneShot&lt;/code> uses a fixed &lt;code>Vector3&lt;/code> at spawn time; &lt;code>PlayOneShotAttached&lt;/code> follows the GameObject’s transform.&lt;/li>
&lt;li>EventReference vs string: &lt;code>EventReference&lt;/code> is type‑safe and assignable in the Inspector; a string path is quick but error‑prone.&lt;/li>
&lt;/ul>
&lt;p>Example using an &lt;code>EventReference&lt;/code> field:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> FMODUnity;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> UnityEngine;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">PlayEventRef&lt;/span> : MonoBehaviour
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e"> [SerializeField]&lt;/span> &lt;span style="color:#66d9ef">private&lt;/span> EventReference eventRef;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> Start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RuntimeManager.PlayOneShot(eventRef, transform.position);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Or follow this GameObject:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// RuntimeManager.PlayOneShotAttached(eventRef, gameObject);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="example-when-you-need-control">Example: When You Need Control&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// For parameter control, don&amp;#39;t use PlayOneShot:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">var&lt;/span> instance = RuntimeManager.CreateInstance(&lt;span style="color:#e6db74">&amp;#34;event:/Music/Stinger&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>instance.setParameterByName(&lt;span style="color:#e6db74">&amp;#34;Intensity&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">0.8f&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>instance.start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// ... later, you can modify or stop it ...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>instance.release();
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="key-takeaway">Key Takeaway&lt;/h2>
&lt;p>&lt;strong>The GUID overload is the &amp;ldquo;real&amp;rdquo; function&lt;/strong> - the EventReference and string versions are just convenient wrappers that convert their input to GUID and pass it along. This is a common design pattern called the &lt;strong>Telescoping Pattern&lt;/strong> or &lt;strong>Overload Chaining&lt;/strong>.&lt;/p>
&lt;p>&lt;strong>Bottom line:&lt;/strong> PlayOneShot functions are perfect for simple one-off sounds that don&amp;rsquo;t need runtime control.&lt;/p>
&lt;h3 id="event-instances-and-nested-namespaces">&lt;strong>Event Instances and Nested Namespaces&lt;/strong>&lt;/h3>
&lt;p>FMOD exposes nested namespaces, such as &lt;code>FMOD.Studio.EventInstance&lt;/code>:&lt;/p>
&lt;ul>
&lt;li>&lt;code>FMOD&lt;/code> is a namespace.&lt;/li>
&lt;li>&lt;code>Studio&lt;/code> is a nested namespace inside &lt;code>FMOD&lt;/code>.&lt;/li>
&lt;li>&lt;code>EventInstance&lt;/code> is a struct inside &lt;code>Studio&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>Open up the definition of &lt;code>EventInstance&lt;/code> in Visual Studio to see its members and methods. &lt;code>EventInstance&lt;/code> is a value type (struct) representing a playable instance of an FMOD event.&lt;/p>
&lt;blockquote>
&lt;p>Struct: A value type that holds data directly, unlike classes which are reference types.&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">struct&lt;/span> &lt;span style="color:#a6e22e">EventInstance&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Methods and properties for controlling the event instance&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">public&lt;/span> RESULT start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">public&lt;/span> RESULT stop(STOP_MODE mode);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">public&lt;/span> RESULT setParameterByName(&lt;span style="color:#66d9ef">string&lt;/span> name, &lt;span style="color:#66d9ef">float&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// ... more members ...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>See if you can find methods like &lt;code>start()&lt;/code>, &lt;code>stop()&lt;/code>, and &lt;code>setParameterByName()&lt;/code> in the definition.&lt;/p>
&lt;p>If you use types from &lt;code>FMOD.Studio&lt;/code>, add the appropriate using directives:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> FMOD;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> FMOD.Studio;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Alternatively, fully qualify names (for example, &lt;code>FMOD.Studio.EventInstance&lt;/code>).&lt;/p>
&lt;p>&lt;strong>Streamlining Your Scripts&lt;/strong>
Unity sometimes adds unused namespaces to your scripts by default. Visual Studio will gray out those not needed; clean them up to keep your code clear.&lt;/p>
&lt;p>&lt;strong>Wrap-Up&lt;/strong>
By understanding and leveraging namespaces you can:&lt;/p>
&lt;ul>
&lt;li>Organize code clearly and avoid name collisions.&lt;/li>
&lt;li>Use shorter, clearer code with &lt;code>using&lt;/code> directives or fully qualified names when appropriate.&lt;/li>
&lt;li>Navigate Unity and FMOD APIs more confidently during audio implementation.&lt;/li>
&lt;/ul></content></item><item><title>How to Use Playback States to Control Audio Flow</title><link>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-14/playback-states/</link><pubDate>Wed, 12 Jun 2024 00:00:00 +0000</pubDate><guid>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-14/playback-states/</guid><description>When implementing dynamic audio in Unity using FMOD, you often need to know exactly what an audio event is doing at any given moment. Is it playing? Is it stopping? Has it already stopped?
This is where Playback States come in.
In this tutorial, we will explore what FMOD Playback States are, how to access them in your C# scripts, and how to use them to fix a common audio bug: the &amp;ldquo;stuttering&amp;rdquo; sound effect on UI sliders.</description><content>&lt;p>When implementing dynamic audio in Unity using FMOD, you often need to know exactly what an audio event is doing at any given moment. Is it playing? Is it stopping? Has it already stopped?&lt;/p>
&lt;p>This is where &lt;strong>Playback States&lt;/strong> come in.&lt;/p>
&lt;p>In this tutorial, we will explore what FMOD Playback States are, how to access them in your C# scripts, and how to use them to fix a common audio bug: the &amp;ldquo;stuttering&amp;rdquo; sound effect on UI sliders.&lt;/p>
&lt;h2 id="what-is-a-playback-state">What is a Playback State?&lt;/h2>
&lt;p>A Playback State is a tool from FMOD’s API that allows us to check the status of an Event Instance. It returns the current status of the audio as an &lt;strong>Enumerator (Enum)&lt;/strong>.&lt;/p>
&lt;p>The five main states returned by the API are:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>PLAYING&lt;/strong>&lt;/li>
&lt;li>&lt;strong>STARTING&lt;/strong>&lt;/li>
&lt;li>&lt;strong>STOPPING&lt;/strong>&lt;/li>
&lt;li>&lt;strong>STOPPED&lt;/strong>&lt;/li>
&lt;li>&lt;strong>SUSTAINING&lt;/strong>&lt;/li>
&lt;/ol>
&lt;p>By accessing these states, we can write logic that prevents sounds from overlapping or triggering incorrectly.&lt;/p>
&lt;h2 id="part-1-setting-up-the-variable">Part 1: Setting Up the Variable&lt;/h2>
&lt;p>To use Playback States, you first need to define a variable to store the state information. Open the &lt;code>MusicPlayer&lt;/code> script.&lt;/p>
&lt;p>Inside your class, declare a variable of type &lt;code>FMOD.Studio.PLAYBACK_STATE&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">private&lt;/span> FMOD.Studio.PLAYBACK_STATE musicPlayState;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;em>Note: You can omit &lt;code>FMOD.Studio&lt;/code> if you are already using that namespace at the top of your script.&lt;/em>&lt;/p>
&lt;h2 id="part-2-retrieving-the-state">Part 2: Retrieving the State&lt;/h2>
&lt;p>To see what your audio is doing, you need to use the &lt;code>.getPlaybackState()&lt;/code> command. This command queries the event instance and outputs the result into the variable we just created.&lt;/p>
&lt;p>If you want to track the state in real-time, place this logic in your &lt;code>Update()&lt;/code> function:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> Update()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Assuming &amp;#39;_musicInst&amp;#39; is your FMOD Event Instance&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> _musicInst.getPlaybackState(&lt;span style="color:#66d9ef">out&lt;/span> musicPlayState);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Debug to the console to see the state change&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Debug.Log(musicPlayState);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>How it works:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>While Playing:&lt;/strong> The console will log &lt;code>PLAYING&lt;/code>.&lt;/li>
&lt;li>&lt;strong>Entering a Portal/Stopping:&lt;/strong> If you trigger a stop command, it switches to &lt;code>STOPPING&lt;/code>.&lt;/li>
&lt;li>&lt;strong>Finished:&lt;/strong> Once the audio fades out completely, it switches to &lt;code>STOPPED&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>&lt;em>Important:&lt;/em> &lt;code>getPlaybackState&lt;/code> only checks the state on the specific frame it is called. If you only put this in &lt;code>Start()&lt;/code>, it will likely only ever say &lt;code>STARTING&lt;/code> because it never updates again.&lt;/p>
&lt;h2 id="part-3-a-practical-example-the-ui-slider-fix">Part 3: A Practical Example (The UI Slider Fix)&lt;/h2>
&lt;p>One of the most useful applications for Playback States is preventing audio spam.&lt;/p>
&lt;p>Imagine you have a volume slider in your Settings menu. You want a sound effect to play when the user moves the slider so they can hear the volume level. However, if you simply tell the sound to &lt;code>Start()&lt;/code> every time the slider value changes, the sound will restart every single frame the mouse moves. This creates a messy &amp;ldquo;machine gun&amp;rdquo; stuttering effect.&lt;/p>
&lt;p>We can use Playback States to ensure the sound only starts if it isn&amp;rsquo;t &lt;em>already&lt;/em> playing.&lt;/p>
&lt;h3 id="step-1-the-setup">Step 1: The Setup&lt;/h3>
&lt;ol>
&lt;li>&lt;strong>In FMOD:&lt;/strong> Create a 2D Event (e.g., &lt;code>LevelTest&lt;/code>) in a &amp;ldquo;UI&amp;rdquo; folder.
&lt;ol>
&lt;li>Add some test sounds into a Multi Instrument.&lt;/li>
&lt;li>Ensure the event is sent to the SFX bus.&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>&lt;strong>In Unity:&lt;/strong> Open your &lt;code>F_BusControl&lt;/code> script.&lt;/li>
&lt;/ol>
&lt;h3 id="step-2-modify-the-script">Step 2: Modify the Script&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// ... Your existing using statements ...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">F_BusControl_class&lt;/span> : MonoBehaviour
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// .. . Your existing variables ...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> EventInstance levelTestEvent;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Start is called once before the first execution of Update after the MonoBehaviour is created&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> Start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// ... Your existing bus setup code ...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// only assign the event if its actually an SFX&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span>(busPath == &lt;span style="color:#e6db74">&amp;#34;SFX&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> levelTestEvent = RuntimeManager.CreateInstance(&lt;span style="color:#e6db74">&amp;#34;event:/SFX/UI/LevelTest&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#66d9ef">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> levelTestEvent.release();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> VolumeLevel(&lt;span style="color:#66d9ef">float&lt;/span> newSliderValue)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bus.setVolume(newSliderValue);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> levelTestEvent.start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You can hear that the sound stutters when you drag the slider quickly. We need to fix it by telling it to only start if th sound is not already playing.&lt;/p>
&lt;p>We can use playback states to fix this issue:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// ... Your existing using statements ...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">F_BusControl_class&lt;/span> : MonoBehaviour
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// .. . Your existing variables ...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> EventInstance levelTestEvent;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> PLAYBACK_STATE pbState;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Start is called once before the first execution of Update after the MonoBehaviour is created&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> Start()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// ... Your existing bus setup code ...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// only assign the event if its actually an SFX&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span>(busPath == &lt;span style="color:#e6db74">&amp;#34;SFX&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> levelTestEvent = RuntimeManager.CreateInstance(&lt;span style="color:#e6db74">&amp;#34;event:/SFX/UI/LevelTest&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#66d9ef">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> levelTestEvent.release();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> VolumeLevel(&lt;span style="color:#66d9ef">float&lt;/span> newSliderValue)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bus.setVolume(newSliderValue);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span>(busPath == &lt;span style="color:#e6db74">&amp;#34;SFX&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Check the playback state before starting the event&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> levelTestEvent.getPlaybackState(&lt;span style="color:#66d9ef">out&lt;/span> pbState);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (pbState != PLAYBACK_STATE.PLAYING)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> levelTestEvent.start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, when you drag the slider, the sound will play once. It will only play again after the previous instance has finished, resulting in a smooth, non-stuttering audio experience.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>By using &lt;strong>Playback States&lt;/strong>, you gain precise control over when your audio triggers. You are no longer just telling FMOD to &amp;ldquo;Play&amp;rdquo;; you are asking FMOD &amp;ldquo;Are you busy?&amp;rdquo; and acting accordingly.&lt;/p>
&lt;p>This simple check is essential for creating polished UI sounds, managing dynamic music transitions, and ensuring your game&amp;rsquo;s audio sounds professional and clean.&lt;/p></content></item><item><title>Interpreting Code: Understanding Game Scripts as an Audio Designer</title><link>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-11/interpreting-code/</link><pubDate>Wed, 12 Jun 2024 00:00:00 +0000</pubDate><guid>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-11/interpreting-code/</guid><description>Interpreting Code: Understanding Game Scripts as an Audio Designer When designing sound for games, we often spend our time inside FMOD, Wwise, or Unity’s audio windows—triggering events, mixing layers, and crafting dynamic soundscapes. But at some point, we hit a barrier: to make our sounds respond to gameplay, we need to understand the game’s code.
This post walks you through the fundamentals of reading and using existing scripts—without needing to become a full-on programmer.</description><content>&lt;h1 id="interpreting-code-understanding-game-scripts-as-an-audio-designer">Interpreting Code: Understanding Game Scripts as an Audio Designer&lt;/h1>
&lt;p>When designing sound for games, we often spend our time inside FMOD, Wwise, or Unity’s audio windows—triggering events, mixing layers, and crafting dynamic soundscapes. But at some point, we hit a barrier: to make our sounds respond to gameplay, we need to understand &lt;em>the game’s code.&lt;/em>&lt;/p>
&lt;p>This post walks you through the fundamentals of reading and using existing scripts—without needing to become a full-on programmer. It’s about collaboration, curiosity, and creative problem-solving through code.&lt;/p>
&lt;hr>
&lt;h2 id="general-vs-custom-audio-implementation">General vs. Custom Audio Implementation&lt;/h2>
&lt;p>Audio in games generally falls into two categories:&lt;/p>
&lt;h3 id="general-implementation">General Implementation&lt;/h3>
&lt;p>These sounds use built-in tools from Unity or FMOD. You don’t need to touch other scripts to make them work.
Examples include ambient loops, trigger-based footsteps, or teleport sounds using Unity’s collider system.&lt;/p>
&lt;h3 id="custom-implementation">Custom Implementation&lt;/h3>
&lt;p>These sounds rely on &lt;em>code written by developers&lt;/em>. They interact with the game’s systems—like dynamic music that changes when the player enters combat or a car engine pitch that follows RPM.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Aspect&lt;/th>
&lt;th>General Implementation&lt;/th>
&lt;th>Custom Implementation&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>What it is&lt;/td>
&lt;td>Uses built-in tools in Unity or FMOD; no script edits needed&lt;/td>
&lt;td>Relies on developer-written code; hooks into gameplay systems&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Examples&lt;/td>
&lt;td>Ambient loops; trigger-based footsteps; teleport sounds via colliders&lt;/td>
&lt;td>Dynamic music reacting to combat; engine pitch following RPM&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>To handle these, you’ll need to identify where the relevant variables and functions live in code.&lt;/p>
&lt;hr>
&lt;h2 id="reading-game-code-without-fear">Reading Game Code Without Fear&lt;/h2>
&lt;p>Let’s use Unity’s &lt;strong>PlayerController&lt;/strong> script (from the 3D Game Kit) as an example. Even without programming experience, you can learn a lot by exploring the script methodically.&lt;/p>
&lt;h3 id="1-look-at-variable-names">1. Look at Variable Names&lt;/h3>
&lt;p>Names like &lt;code>JumpSpeed&lt;/code>, &lt;code>Gravity&lt;/code>, or &lt;code>m_ReadyToJump&lt;/code> tell you their purpose.
If a variable is &lt;em>public&lt;/em>, you’ll also see it in Unity’s Inspector—meaning you or another script can adjust it at runtime.&lt;/p>
&lt;p>In another script attached to Ellen you could have:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">EllenAudio&lt;/span> : MonoBehaviour
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Reference to the PlayerController script&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">private&lt;/span> PlayerController pc;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> Start() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Get the PlayerController component&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> pc = GetComponent&amp;lt;PlayerController&amp;gt;();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> pc.JumpSpeed = &lt;span style="color:#ae81ff">10.0f&lt;/span>; &lt;span style="color:#75715e">// Example of accessing JumpSpeed variable&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You might also spot variables like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">bool&lt;/span> m_IsGrounded;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">bool&lt;/span> m_PreviouslyGrounded;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>These booleans (true/false variables) control jumping logic. Understanding their behavior helps us know when to trigger audio—like footsteps, landings, or jump sounds.&lt;/p>
&lt;hr>
&lt;h3 id="2-read-the-programmer-notes">2. Read the Programmer Notes&lt;/h3>
&lt;p>Most scripts include comments after &lt;code>//&lt;/code>.
These notes explain why a line of code exists. For example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// Whether or not the input state and Ellen are correct to allow jumping&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Comments like this tell you the intent behind a variable—perfect clues when you’re connecting audio to gameplay.&lt;/p>
&lt;hr>
&lt;h3 id="3-trace-variables-in-code">3. Trace Variables in Code&lt;/h3>
&lt;p>In Visual Studio or VS Code, double-click a variable to highlight every place it’s used.
Follow it through the script to see &lt;em>where it changes value&lt;/em>.&lt;/p>
&lt;p>You can also right click and select &amp;ldquo;Find All References&amp;rdquo; to see every instance of that variable in the script.&lt;/p>
&lt;p>For example, you might find:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> FixedUpdate()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> m_PreviouslyGrounded = m_IsGrounded;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CalculateVerticalMovement();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>FixedUpdate()&lt;/code> runs 50 times per second—ideal for physics checks.
Here, the code tracks whether Ellen &lt;em>was&lt;/em> on the ground last frame versus &lt;em>is&lt;/em> on the ground now. That’s exactly the kind of information you could use for sound triggers.&lt;/p>
&lt;hr>
&lt;h2 id="debugging-to-learn">Debugging to Learn&lt;/h2>
&lt;p>Debugging isn’t just for programmers—it’s an &lt;em>auditory designer’s microscope.&lt;/em>
By adding a simple print statement, you can &lt;em>watch&lt;/em> how the game behaves in real-time.&lt;/p>
&lt;p>Add this line to your script:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>Debug.Log(&lt;span style="color:#e6db74">&amp;#34;IG: &amp;#34;&lt;/span> + m_IsGrounded + &lt;span style="color:#e6db74">&amp;#34; PG: &amp;#34;&lt;/span> + m_PreviouslyGrounded);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>When you run the game, Unity’s Console will display the variables each frame.
Jump, and you’ll see them flip from true to false and back again.&lt;/p>
&lt;p>To filter specific moments, use an &lt;code>if&lt;/code> statement:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">if&lt;/span> (m_PreviouslyGrounded != m_IsGrounded)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Debug.Log(&lt;span style="color:#e6db74">&amp;#34;Grounded state changed!&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This reports only when the player leaves or touches the ground—an exact moment you might trigger a landing sound or dust effect.&lt;/p>
&lt;p>We could use this information now to play a jump sound when the player leaves the ground and a landing sound when they touch down.&lt;/p>
&lt;hr>
&lt;h2 id="searching-through-scripts">Searching Through Scripts&lt;/h2>
&lt;p>Large scripts can feel overwhelming. Thankfully, you can search them.&lt;/p>
&lt;p>&lt;strong>Ctrl+F (Windows)&lt;/strong> or &lt;strong>Cmd+F (Mac)&lt;/strong> brings up a search bar.
Search for a term like &lt;code>die&lt;/code>, and you might find a function:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> Die()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Called when the player loses all health&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now you’ve found the perfect place to play a “death cry” sound.&lt;/p>
&lt;hr>
&lt;h2 id="a-custom-audio-example">A Custom Audio Example&lt;/h2>
&lt;p>Once you find the &lt;code>Die()&lt;/code> function, adding sound is simple:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>FMODUnity.RuntimeManager.PlayOneShotAttached(&lt;span style="color:#e6db74">&amp;#34;event:/Player/Death&amp;#34;&lt;/span>, gameObject);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>That single line links FMOD to Unity, playing your event when the player dies.
You can test it instantly—jump into the water or get hit by an enemy, and your audio should trigger when the character respawns.&lt;/p>
&lt;hr>
&lt;h2 id="knowing-when-to-collaborate">Knowing When to Collaborate&lt;/h2>
&lt;p>Sometimes you’ll find that the data you need—like enemy states or vehicle speed—comes from complex systems spread across many scripts. When that happens, it’s best to collaborate with the programmers. They can create clean hooks for audio events faster than you can reverse-engineer the code.&lt;/p>
&lt;p>However, by understanding the basics, you can communicate clearly:&lt;/p>
&lt;blockquote>
&lt;p>“I noticed there’s a &lt;code>bool inCombat&lt;/code> variable in EnemyAI.cs. Could we use that to switch FMOD music states?”&lt;/p>
&lt;/blockquote>
&lt;p>That kind of precise collaboration makes your work more efficient—and helps the whole team.&lt;/p>
&lt;hr>
&lt;h2 id="practice-explore-the-playercontroller">Practice: Explore the PlayerController&lt;/h2>
&lt;p>Here’s your challenge:&lt;/p>
&lt;ol>
&lt;li>Open the &lt;code>PlayerController&lt;/code> script.&lt;/li>
&lt;li>Find the &lt;code>PlayAudio()&lt;/code> function—used to trigger one-shots for footsteps, jumps, and impacts.&lt;/li>
&lt;li>See what conditions must be true before it runs.&lt;/li>
&lt;li>Use &lt;code>Debug.Log()&lt;/code> to print out those variables to the Console.&lt;/li>
&lt;li>Try connecting one of them to an FMOD event.&lt;/li>
&lt;/ol>
&lt;p>You’ll be surprised how much you already understand—and how quickly “interpreting code” becomes part of your creative toolkit.&lt;/p>
&lt;hr>
&lt;h2 id="closing-thoughts">Closing Thoughts&lt;/h2>
&lt;p>Understanding code isn’t about becoming a programmer.
It’s about reading the systems behind your sounds, identifying where to connect, and knowing when to collaborate.&lt;/p>
&lt;p>By interpreting scripts, you gain control over how your soundscapes react to gameplay—and make your audio as dynamic as the worlds you build.&lt;/p></content></item><item><title>Attaching Audio to Animations with FMOD and Unity</title><link>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-9/attaching-audio-to-animations/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/lectures/week-9/attaching-audio-to-animations/</guid><description>Introduction This post walks through a practical pipeline for triggering FMOD sound events from Unity Animation Events so your character’s footsteps, landings, and swishes line up precisely with what’s on screen. We’ll build a reusable C# function that can be called from any Animation Event and, later, accept the FMOD event path as a parameter—setting us up to drive parameters (like surface material) in a follow-up lesson.
What You’ll Build An FMOD event for a footstep, with a Material parameter that selects between multiple surface types.</description><content>&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>This post walks through a practical pipeline for triggering FMOD sound events from Unity Animation Events so your character’s footsteps, landings, and swishes line up precisely with what’s on screen. We’ll build a reusable C# function that can be called from any Animation Event and, later, accept the FMOD event path as a parameter—setting us up to drive parameters (like surface material) in a follow-up lesson.&lt;/p>
&lt;hr>
&lt;h2 id="what-youll-build">What You’ll Build&lt;/h2>
&lt;ul>
&lt;li>An FMOD event for a footstep, with a &lt;code>Material&lt;/code> parameter that selects between multiple surface types.&lt;/li>
&lt;li>A Unity C# function that:
&lt;ul>
&lt;li>Creates and attaches an FMOD event instance to the player at runtime,&lt;/li>
&lt;li>Starts the event,&lt;/li>
&lt;li>Releases the instance,&lt;/li>
&lt;li>Accepts a string parameter for the FMOD event path (so you can reuse it across many animations).&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Animation Events in Unity that call your C# function exactly when each foot hits the ground.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="fmod-setup-a-single-footstep-event-many-materials">FMOD Setup: A Single Footstep Event, Many Materials&lt;/h2>
&lt;p>Create a 3D event named &lt;code>Ellen_Run&lt;/code> in the &lt;code>SFX/Ellen&lt;/code> folder that plays one footstep per trigger.&lt;/p>
&lt;p>Add a user parameter named &lt;code>Material&lt;/code> and set it to Hold (so the footstep that already triggered won’t switch mid-play if the parameter changes). Add four values representing different surface types:&lt;/p>
&lt;ol>
&lt;li>Earth&lt;/li>
&lt;li>Stone&lt;/li>
&lt;li>Puddle&lt;/li>
&lt;li>Grass&lt;/li>
&lt;/ol>
&lt;p>&lt;img alt="alt text" src="https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/f-25-dad-424-sound-design-for-games/lectures/week-9/attaching-audio-to-animations/material-sheet.png">&lt;/p>
&lt;p>Set the parameter to Hold during playback to allow the current footstep to complete even if the player moves to a different surface.&lt;/p>
&lt;p>&lt;img alt="alt text" src="https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/f-25-dad-424-sound-design-for-games/lectures/week-9/attaching-audio-to-animations/set-to-hold.png">&lt;/p>
&lt;p>Add a Multi Instrument (right-click, then &lt;code>Add Multi Instrument&lt;/code>) for each material value and add the appropriate footstep samples for that surface. Enable Shuffle in the Multi Instrument settings to randomize footstep playback.&lt;/p>
&lt;p>&lt;img alt="alt text" src="https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/f-25-dad-424-sound-design-for-games/lectures/week-9/attaching-audio-to-animations/mutli-instrument.png">&lt;/p>
&lt;p>Now, when you select different values for the &lt;code>Material&lt;/code> parameter, you’ll hear footsteps on different surfaces.&lt;/p>
&lt;p>Save your project and build your banks before switching back to Unity.&lt;/p>
&lt;hr>
&lt;h2 id="unity-project-setup">Unity Project Setup&lt;/h2>
&lt;p>Create a script named &lt;code>F_Player&lt;/code> and add it to your player (Ellen) GameObject. This script will own all player-related SFX behaviors.&lt;/p>
&lt;h3 id="the-reusable-one-shot-function">The Reusable One-Shot Function&lt;/h3>
&lt;p>We’ll implement a “play one-shot with control” pattern: create an instance, attach it to the player, start it, and release it—while still allowing parameter control if we need it later.&lt;/p>
&lt;p>Here’s a simple implementation using a new &lt;code>PlayRunEvent&lt;/code> function. We use a public function so that Unity Animation Events can call it directly. For now we hardcode the event path; in the next section we’ll update the function to accept a string parameter so different animations can play different events.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> PlayRunEvent()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> EventInstance run = RuntimeManager.CreateInstance(&lt;span style="color:#e6db74">&amp;#34;event:/SFX/Ellen/Ellen_Run&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RuntimeManager.AttachInstanceToGameObject(run, gameObject, GetComponent&amp;lt;Rigidbody&amp;gt;());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> run.start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> run.release();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Why this approach instead of &lt;code>RuntimeManager.PlayOneShot(...)&lt;/code>?&lt;/p>
&lt;ul>
&lt;li>It gives you full control over parameters (e.g., &lt;code>Material&lt;/code>) per trigger.&lt;/li>
&lt;li>It’s easy to reuse: different animations can pass different event paths into the same function.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="hooking-up-unity-animation-events">Hooking Up Unity Animation Events&lt;/h2>
&lt;p>Animation Events in Unity let an animation call your function at a specific frame. For footsteps, add an event exactly when each foot contacts the ground.&lt;/p>
&lt;ol>
&lt;li>Locate Ellen’s run animations in the Project window:
&lt;ul>
&lt;li>&lt;code>3DGameKit/Art/Animations/AnimationClips/Characters/Ellen/&lt;/code>&lt;/li>
&lt;li>Expand the &lt;code>Ellen Run Forward&lt;/code> FBX to see its clips (forward, diagonal, etc.).&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Preview the clip in the Inspector. If you see “No model is available for preview,” drag the Ellen model into the preview window from &lt;code>Art/Models/Characters/Ellen&lt;/code>.&lt;/li>
&lt;li>Find the &lt;code>Ellen_Run_Forward&lt;/code> clip and click it to open the Animation tab. Click Edit to unlock it for modification.
&lt;ol>
&lt;li>&lt;img alt="alt text" src="https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/f-25-dad-424-sound-design-for-games/lectures/week-9/attaching-audio-to-animations/ellen-animation.png">&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>Open the Events tab on the animation.
&lt;ol>
&lt;li>&lt;img alt="alt text" src="https://dsu-digital-sound-design.github.io/f-25-dad-424-sound-design-for-games/f-25-dad-424-sound-design-for-games/lectures/week-9/attaching-audio-to-animations/events.png">&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>Scrub the timeline to the exact frame the foot contacts the ground.&lt;/li>
&lt;li>Click Add Event. In the Function field, enter &lt;code>PlayRunEvent&lt;/code>.&lt;/li>
&lt;li>Add a second event for the other foot.&lt;/li>
&lt;li>Click Apply to save the clip modifications.&lt;/li>
&lt;li>Play the scene and run forward; you should hear a footstep on each contact.&lt;/li>
&lt;/ol>
&lt;p>Important: The script containing the target function must live on the same GameObject that plays the animation via an Animator. Otherwise, the Animation Event won’t be able to find your function.&lt;/p>
&lt;hr>
&lt;h2 id="add-the-event-path-parameter">Add the Event Path Parameter&lt;/h2>
&lt;p>Now that the events are triggering, let’s pass the FMOD event path as a string parameter so we can reuse this function for other animations (like landing or attacking) later.&lt;/p>
&lt;p>Edit your &lt;code>F_Player&lt;/code> script to update &lt;code>PlayRunEvent&lt;/code> to accept a string parameter for the event path, as shown below.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> PlayRunEvent(&lt;span style="color:#66d9ef">string&lt;/span> eventPath)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> EventInstance run = RuntimeManager.CreateInstance(eventPath);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RuntimeManager.AttachInstanceToGameObject(run, gameObject, GetComponent&amp;lt;Rigidbody&amp;gt;());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> run.start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> run.release();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In your Animation Events, update each event to pass the event path string:
&lt;code>event:/SFX/Ellen/Ellen_Run&lt;/code> (or whatever your event path is).&lt;/p>
&lt;hr>
&lt;h2 id="understanding-c-parameters">Understanding C# Parameters&lt;/h2>
&lt;p>We defined &lt;code>PlayRunEvent(string eventPath)&lt;/code> so each animation can pass its own FMOD event path. This is a value parameter: the Animation Event provides a string, Unity passes it into your function at call time, and you use that to create the event instance.&lt;/p>
&lt;p>This pattern scales nicely:&lt;/p>
&lt;ul>
&lt;li>Walk clip → &lt;code>event:/SFX/Ellen/Ellen_Walk&lt;/code>&lt;/li>
&lt;li>Land clip → &lt;code>event:/SFX/Ellen/Ellen_Land&lt;/code>&lt;/li>
&lt;li>Attack clip → &lt;code>event:/SFX/Ellen/Ellen_Slash&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>You keep one function and let animations decide which event to play.&lt;/p>
&lt;hr>
&lt;h2 id="common-pitfalls">Common Pitfalls&lt;/h2>
&lt;ul>
&lt;li>The function name in Animation Events must match exactly, including case.&lt;/li>
&lt;li>The script with &lt;code>PlayRunEvent&lt;/code> must be on the animated GameObject (the same one with the Animator).&lt;/li>
&lt;li>Don’t forget to click Apply after editing Animation Events; missed applies are a classic time sink.&lt;/li>
&lt;li>Always call &lt;code>release()&lt;/code> after &lt;code>start()&lt;/code>; this defers cleanup until playback ends.&lt;/li>
&lt;li>If your footstep feels off by a few frames, adjust the event position slightly in the animation timeline—what looks like “contact” in the mesh may need a tiny lead or lag to feel right in-game.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="helpful-links">Helpful Links&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://docs.unity3d.com/Manual/AnimationEvents.html">Unity Animation Events Documentation&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://fmod.com/resources/documentation-unity?version=2.0&amp;page=welcome.html">FMOD Unity Integration Documentation&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://fmod.com/resources/documentation-api?version=2.0&amp;page=core-api-studio-eventinstance.html">FMOD EventInstance API Reference&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.unity3d.com/ScriptReference/Rigidbody.html">Unity Scripting API: Rigidbody&lt;/a>&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="more-complete-version-with-error-checking-and-parameterization">More complete version with error checking and parameterization:&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> UnityEngine;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> FMODUnity; &lt;span style="color:#75715e">// FMOD Unity integration&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">using&lt;/span> FMOD.Studio; &lt;span style="color:#75715e">// FMOD Studio API&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// by including these namespaces, we can use FMOD types directly&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">F_Player&lt;/span> : MonoBehaviour
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Optional: cache components you’ll likely need&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">private&lt;/span> Rigidbody _rb;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">void&lt;/span> Awake()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> _rb = GetComponent&amp;lt;Rigidbody&amp;gt;();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Called by Unity Animation Events. Pass an FMOD event path, e.g. &amp;#34;event:/SFX/Ellen/Ellen_Run&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> PlayRunEvent(&lt;span style="color:#66d9ef">string&lt;/span> eventPath)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#66d9ef">string&lt;/span>.IsNullOrEmpty(eventPath))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Debug.LogWarning(&lt;span style="color:#e6db74">&amp;#34;PlayRunEvent called without a valid FMOD event path.&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> EventInstance run = RuntimeManager.CreateInstance(eventPath);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Attach to this GameObject so the sound follows the player and 3D attenuation works as expected&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> RuntimeManager.AttachInstanceToGameObject(run, gameObject, _rb);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> run.start();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> run.release(); &lt;span style="color:#75715e">// Safe: schedules the instance for cleanup after playback finishes&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></content></item></channel></rss>