Footsteps Sounds in Unity3D C# code example

This was my old attempt to implement footsteps sounds in Unity 3D which I did use to test my sounds (it was used, inter alia, here /footsteps-in-unity3d/) some time ago. If you need some easy example, then it may help you. Or else. It is written in C# in Unity3D 4.x but it is still works with Unity 5.x. It uses Resources.Load which maybe isn’t the most efficient way of loading sounds to the memory but it is an easy one. It works with default First Person Controller. The script supports switching footsteps type based on splatmap detection and object collision.

To contains two scripts footstepsSound.cs and footstepsSoundClass.cs.  In order to work you need to creat it into your project and attach footstepsSound.cs script to First Person Controller.

Between lines 34 and 47 in footstepsSound.cs script you have to enter paths to your own directories where you have your sounds. Single file is for single step so you need to cut it appropriately.

The tag parameter is used to associate particular footsteps type with objects tag. It means you need to add the same tag here and to the object if you want to detect it’s collision. Another parameter tells the script whether the sound corresponds with objects or textures on splatmap. “-1” is for objects, and the numbers (starting with 0) are for splatmap texture index. If you are on that texture this sound will be played then.

You can decide if you use AudioSource or PlayOneshot to play sounds. The latter one allows the sound to overlap. Not the only way to achieve that but the easiest one (for me).

Variables for you:

  • foostepsAAOn – if you are on splatmap and go from one texture to another it will play two sounds at once: current one and the last texture one with half of the volume. It is not applied to the objects, just splatmap textures.
  • footstepsDelay – an integer value of delay between succeding footsteps. It is not time value so you need to experiment which suits you best. The script detects the speed of movement thanks to controller.velocity.magnitude so if you run footsteps play faster.

footstepsSound.cs:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;


// [RequireComponent(typeof(AudioSource))]

public class footstepsSound : MonoBehaviour
{
// THIS VARIABLE ARE FOR YOU
	bool footstepsAAOn = true; //SET TRUE IF YOU PREFER TO TURN FOOTSTEPS ANTIALIASING ON OR OFF.
	int footstepsDelay = 150;  // FOOTSTEPS PLAY SPEED - THE SMALLER VALUE, THE QUICKER THE FOOTSTEPS ARE

// OTHER VARIABLES
	List <footstepsSoundClass> Footsteps = new List <footstepsSoundClass> ();
	int 		footstep_index;
	float		footstepsInterval;
	bool 		playNextStep;
	footstepsSoundClass sSoundPrev;
	CharacterController controller;

// SPLATMAP CHECKER PART
	float 		alphamapResolution;
	float[,,] 	terrainAlphamaps;
	int 		splatCount;
	bool 		doCheckWhatsUnderTheFoot;
	int 		textureIndex;

/* HERE YOU ASSIGN CORRESPONDING SOUND PATHS AND TAGS FOR TEXTURES AND COLLIDING OBJECTS. AND A FEW MORE PARAMETERS:
Volume: is a float value from 0 to 1.
Play Oneshot is a value wheter to treat sound as oneshot.
*/

	void Start () {
//											Volume, Play Oneshot,Directory Path	,	Tag , Texture Index (-1 for NonTexture)
		Footsteps.Add (new footstepsSoundClass (0.7F , 	false,	"Sounds/Footsteps/ground"	, 	"Groundtag"	, 	 0	));
		Footsteps.Add (new footstepsSoundClass (0.7F , 	true ,	"Sounds/Footsteps/water"	,	"Water"		, 	-1	));
		Footsteps.Add (new footstepsSoundClass (1F   , 	false,	"Sounds/Footsteps/stones"	, 	"Stones"	,	 1	));
		Footsteps.Add (new footstepsSoundClass (0.55F, 	false, 	"Sounds/Footsteps/sand"		, 	"Sand"		, 	 3	)); 
		Footsteps.Add (new footstepsSoundClass (0.65F, 	false, 	"Sounds/Footsteps/grass_dry",	"Grass_Dry"	, 	 5	));
		Footsteps.Add (new footstepsSoundClass (0.85F, 	false, 	"Sounds/Footsteps/wood1"	, 	"Wood1"		, 	-1	));
		Footsteps.Add (new footstepsSoundClass (0.85F, 	false, 	"Sounds/Footsteps/wood2"	, 	"Wood2"		, 	-1	));
		foreach (footstepsSoundClass snd in Footsteps) { 
			snd.load (); 
		}

		playNextStep = true;
		doCheckWhatsUnderTheFoot = true;
		controller = GetComponent<CharacterController> ();

// SPLATMAP CHECKER PART
		terrainAlphamaps = Terrain.activeTerrain.terrainData.GetAlphamaps (0, 0, Terrain.activeTerrain.terrainData.alphamapWidth, Terrain.activeTerrain.terrainData.alphamapHeight);
		alphamapResolution = Terrain.activeTerrain.terrainData.alphamapResolution;
		StartCoroutine ("UnderFootIs");
	}

// WHICH FOOTSTEPS SET TO PLAY
	
