Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Scalable Sound System

Discussion in 'Scripting' started by ThickyVEVO, Aug 18, 2023.

  1. ThickyVEVO

    ThickyVEVO

    Joined:
    May 28, 2020
    Posts:
    16
    I'm trying to write a scalable system for playing footstep audio. What I'd like to accomplish is having Unity play the correct array of sounds when walking upon a matching surface (eg. walking on stone plays stone footstep audio, water plays water footsteps etc.) The issue I'm running into regards finding the proper way of matching the "hit.collider.tag's" output string to a corresponding array with the same name. Obviously, you can't use a string as an input to an array so is there a good way to use the correct array when the "hit.collider.tag's" output matches the string within the "FootStepSounds" class?

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. [System.Serializable]
    6. public class FootStepSounds
    7. {
    8.     public string stepName;
    9.     public AudioClip[] audioClips;
    10. }
    11. public class FootSteps : MonoBehaviour
    12. {
    13.     [SerializeField] AudioSource audioSrc;
    14.     [SerializeField] FootStepSounds[] stepSounds;
    15.     private PlayerController pc;
    16.     [SerializeField] Transform playerCamPos;
    17.     private float soundVolume;
    18.     private float stepTimer;
    19.     private int stepNumber;
    20.     private int previousStepNumber;
    21.     private void Start()
    22.     {
    23.         pc = GetComponent<PlayerController>();
    24.     }
    25.     private void Update()
    26.     {
    27.         HandleFootsteps(DetectSurface());
    28.     }
    29.     string DetectSurface()
    30.     {
    31.         Physics.SphereCast(playerCamPos.transform.position, .25f, Vector3.down, out RaycastHit hit, 3f);
    32.         return hit.collider.tag;
    33.     }
    34.     void HandleFootsteps(string surfaceName)
    35.     {
    36.         if (!pc.controller.isGrounded) return;
    37.         if (!PlayerController.isMoving) return;
    38.         stepTimer -= Time.deltaTime;
    39.         if (stepTimer <= 0)
    40.         {
    41.             print(surfaceName); print(soundVolume);
    42.             while (stepNumber == previousStepNumber)
    43.             {
    44.                 stepNumber = Random.Range(0, stepSounds[surfaceName].audioClips.Length - 1);
    45.             }
    46.             if (PlayerController.isCrouching)
    47.                 soundVolume = 0.15f;
    48.             else if (PlayerController.isSprinting)
    49.                 soundVolume = 0.4f;
    50.             else
    51.                 soundVolume = 0.25f;
    52.             audioSrc.volume = soundVolume;
    53.             audioSrc.PlayOneShot(stepSounds[surfaceName].audioClips[stepNumber]);
    54.             previousStepNumber = stepNumber;
    55.             stepTimer = pc.getCurrentOffset;
    56.         }
    57.     }
    58. }
    59.  
    upload_2023-8-18_14-15-48.png

    upload_2023-8-18_14-26-24.png
     
  2. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    I can think of 3 suggestions;
    • Make the arrays static so they can be used in a generic function,
    • Use an enum to determine surface which only references the correct array(and Length),
    • or Have all of your sounds in one array/list and keep track of what index-index is what.
    After those thoughts, I'm drawing a blank, lol..
     
  3. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    Just have a component that you attach to game objects that define the sounds of type of sounds they make.

    Make scriptable objects to define these sets of sounds. Then just raycast down and grab these component, and blindly play the sounds.

    Tags definitely won't be scalable at all here.
     
  4. ThickyVEVO

    ThickyVEVO

    Joined:
    May 28, 2020
    Posts:
    16
    Interesting solution. If I understand correctly, instead of having all the sounds stored on the player, the objects will hold the sound information instead and the player will grab from the object instead of it's own library.
     
  5. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    That's what I meant about the static array idea, just one class they're in, then character checks what floor it's on and references that array with a generic function. But yeah, I think Scriptable Objects basically do the same thing, not sure if they duplicate extra copies though, I'm not familiar with Scriptable Objects.
     
  6. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    Sort of. The idea is to make it data driven, rather than logic driven. You don't even need to grab the sound information, you just need to tell this object to 'play a random footstep sound' from the collection within it's scriptable object, perhaps providing an audio source to do so. This is the "Tell, don't ask" programming principle.
     
  7. ThickyVEVO

    ThickyVEVO

    Joined:
    May 28, 2020
    Posts:
    16
    I really like that way of thinking. Even conceptually, I initially thought about how it would work logically in reality, with steps coming from the players perspective, but in actuality it doesn't matter if the sounds data comes from the object instead because the player doesn't really care where the sound's data originates from because they wouldn't even know. As long as it keep up the illusion of reality it works.