In this lecture Sam said he will not be covering Random Audio Playing. If you want to learn that go take the 3D Game Kit course on the Unity website. This course is suggested to take 4h and 40mins.
[LINK HERE] 3D Game Kit | Tutorials | Unity Asset Store
So I did that, and they don’t talk about how to add a Random Audio feature at all in that course. lol. So I came back here. Might want to revise the video here or update the content of the course to reflect that information.
So I thought I would try and figure it out on my own. I copied the Random Audio Player script they had in there course. I thought it was quite good, as it allowed you to
- Randomize Pitch, Have a delay if you wanted, Add in your sounds to randomly play, and then Allow you to add in an override. (I saw they had used the override for when the player character walked on rock floor, grassy field, through mud, ect. They would have an override for each scenario and fill the clips again with footsteps sounds for that particular terrain)
Here is the full code for their Random Audio Player
- THE only thing I changed was adding the namespace RPG.Audio to the code, when I placed it in our project. Which I mention below.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
namespace RPG.Audio
{
[RequireComponent(typeof(AudioSource))]
public class RandomAudioPlayer : MonoBehaviour
{
[Serializable]
public class MaterialAudioOverride
{
public Material[] materials;
public SoundBank[] banks;
}
[Serializable]
public class SoundBank
{
public string name;
public AudioClip[] clips;
}
public bool randomizePitch = true;
public float pitchRandomRange = 0.2f;
public float playDelay = 0;
public SoundBank defaultBank = new SoundBank();
public MaterialAudioOverride[] overrides;
[HideInInspector]
public bool playing;
[HideInInspector]
public bool canPlay;
protected AudioSource m_Audiosource;
protected Dictionary<Material, SoundBank[]> m_Lookup = new Dictionary<Material, SoundBank[]>();
public AudioSource audioSource { get { return m_Audiosource; } }
public AudioClip clip { get; private set; }
void Awake()
{
m_Audiosource = GetComponent<AudioSource>();
for (int i = 0; i < overrides.Length; i++)
{
foreach (var material in overrides[i].materials)
m_Lookup[material] = overrides[i].banks;
}
}
/// <summary>
/// Will pick a random clip to play in the assigned list. If you pass a material, it will try
/// to find an override for that materials or play the default clip if none can ben found.
/// </summary>
/// <param name="overrideMaterial"></param>
/// <returns> Return the choosen audio clip, null if none </returns>
public AudioClip PlayRandomClip(Material overrideMaterial, int bankId = 0)
{
#if UNITY_EDITOR
//UnityEditor.EditorGUIUtility.PingObject(overrideMaterial);
#endif
if (overrideMaterial == null) return null;
return InternalPlayRandomClip(overrideMaterial, bankId);
}
/// <summary>
/// Will pick a random clip to play in the assigned list.
/// </summary>
public void PlayRandomClip()
{
clip = InternalPlayRandomClip(null, bankId: 0);
}
AudioClip InternalPlayRandomClip(Material overrideMaterial, int bankId)
{
SoundBank[] banks = null;
var bank = defaultBank;
if (overrideMaterial != null)
if (m_Lookup.TryGetValue(overrideMaterial, out banks))
if (bankId < banks.Length)
bank = banks[bankId];
if (bank.clips == null || bank.clips.Length == 0)
return null;
var clip = bank.clips[Random.Range(0, bank.clips.Length)];
if (clip == null)
return null;
m_Audiosource.pitch = randomizePitch ? Random.Range(1.0f - pitchRandomRange, 1.0f + pitchRandomRange) : 1.0f;
m_Audiosource.clip = clip;
m_Audiosource.PlayDelayed(playDelay);
return clip;
}
}
}
So now onto getting it to work in our project. * We already added this in our course here *, we are just changing the Single Sound to a Random Sound
Lets add it to our regular arrow. I want to play two things here.
- Launch Sound (random)
- Hit Sound (random)
So on the Launch Sound we had
And on the Hit Sound we had
The main takeaways here are the one sound (in the Audio Clip) and the “Play On Awake” on the Launch Sound ONLY is checked off.
And now back at the top the the Hierarchy (under the Main “ArrowProjectile White” in this case) we had the Projectile script attached and in there we had the Hit Sound dragged and dropped into the On Hit() section.
Now we are going to change it to play a random sound.
I went back to the Unity 3D Game Kit course and tried to follow what they were doing.
They had on their sound the same type of setup.
In the Hierarchy (it looked just like ours - an AudioSource and then sounds under it)
And in the sounds themselves they were the same as ours.
They had the Audio Source, but they also had the Random Audio Player script (which I linked above)
The main takeaway here was that NO Audio Clip was selected in the Audio Source top section
First thing to do was get their script into our project. I copied the Random Audio Player script and pasted it in our scripts section, under Audio.
I then went into the script and changed the namespace to RPG.Audio
Then back to the prefab “ArrowProjectile White” and I added the Random Audio Player script to our Launch Sound and Hit Sound.
Then added the random sound files to each
AND remembered to clear the Audio lip in the Main Audio Source to nothing on both.
Here is the Launch Sound
Here is the Hit Sound
Now back to the Hierarchy at the top again under “ArrowProjectile White” I changed the hit sound (drag and dropped the new hit Sound) to the Projectile script location On Hit area at the bottom. I also changed the Pull down beside it to Play Random Clip.
You will notice it changed the icon beside the Hit Sound from a sound speaker to a script icon.
After this I did a quick play test and the Hit Sound works! Every time the enemy gets hit it plays a random sound. But also now… there is NO sound being played at all when you Launch the arrow.
So what is the issue there? I tried many different things but will only illustrate one here as to not confuse the issue and/or solution. I thought “where?” should the sound be played, and came to the conclusion it should be played under the Fighter.cs script. I thought this was a good spot as this is where we determine if we are in range of the target, AND if we have a projectile, then we should launch the projectile at the target. So I added this line of code
private RandomAudioPlayer launchAudio;
I added it to the top under the LazyValue
public class Fighter : MonoBehaviour, IAction, ISaveable, IModifierProvider
{
[SerializeField] float timeBetweenAttacks = 1f;
[SerializeField] Transform rightHandTransform = null;
[SerializeField] Transform leftHandTransform = null;
[SerializeField] Weapon defaultWeapon = null;
Health target;
float timeSinceLastAttack = Mathf.Infinity;
LazyValue<Weapon> currentWeapon;
private RandomAudioPlayer launchAudio;
Now doing so meant that I would also have to call namespace RPG.Audio at the top to get the RandomAudioPLayer script to work. Also the LaunchAudio portion I would paste under the Hit area in the code near the bottom in this Fighter.cs script.
void Hit()
{
if (target == null) { return; }
if (!GetIsInRange(target.transform)) return;
float damage = GetComponent<BaseStats>().GetStat(Stat.Damage);
if (currentWeapon.value.HasProjectile())
{
currentWeapon.value.LaunchProjectile(rightHandTransform, leftHandTransform, target, gameObject, damage);
launchAudio.PlayRandomClip();
}
else
{
target.TakeDamage(gameObject, damage);
}
}
Now we need to associate the sound with the Projectile.cs script itself so that when the Fighter.sc script called the LaunchProjectile or HasProjectile function it would play the sound I added to the “ArrowProjectile White” prefab. So load up the Projectile.cs script and add this line to the top under the SerializeField section. “Public RandomAudioPlayer launchAudio & and the title [Header(“Audio”)]” so you could see it.
public class Projectile : MonoBehaviour
{
[SerializeField] float speed = 1;
[SerializeField] float damage = 1f;
[SerializeField] bool isHoming = true;
[SerializeField] GameObject hitEffect = null;
[SerializeField] float maxLifeTime = 10;
[SerializeField] GameObject[] destroyOnHit = null;
[SerializeField] float lifeAfterImpact = 2;
[SerializeField] UnityEvent onHit;
Health target = null;
GameObject instigator = null;
[Header("Audio")]
public RandomAudioPlayer launchAudio;
Remember to call the namespace RPG.Audio at the very top to get the RandomAudioPlayer to work.
And the last thing to do was to add the sound to the prefab. Load up the “ArrowProjectile White” again. And in the top “ArrowProjectle White” under the Hierarchy go to the Inspector and you will see in the Projectile script attached at the bottom a new Launch Audio field. Drag and Drop your Launch Sound under the Hierarchy to this.
And we should be done. I checked the Unity 3D Game Kit course again to see if it was the same as how they added it.
Yup. Looked the same.
Now back to the game to playtest, and I unfortunately get this error now
Line 123 in the Fighter.cs script is you guessed it…
launchAudio.PlayRandomClip();
Now this is how they called in in the Unity 3D Game Kit course, so I’m not sure what I’m missing here. I’ve tried pasting this line into many different locations in the code for this section (at the beginning of this section, at the end, ect) , and always get the same error. So I’m sure it is something simple that I’m overlooking.
[EDIT]
I’ve been thinking about this some more and I think this might be the issue. In the Projectile.cs script I’m stating at the top Public RandomAudioPlayer launchAudio, and in the Fighter script at the top I’m also stating private RandomAudioPLayer launchAudio. I think I should be calling the launchAudio I assign from the projectile.cs INTO the fighter.cs script for this to work right. But I don’t know how to do this.
[End EDIT]
I’m very sorry this was so long, but I wanted to have a good document for new users to follow on
"How to add this to your game", or “How to fix this if it doesn’t work in your game”.
If anyone has anything to try or if anyone can help I would be extremely happy for the guidance. And I’m sure new users would also be very thankful for the assistance. Cheers Everyone!