	void OnControllerColliderHit (ControllerColliderHit hit) {
// CHECKING SURFACE (TEXTURES in splatmap) 
		while (doCheckWhatsUnderTheFoot) {
				UnderFootIs ();
		}
		footstepsInterval += controller.velocity.magnitude;
		if (footstepsInterval > footstepsDelay) {
			footstepsInterval = 0;
			playNextStep = true;
		}

// BASED ON OBJECTS COLLISIONS
		if (playNextStep == true 
			&& controller.isGrounded 
			&& controller.velocity.magnitude < 14 
			&& controller.velocity.magnitude > 3) {
// CHECKING WHAT TO PLAY
			foreach (footstepsSoundClass footsteps in Footsteps) {
				if (hit.gameObject.tag == footsteps.tag) {
					if (footsteps.playOneshot == false) {
						PlayFootsteps (footsteps);
					} else {
						PlayFootstepsOneshot (footsteps);
					}
						break; 
					} else if (hit.gameObject.tag == "Ground" && textureIndex == footsteps.ind && footsteps.ind >= 0) {
						PlayFootstepsTerrain (footsteps);
						break;
				}
			}
		}
	}

// PLAY FOOTSTEPS
	void PlayFootsteps (footstepsSoundClass sSound) {

		if (footstep_index >= sSound.soundsArray.Length) {	
			footstep_index = 0;
			table_shuffler (sSound.soundsArray);
		}
		playNextStep = false;
		GetComponent<AudioSource>().clip = sSound.soundsArray [footstep_index++];
		GetComponent<AudioSource>().volume = Random.Range (sSound.Volume - 0.1F, sSound.Volume);
		GetComponent<AudioSource>().pitch = Random.Range (0.97F, 1.03F);
		GetComponent<AudioSource>().Play ();
	}

	void PlayFootstepsTerrain (footstepsSoundClass sSound)	{
		if (footstep_index >= sSound.soundsArray.Length) {
			footstep_index = 0;
			table_shuffler (sSound.soundsArray);
		}
		playNextStep = false;
		GetComponent<AudioSource>().clip = sSound.soundsArray [footstep_index++];
		GetComponent<AudioSource>().volume = Random.Range (sSound.Volume - 0.1F, sSound.Volume);
		GetComponent<AudioSource>().pitch = Random.Range (0.97F, 1.03F);
		GetComponent<AudioSource>().Play ();

		if (sSoundPrev != sSound && sSoundPrev != null && footstepsAAOn) {
			GetComponent<AudioSource>().PlayOneShot (sSoundPrev.soundsArray [Random.Range (0, sSoundPrev.soundsArray.Length)], sSoundPrev.Volume / 2F);
		}
		sSoundPrev = sSound;
	}
// PLAY WATER FOOTSTEPS AND THE OTHER, LONGER SOUNDS.
	void PlayFootstepsOneshot (footstepsSoundClass sSound) {
		if (footstep_index >= sSound.soundsArray.Length) {
			footstep_index = 0;
			table_shuffler (sSound.soundsArray);
		}
		playNextStep = false;
		GetComponent<AudioSource>().volume = 1;
		GetComponent<AudioSource>().PlayOneShot (sSound.soundsArray [footstep_index++], sSound.Volume);
	}
// SPLATMAP (TEXTURE) CHECKER
	void UnderFootIs () {
		doCheckWhatsUnderTheFoot = false;
		for (int splatCount = 0; splatCount < terrainAlphamaps.GetLength(2); splatCount++) {
			float textureStrength = terrainAlphamaps [(int)gameObject.transform.position.z * 2, (int)gameObject.transform.position.x * 4, splatCount];
			if (textureStrength > 0.4) {
					textureIndex = splatCount;
			}
		}
		StartCoroutine ( WaitWithSplatCheck () );
	}

	IEnumerator WaitWithSplatCheck () {
		yield return new WaitForSeconds (0.2F);
		doCheckWhatsUnderTheFoot = true;
	}

	void table_shuffler (AudioClip[] table_to_shuffle) {
		for (int g = 0; g < table_to_shuffle.Length; g++) {
			AudioClip transfer = table_to_shuffle [g];
			int indexer = Random.Range (g, table_to_shuffle.Length);
			table_to_shuffle [g] = table_to_shuffle [indexer];
			table_to_shuffle [indexer] = transfer;
		}
	}
	
} // The END

and footstepsSoundClass.cs

using UnityEngine;
using System.Collections;

public class footstepsSoundClass
{
	public string SoundPath;
	public AudioClip[] soundsArray;
	private float volumeVal;
	int[] SoundIndex;
	public string tag;
	public int ind;
	public bool playOneshot;

	public footstepsSoundClass (float sVolume, bool splayOneshot, string sSoundsPath, string sTag, int sInd) {
		if (sVolume > 1 || sVolume < 0) {
			Debug.Log ("Volume is out of range 0-1");
		}
		volumeVal = sVolume;
		SoundPath = sSoundsPath;
		tag = sTag;
		ind = sInd;
		playOneshot = splayOneshot;
	}

	public void load () {
		soundsArray = new AudioClip[Resources.LoadAll<AudioClip> (SoundPath).Length];
		SoundIndex = new int[soundsArray.Length];
		soundsArray = Resources.LoadAll<AudioClip> (SoundPath);
		for (int i = 1; i < soundsArray.Length; i++) {
			SoundIndex [i] = i;
		}
	}

	public float Volume {				
		get {
			return volumeVal;
		}
		set { 
			if (value > 1 || value < 0) {
				Debug.Log ("Volume is out of range 0-1");
			}
			volumeVal = value;
		}
	}

} // THE END

or get it from GitHub: https://github.com/n9r/FootstepsSoundsForUnity3Dengine

Leave a Reply