Rotate object without coroutine? - unity3d

I'm currently working on a Time of Day system, and in that system I want to be able to control at which hour SUNRISE starts, DAY start, SUNSET starts and NIGHT starts. At the moment this is possible in the system and when the "clock" hits the next start hour I start a Coroutine to start the rotating of the sun and moon.
My coroutine looks like this:
IEnumerator RotateSun(Vector3 fDegrees, float SecondsDuringTimeSet)
{
Quaternion quaFromPosition = _lSunLight.transform.rotation;
Quaternion quaToPostion = Quaternion.Euler(fDegrees);
for (float t = 0.0f; t < 1; t += Time.deltaTime / SecondsDuringTimeSet)
{
_lSunLight.transform.rotation = Quaternion.Lerp(quaFromPosition, quaToPostion, t);
yield return null;
}
}
And as you see I pass values into that for each time I run it. This is done like this:
if (_fCurrGameHour == _fTimeStartSunrise && _bMoveSunAndMoonOnlyOnce == true)
{
// Which TIMESET should it be:
_en_CurrTimeSet = TimeSet.SUNRISE;
// How long should the SUN move and how many which degree should it reach before that time ends
vec3MoveSun = new Vector3(10.0f, 0, 0);
StartCoroutine(RotateSun(vec3MoveSun, _fConvertSunriseToGTSeconds));
// How long should the MOON move and how many which degree should it reach before that time ends
vec3MoveMoon = new Vector3(180.0f, 0, 0);
StartCoroutine(RotateMoon(vec3MoveMoon, _fConvertSunriseToGTSeconds));
// Tell bool that we don't need to move the sun and moon more then once
_bMoveSunAndMoonOnlyOnce = false;
}
And as I wrote this system works at the moment. However it is kind of a static system. So at the moment I need to make sure that the starting hour of the game is the sunrise hour. Otherwise my coroutine would break since it would not rotate to the correct position before the day start hour. And I can definitly not start the game close to sunset then the sun rotates the complete wrong way since it's closer to rotate the wrong way then (I assume).
So my first question is: Could I somehow make the system more dynamic? I want to be able to still set at which hours SUNRISE, DAY, SUNSET and NIGHT should start so during different season for example I could have different lengths on my day. I want to be able to change these hours in the game and then if the SUNSET would be set later the sun would start to rotate a little slower since it is supposed to take longer to reach it's SUNSET position.
Onto my second question: Would it also be possible to rewrite my coroutine so I could start the game at any hour I want and the sun would still start at the right degree rotation?
The sun will always set at the same rotation (170 degrees) and rise at the same rotation (350 degree). I just want to control the time it takes before it reaches to those rotations. Maybe I could somehow move this to my update phase instead of using a Coroutine for this? I have no clue how to change this in my system so if anyone have any ideas. Please help.

