I haven’t done a lot of work with audio in Unity, and I’m having trouble finding relevant information on what I’m trying to accomplish.
I’ve got some moving parts, and I want to play some audio when the part moves, and change the pitch of the sound depending on the motion speed.
The problem is that any inconsistencies in the motion mess with the playback.
So far, I’m getting things to work with ugly hacks - but I don’t like ugly hacks. I’m thinking the must be a better way to do this.
Here’s an example:
I’ve got a turret and a looping mechanical audio source. When the turret rotates, the sound should play and change pitch depending on the rotation rate of the turret.
This part is good in theory, but begins to fail in practice.
Here is a simple bit of rotation code (the most typical way to create it)
Quaternion _rot = Quaternion.LookRotation( target - rotator.transform.position, rotator.transform.up );
rotator.transform.rotation = Quaternion.RotateTowards( rotator.transform.rotation, _rot, _rotationRate * Time.deltaTime );
And here is some starting audio code:
void TurnSound ( AudioSource sound, float pitchMin, float rate, float rateMax ) {
if ( rate > 0f ) {
sound.pitch = pitchMin + (( rate / rateMax ) * (1f - pitchMin));
if ( sound.isPlaying == false ) { // don't repeatedly call Play() if already playing
sound.Play();
}
} else {
sound.Stop();
}
}
The audio will play at a pitch of 1 when at full rotation rate, and as the rate approaches 0, then pitch approaches pitchMin.
Rate is simply:
float rate = Quaternion.Angle( lastRotation, currentRotation ) / Time.deltaTime;
The problem, is that (depending on rotation technique) you get a frame or two where either rate momentarily isn’t 0 when not moving, or rate momentarily is 0 when moving.
In the above example, it’s the latter problem, and this happens every few frames causing the audio to rapidly start and stop causing lots of audio jitter and garbling. (Mainly when the turret isn’t rotating at full speed because it’s tracking a target.)
The workaround seems to be to something like this:
void TurnSound ( AudioSource sound, float pitchMin, float rate, float rateMax ) {
if ( rate > .1f ) { // avoid near 0 values
sound.pitch = pitchMin + (( rate / rateMax ) * (1f - pitchMin));
if ( sound.isPlaying == false ) { // don't repeatedly call Play() if already playing
sound.Play();
}
audioStopTime = Time.time + .1f; // continuously refresh stop time
} else {
if ( Time.time >= audioStopTime ) { // make sure rotation has been stopped for at least .1 seconds
sound.Stop();
}
}
}
This way adds a rate threshold before the sound will begin, and adds a slight delay upon stopping that will gloss over any momentary 0 rate occurrences.
Unfortunately, I’m going to need an audioStopTime for every audio source. The clearest way seems to be to wrap a time variable and the audioSource together in a class or struct.
I feel like the code is becoming a mess for something that should be straightforward.
Maybe I’m just not yet well versed enough with audio.
Any advice?