<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Game Audio 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/game-audio/</link><description>Recent content in Game Audio 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/game-audio/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>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>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>