You can get away from the coroutine by using a State design pattern.
public int SunRise = 8;
public int SunSet = 15;
public Quaternion RiseRotation;
public Quaternion SetRotation;
public int StartHour = 8;
float time; // our time on a 24 hr system
bool wasSun; // what was our previous state?
void Start()
{
GoToHour(StartHour);
ToggleState(IsSun());
}
void Update()
{
bool isSun = IsSun();
if (isSun != wasSun)
ToggleState(isSun);
time = (time + Time.deltaTime) % 24; // wrap at 24 hrs back to 0
if (isSun)
UpdateSun();
else
UpdateMoon();
}
bool IsSun()
{
return time >= SunRise && time < SunSet
}
void UpdateSun()
{
float t = (time - SunRise) / (SunSet - SunRise);
UpdateRotation(_lSunLight.transform, RiseRotation, SetRotation, t);
}
void UpdateMoon()
{
float t;
if (time >= SunRise)
t = time - SunRise;
else
t = time + (24 - SunSet);
t = t / ((24 - SunSet) + SunRise));
UpdateRotation(_lMoonLight.transform, RiseRotation, SetRotation, t);
}
void GoToHour(int hour)
{
t = hour / 24;
}
void ToggleState(bool isSun)
{
if (isSun)
{
// Custom logic here, turn on sun, turn off moon?
}
else
{
// Custom logic here, turn on moon, turn off sun?
}
wasSun = isSun;
}
static void SetRotation(Transform target, Quaternion start, Quaternion end, float t)
{
target.rotation = Quaternion.Lerp(start, end, t);
}
Completely untested code, but this is a base idea for how you can use a class to update either the sun or the moon. This is a binary implementation, so either the sun is showing, or the moon is. If you want to have overlap, you can of course customize it do that easily. You will want different hours for the moon rise and moon set, and then not if/else the moon and sun update.
The code is also pretty basic and makes a lot of assumptions (sunrise never overlaps the 24 hour mark.) All of these can be cleaned up. You can also create variables to store calculations that are done each frame (regarding the duration of the sunrise/set) at your discretion. If you want to do that, just set it in the GoToHour method.
The SetRotation isn't super necessary, but it serves to keep the code clean. If you later decide you want to use a Quaternion.Slerp, you only have to change it in one place.
I exposed Quaternions in the inspector, you may rather using just a float for degrees, or a Euler Vecter3. If you do either, you can just do a conversion in the Start.
I hope this answered all your questions, if not, feel free to clarify on what isn't answered.

Related

Timer drags a bit when too much is happening

I'm working on a timer that needs to do some calculations and run some functions at a certain interval. I'm trying to keep the interval as big as possible, but I need it to be kind of fine grained.
Here is the periodic timer with some of the stuff that needs to happen.
So as you can see, every second (the milliseconds passed % 1000 == 0) it will do some stuff if some conditions are met. But also every 10 milliseconds I need to check some stuff.
It seems this is a bit too much, and after running the timer for 2 minutes it already drags 1 second behind. I guess I'm approaching this the wrong way. Could/should I somehow put all that logic in a function that just runs async so the timer can just keep going.
It's not the end of the world if the timer display drags for a few milliseconds every now and then, if it catches up later. But now the whole timer just drags.
_timer = Timer.periodic(Duration(milliseconds: 10), (timer) {
passedMilliseconds = passedMilliseconds + 10;
// METRONOME BEEP AND BLINK
if (passedMilliseconds % currentTimerSettingsObject.milliSecondDivider == 0) {
_playMetronomeBeep();
_startMetronomeBlink();
}
// ONE SECOND
if (passedMilliseconds % 1000 == 0) {
secondsDuration--;
// COUNTDOWN
if (secondsDuration < currentTimerSettingsObject.countDown + 1) {
_player.play('sounds/beep.mp3');
}
// SET COUNTDOWN START VALUES
if (secondsDuration == currentTimerSettingsObject.countDown) {
isCountdown = true;
}
notifyListeners();
}
// TIME IS UP
if (secondsDuration < 0) {
switchToNextTimer();
notifyListeners();
}
});
}
You cannot rely on a timer to deliver events exactly on time. You need to use a more exact method than simply incrementing a counter by 10 on every tick. One example would be to start a Stopwatch before the timer and then (knowing that your ticks will only be on approximately 10ms intervals) read stopwatch.elapsedMilliseconds and base your decisions on that.
You will need to change your logic a bit. For example, you want to know when you pass a 1 second boundary. Previously, with your exact increments of 10 you knew you would eventually reach a round 1000. Now, you might see 995 followed by 1006, and need to deduce that you've crossed a second boundary to run your per second logic.

Coroutine Countdown Timer being extremely slow

