How do you execute code every X angles reliably?
How would you execute code every X angles? To provide some context, in the VR game I'm working on, I want to play an audio file every 30 degrees that a crank turns. When using my current code, turning the crank slowly works mostly fine, but turning it quickly results in it missing certain angles, so that it doesn't play the audio file reliably.
Below is a snippet of the code in my Update function (not fixed update).
if(drum.angularVelocity.z < -1)
{
if((int)drum.rotation.eulerAngles.z % 30 == 0)
{
if(shouldPlayClick)
{
if(clickCount > crankClicks.Length -1)
{
clickCount = 0;
}
crankAudio.clip = crankClicks[clickCount];
crankAudio.Play();
clickCount++;
shouldPlayClick = false;
}
}
else
{
shouldPlayClick = true;
}
}
Any ideas? Am I going about the problem wrong, or is there something I can add to my code to make it more reliable?
You could check if the old angle is smaller but the new angle is bigger than the angle. I don't know how to do this though...
Answer by Iseeicy · Jun 27, 2016 at 07:09 PM
Well, after a bit of tinkering and some debugging, I realized that @jdean300 's solution works incredibly well as long as you change the if statement to check if the absolute value of zRotCounter is greater than zRotLimit, like so:
zRotCounter += handle.rotation.eulerAngles.z - lastEulerZ;
lastEulerZ = handle.rotation.eulerAngles.z;
if (Mathf.Abs(zRotCounter) > zRotLimit)
{
zRotCounter = 0;
crankAudio.Play();
}
Thank you for your help everyone!!
Answer by jdean300 · Jun 26, 2016 at 06:52 AM
You could track the changes to the angle each frame, then add that change to a counter variable. Anytime that counter gets over 30, reset it to zero and play the sound. Something like this:
private float zRotCounter = 0f;
private float zRotLimit = 30f;
private float lastEulerZ; //initialize this to starting z rotation
private void Update()
{
var dif = (drum.rotation.eulerAngles.z - lastEulerZ);
lastEulerZ = drum.rotation.eulerAngles.z;
zRotCounter += dif;
if (zRotCounter > zRotLimit){
zRotCounter = 0f;
//play sound
}
}
You'll have to deal with negative rotations correctly.
The other way to handle this would be keep track of the last angle the audio was played at, then on every update check the current angle with the last angle the audio was played at. If they are greater than 30 degrees apart, play the sound and set the last angle to the current angle. In fact this might be the better way to do it - would be easier to handle negatives.
You shouldn't reset the counter to 0 as you will scrap the amount that is larger than your limit. It's usually better to simply subtract the limit from the counter. That will preserve the remaining amount.
if (zRotCounter > zRotLimit){
zRotCounter -= zRotLimit;
//play sound
}
I considered that. You may or may not want to play the sound multiple times in quick succession - possibly one update after another - if the crank is turned extremely fast. Both ways are valid, it just depends on exactly what you're doing and what effect you get from each.
So, after trying both solutions, I still cant get it to work. When using @jdean300 and @Bunny83's answers, when I turn the crank, a lot of clicks are played at the same time, and then are never played again. At first I thought it could have somehow been an error with my AudioClip array (crankClicks[]), but I double checked by having it just use a single audio clip ins$$anonymous$$d and it behaved the same. Could it be something on my side?
EDIT: Actually, jdean300's version just plays a single click then does nothing.