- Home /
Playing footsteps with interval
using UnityEngine;
using System.Collections;
public class AdvancedFootsteps : MonoBehaviour
{
// character controller reference
CharacterController characterController;
// script referencec
tpAnimator tpanimator;
// audio source
AudioSource audio;
//Audio clips
public AudioClip woodStep;
public AudioClip waterStep;
public AudioClip metalStep;
public AudioClip grassStep;
public AudioClip dirtStep;
public AudioClip stoneStep;
public AudioClip sandStep;
// intervals between steps
float runningInterval = 0.2f;
float walkingInterval = 0.5f;
float crouchingInterval = 1f;
/** step volumes
float runningVolume;
float walkingVolume;
float crouchingVolume; **/
bool playsteps;
void Awake ()
{
characterController = GetComponent<tpController> ().characterController;
tpanimator = GetComponent<tpAnimator> ();
audio = GetComponent<AudioSource> ();
}
void Start ()
{
playsteps = true;
StartCoroutine ("PlayFootsteps");
}
void Update ()
{
Debug.Log ("Update");
if (characterController.isGrounded && !playsteps && characterController.velocity.magnitude > 0.2f) {
playsteps = true;
StartCoroutine ("PlayFootsteps");
}
if (!characterController.isGrounded && playsteps) {
playsteps = false;
StopCoroutine ("PlayFootsteps");
}
}
IEnumerator PlayFootsteps ()
{
float vel = characterController.velocity.magnitude;
RaycastHit hit;
string floorTag;
if (characterController.isGrounded && vel > 0.2f) {
if (Physics.Raycast (transform.position, Vector3.down, out hit)) {
floorTag = hit.collider.gameObject.tag;
if (floorTag == "Wood") {
audio.clip = woodStep;
} else if (floorTag == "Water") {
audio.clip = waterStep;
} else if (floorTag == "Metal") {
audio.clip = metalStep;
} else if (floorTag == "Terrain") {
// check texture and get correct clip
int t = GetMainTexture (transform.position);
switch (t) {
case 0:
// grass
audio.clip = grassStep;
break;
case 1:
// dirt
audio.clip = dirtStep;
break;
case 2:
// stone
audio.clip = stoneStep;
break;
case 3:
// gravel
audio.clip = stoneStep;
break;
case 4:
// sand
audio.clip = dirtStep;
break;
case 5:
// wet mud
audio.clip = dirtStep;
break;
case 6:
// branches
audio.clip = stoneStep;
break;
case 7:
// snow
audio.clip = stoneStep;
break;
case 8:
// concrete
audio.clip = dirtStep;
break;
}
}
// Play the correct sound
audio.PlayOneShot (audio.clip);
if (tpanimator.isRunning) {
yield return new WaitForSeconds (runningInterval);
} else if (tpanimator.isCrouching) {
yield return new WaitForSeconds (crouchingInterval);
} else {
yield return new WaitForSeconds (walkingInterval);
}
playsteps = false;
} else {
yield return null;
}
}
}
}
Edit: working script, i will not take credit for most of the code since it is a combination of scripts found on this site.
Answer by siaran · Mar 20, 2015 at 08:27 PM
that's a lot of else-if statements you have there, consider at least turning those into a switch.
anyway, onto your problem
You are calling audio.PlayOneShot inside update, so it will play every frame. Yes, you're calling it after StartCoroutine, but thats not how that works. Also starting a coroutine in every update frame is generally not a great idea...instead you will want do something like
void Start(){
StartCoroutine(Footsteps());
}
IENumerator Footsteps(){
audio.PlayOneShot(clip);
yield return new WaitForSeconds(interval);
StartCoroutine(Footsteps());
}
Of course this gives you the problem that you cannot stop your Footsteps sounds, but thankfully there is http://docs.unity3d.com/ScriptReference/MonoBehaviour.StopCoroutine.html
so you will get something like
IENumerator coroutine;
bool playsteps;
void Start(){
playsteps = true;
coroutine = Footsteps();
StartCoroutine(coroutine);
}
void Update(){
//controller is grounded and moving but we are not playing footsteps
if(characterController.isGrounded && !playsteps && velocity > 0.2f){
playsteps = true;
StartCoroutine(coroutine);
}
//controller is not grounded but we are playing steps - stop playing
if(!characterController.isGrounded && playsteps){
playsteps = false;
StopCoroutine(coroutine);
}
/*
You;ll want some additional start-stop conditions, like if velocity is
low also stop playing or something.
*/
}
Well, something like that. I've never actually used StopCoroutine so I have no idea if this actualy works. Long story short your problem is that you are starting your audio play and coroutine every frame.
I have used StopCoroutine like that before. I find it useful for loops that you want to stop. I would have done a while loop for the coroutine though
IENumerator Footsteps(){
while(true)
{
audio.PlayOneShot(clip);
yield return new WaitForSeconds(interval);
}
}
Thanks for your answer, are there any advantages on using a switch? Amountwise the code wont be any shorter, more readable tho.
Will try the rest of the answer tomorrow and get back to you.
immersiveGamer, ive tried the while loop but it just crashes unity upon starting the game.
more readable code is an advantage in itself.
switches are also faster than if-else ladders because the compiler is better at optimizing them, although that is probably not a difference you will notice. In which case you should use the more readable option, which in this case is also a switch :)
ah okay, your suggestion worked, added a playstep = false; at the end of the coroutine. Will upload the edited script now if anyone has any use for it :)