Hey guys so just as a disclaimer I'm relatively new to programming so if I'm making some super obvious mistake please go easy on me
So I'm trying to create a higher customizable Countdown timer for my game, and I want it to be able to be accurate to 0.01 Seconds. I decided I would use the Coroutine method for creating my timer instead of the delta-time one I have seen a couple of times, thinking that this would be a more efficient approach. My game is not very intensive and thus easily runs on hundreds of frames per second, so I thought that using Waitforseconds(0.01) is going to work better because it only needs to be called 100 times every second rather than multiple hundreds. however, I have come into a major issue with my timer. It is EXTREMELY slow. I ran the countdown timer on google and mine side by side starting at 25 seconds and it beat mine out by ten seconds. I even tried adding a artifical delay thinking the waitforseconds function was overshooting, so I would have the time tick down 0.01 seconds when a bit less then that had passed, but my results ended up being sort of inconsistent. Here is my code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class TimerScript : MonoBehaviour
{
public Text textDisplay;
private double secondsLeft = 30;
public bool takingAway = false;
private string Texttodisplay;
public int Milisecondsdigits = 2;
void Start()
{
textDisplay = GetComponent<Text>();
Texttodisplay = "00:" + secondsLeft;
if(Milisecondsdigits == 0)
{
Milisecondsdigits = -1;
}
}
void Update()
{
if (takingAway == false && secondsLeft > 0)
{
StopAllCoroutines();
StartCoroutine(TimerTake());
}
if(Texttodisplay.Length > 8 - (Mathf.Abs(Milisecondsdigits -2)))
{
Texttodisplay = Texttodisplay.Substring(0,8- (Mathf.Abs(Milisecondsdigits -2)));
}
textDisplay.text = Texttodisplay;
}
IEnumerator TimerTake()
{
takingAway = true;
yield return new WaitForSeconds(0.01f);
secondsLeft -= 0.01;
if(secondsLeft < 10)
{
Texttodisplay = "00:0" + secondsLeft;
}
else
{
Texttodisplay = "00:" + secondsLeft;
}
takingAway = false;
}
}
could somebody please let me know how I could cause this to become more accurate or why it's acting extremely inaccurate currently :/
Coroutine events like WaitForSeconds trigger at a defined point in Unity's event cycle, which takes place after Update() is processed (see
https://docs.unity3d.com/Manual/ExecutionOrder.html). This defined execution point might not line up exactly with the timer delay. This means it may wait longer than you want it to.
In your example, you tell it to wait for 0.01 seconds. Let's say that you are running a game at 30 frames per second. The frame time for 30 frames per second is 1/30 seconds, or approximately 0.0333 seconds. WaitForSeconds will then wait for the next frame, and 0.0333 seconds passes until the next frame. Then the next WaitForSeconds event cycle, it sees that the delay has passed and triggers, but you actually waited over 3 times as long as you wanted to because of the delay between event cycles. Since your code assumes that WaitForSeconds had only waited for 0.01 seconds, you will end up waiting longer than you intended to. This normally doesn't matter in a lot of applications, but when accumulating with frequent short delays it certainly does.
To solve this, you have two choices:
Accumulate time manually using Time.deltaTime in Update().
Coroutines likely check their completion status per frame in a
similar way in the yield WaitForSeconds event. If coroutines
check if they need to continue every frame, doing this manually with
Time.deltaTime might not be any less efficient at all than a
coroutine. You will have to benchmark to find out, because coroutines being more efficient isn't a safe assumption.
Use Time.time (or Time.realtimeSinceStartup if you want it unscaled) to measure the actual span of time that elapsed after the WaitForSeconds trigger, and use that as what you subtract from your remaining time.
There is also an additional consideration here that if you want your
text display to update at specific regular intervals, you will want
to dynamically adjust what value you pass into WaitForSeconds to
compensate for its drift.
Do you try to do it without Corroutine in a fixed update? Fixed update refresh every 0.02 seconds by default but you can settup to run in 0.01 seconds in Edit > Settings > Time > Fixed Timestep.
Replace the corroutine with a function in FixedUpdate
There is a link with better explation how works fixedupdate.
FixedUpdate Unity

In Unity, how to segment the user's voice from microphone based on loudness?

I need to collect voice pieces from a continuous audio stream. I need to process later the user's voice piece that has just been said (not for speech recognition). What I am focusing on is only the voice's segmentation based on its loudness.
If after at least 1 second of silence, his voice becomes loud enough for a while, and then silent again for at least 1 second, I say this is a sentence and the voice should be segmented here.
I just know I can get raw audio data from the AudioClip created by Microphone.Start(). I want to write some code like this:
void Start()
{
audio = Microphone.Start(deviceName, true, 10, 16000);
}
void Update()
{
audio.GetData(fdata, 0);
for(int i = 0; i < fdata.Length; i++) {
u16data[i] = Convert.ToUInt16(fdata[i] * 65535);
}
// ... Process u16data
}
But what I'm not sure is:
Every frame when I call audio.GetData(fdata, 0), what I get is the latest 10 seconds of sound data if fdata is big enough or shorter than 10 seconds if fdata is not big enough, is it right?
fdata is a float array, and what I need is a 16 kHz, 16 bit PCM buffer. Is it right to convert the data like: u16data[i] = fdata[i] * 65535?
What is the right way to detect loud moments and silent moments in fdata?
No. you have to read starting at the current position within the AudioClip using Microphone.GetPosition
Get the position in samples of the recording.
and pass the optained index to AudioClip.GetData
Use the offsetSamples parameter to start the read from a specific position in the clip
fdata = new float[clip.samples * clip.channels];
var currentIndex = Microphone.GetPosition(null);
audio.GetData(fdata, currentIndex);
I don't understand what exactly you convert this for. fdata will contain
floats ranging from -1.0f to 1.0f (AudioClip.GetData)
so if for some reason you need to get values between short.MinValue (= -32768) and short.MaxValue(= 32767) than yes you can do that using
u16data[i] = Convert.ToUInt16(fdata[i] * short.MaxValue);
note however that Convert.ToUInt16(float):
value, rounded to the nearest 16-bit unsigned integer. If value is halfway between two whole numbers, the even number is returned; that is, 4.5 is converted to 4, and 5.5 is converted to 6.
you might want to rather use Mathf.RoundToInt first to also round up if a value is e.g. 4.5.
u16data[i] = Convert.ToUInt16(Mathf.RoundToInt(fdata[i] * short.MaxValue));
Your naming however suggests that you are actually trying to get unsigned values ushort (or also UInt16). For this you can not have negative values! So you have to shift the float values up in order to map the range (-1.0f | 1.0f ) to the range (0.0f | 1.0f) before multiplaying it by ushort.MaxValue(= 65535)
u16data[i] = Convert.ToUInt16(Mathf.RoundToInt(fdata[i] + 1) / 2 * ushort.MaxValue);
What you receive from AudioClip.GetData are the gain values of the audio track between -1.0f and 1.0f.
so a "loud" moment would be where
Mathf.Abs(fdata[i]) >= aCertainLoudThreshold;
a "silent" moment would be where
Mathf.Abs(fdata[i]) <= aCertainSiltenThreshold;
where aCertainSiltenThreshold might e.g. be 0.2f and aCertainLoudThreshold might e.g. be 0.8f.

Unity - Everything freezes on " yield return new WaitForSeconds(); "?

Ok! all of my code in this scene is in one script and one manager object.
all of it is about 700 lines. so I can't put it here.
I tested different things:
1) switch platform from android to
pc/mac
2) test on a previous version
of unity( previous 2017, and current
on is 2018.1 )
none of them solve the problem.
then I change some part of the code that I suspected to cause the problem. ( none of them solve the solution ).
then I started to put Debug.Log()s everywhere. so I found where it freezes.
Here Is the code:
IEnumerator ShowSigns(int Button1State, int EqualState, int Button2State)
{
Debug.Log("ShowSigns");
if (Button1State == 1)
{
OperationOneCorrectSign.GetComponent<CanvasGroup>().alpha = 1;
}
else if (Button1State == 2)
{
OperationOneIncorrectSign.GetComponent<CanvasGroup>().alpha = 1;
}
if (EqualState == 1)
{
EqualCorrectSign.GetComponent<CanvasGroup>().alpha = 1;
}
else if (EqualState == 2)
{
EqualIncorrectSign.GetComponent<CanvasGroup>().alpha = 1;
}
if (Button2State == 1)
{
OperationTwoCorrectSign.GetComponent<CanvasGroup>().alpha = 1;
}
else if (Button2State == 2)
{
OperationTwoIncorrectSign.GetComponent<CanvasGroup>().alpha = 1;
}
Debug.Log("BeforeWaiting");
yield return new WaitForSeconds(0.3f);
Debug.Log("AfterWaiting");
OperationOneCorrectSign.GetComponent<CanvasGroup>().alpha = 0;
OperationOneIncorrectSign.GetComponent<CanvasGroup>().alpha = 0;
EqualCorrectSign.GetComponent<CanvasGroup>().alpha = 0;
EqualIncorrectSign.GetComponent<CanvasGroup>().alpha = 0;
OperationTwoCorrectSign.GetComponent<CanvasGroup>().alpha = 0;
OperationTwoIncorrectSign.GetComponent<CanvasGroup>().alpha = 0;
state = GameState.CreateNewProblem;
Debug.Log("EndSigns");
}
I found that it freezes on this:
yield return new WaitForSeconds(0.3f);
Very strange!!!
This is a picture of the game.
The game is a simple game that shows 2 math phrase and player should choose the bigger or equal.
The logic is this way:
1) make new phrases and change the game state to "ChooseAnswer"
2) player press one of 3 buttons and the answer checked and score and other things changes and the ShowSigns coroutine will start and ends after 0.3 seconds. and as you see at the end of the coroutine state changes to "CreateNewProblem".
3) in the Update when CreateNewProblem detects, the code call for the NewProblem() function to make new phrases and at the end of that game state changes to "ChooseAnswer".
this logic repeats over and over until time reaches zero.
a "step" variable increase and decrease by 1 by any correct and incorrect answer. and a variable level = steps/10 determines the difficulty of phrases.
the game works correctly on %98 click On buttons. but usually, it freezes somewhere after step 20. In 21, 23, 27, 34 ... very randomly. but always after 20 and some time no freeze until time ends. and always right before yield return. exactly at the same line.
I read many questions and answers but none of them was helpful. I have no while loop, no while(true), as long as I know and check my code no infinite loop, on StopAllCoroutines ... nothing. and I stuck for 2 days.
thanks all of you for helping.
OH,and Here Is the code file
The cause of the freezing is using Random.Range to control a while loop which is in the code linked in your question. One way to get random number without using the while loop is to generate them into List then remove each one you use. This should prevent Random.Range from freezing Unity.

Dial rotation jump

Im working on a dial that rotates when a user touches it and drags it around. So far so good, but when the dial gets to more than 360, the value goes back to 0, making the animation jump backwards around the dial instead of continuing.
dialRotation = (atan2(event->localY()-circleCenterY, event->localX()-circleCenterX) * (180/M_PI));
Does anyone know how to stop it from jumping?
You can use the existing value to determine if you should go beyond 360 or not. Perhaps something like this:
currentValue = dialRotation;
dialRotation = (atan2(event->localY()-circleCenterY, event->localX()-circleCenterX) * (180/M_PI));
dialRotation = 360.0 * floor(fmod(currentValue, 360.0)) + dialRotation;
I think this will work in the negative direction, too, though I sometimes get confused by fmod()s behavior for negative numbers, so be sure to check it.
Another way to do it would be to get the delta (the change) between the previous value and the current value and just add that to what you already have. As long as the difference is not greater than 180 degrees, that might work better. Something like this:
// In your class declaration:
float normalizedRotation; // Always between 0 and 360 degrees
float previousNormalizedRotation;
float dialRotation; // current value, can be any valid value from -inf to +inf
// In your method:
normalizedRotation = (atan2(event->localY()-circleCenterY, event->localX()-circleCenterX) * (180/M_PI));
if (normalizedRotation < 0.0) normalizedRotation += 360.0;
float delta = normalizedRotation - previousNormalizedRotation;
previousNormalizedRotation = normalizedRotation;
dialRotation += delta;
Let me know if that works.