Why does my game freeze inside coroutine? - unity3d

sorry if this question is very basic. I created a coroutine that detects collisions every 5 seconds. Inside this coroutine I would like to execute an animation for 3 seconds so I came up with this bit of code:
private IEnumerator OnCollisionStay2D(Collision2D collision)
{
if (collision.collider.tag == "mama") {
//interrupt movement
float currentTime = Time.time;
float madBegin = Time.time;
while (currentTime - madBegin < 3)
{
personAnimator.runtimeAnimatorController = Resources.Load("Animations/human1mad_0") as RuntimeAnimatorController;
currentTime = Time.time;
Debug.Log(currentTime);
}
isAngry = true;
}
yield return new WaitForSeconds(5);
}
I logged the elaped time in the while loop and currentTime does not seem to be updating. As a result, the elapsed time equals 0 and my while loop never exits. Furthermore since this is executing in a coroutine that is supposed to run concurrently I don't get why a nonexiting while loop would freeze the entire game.
Clarification is much appreciated.

From the Unity docs on Time.time
The time at the beginning of this frame (Read Only)
This means that your Time.time will not change over the course of your loop so the difference between currentTime and madBegin will always be 0.
Time.realtimeSinceStartup may be what you're looking for.

Related

I was wondering why my countdown timer is as I will describe

This is just a portion of my code, and I hope will be easy enough to understand. I found a way to "fix" it, but I still don't understand this:
I set my float countDownTime to 2f. In DisplayTime(), i thought the do-while loop would count from 2 down to 0, but instead starts counting down from 0 to negative numbers. I thought that it would count down and stop when countDownTime reaches 0, as assigned in the while(countDownTime >= 0) but it continues beyond that. Thanks for any feedback or assistance.
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space) && !hasStarted)
{
hasStarted = true;
StartGame();
}
DisplayTime();
}
void DisplayTime()
{
if (timerStart)
{
do
{
countDownTime -= Time.deltaTime;
} while (countDownTime >= 0);
timer2.text = Math.Round(countDownTime, 2).ToString();
}
}
I made changes to it, which is my fix:
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space) && !hasStarted)
{
hasStarted = true;
StartGame();
}
DisplayTime();
}
void DisplayTime()
{
if (timerStart && countDownTime >= 0)
{
do
{
countDownTime -= Time.deltaTime;
} while (countDownTime >= 2);
timer2.text = Math.Round(countDownTime, 2).ToString();
}
}
First off, I don't see where you're initially setting countDownTime to 2.
Second, countDownTime will more likely than not stop on a negative number rather than zero. This is because deltaTime is a measure of the ms that have passed since the last update, and varies quite a bit. So you should round it back up 0 if you don't want to be shown as negative.
Finally, I think you want to execute this code as a Coroutine, otherwise the entire loop will execute in a single frame, which is most likely not what you want.
IEnumerator DisplayTime() {
countDownTime = 2;
while (countDownTime > 0) {
countDownTime -= Time.deltaTime;
timer2.text = Math.Round(countDownTime, 2).ToString();
yield return new WaitForEndOfFrame();
}
}
void Start() {
// start coroutine
StartCoroutine(DisplayTime);
}
If you did not want a coroutine, and wanted to called it each frame from your Update method, you can simply drop the while loop.
void DisplayTime() {
if (timerStart) {
if (countDownTime > 0) {
countDownTime -= Time.deltaTime;
}
}
timer2.text = Math.Round(countDownTime, 2).ToString();
}
The benefit of the Coroutine method is it usually easier to manage. Rather than calling a method every time from Update, you can call it once and just let it run. Since the looping is confined to the method itself you can use DisplayTime to set the initial value of countDownTime to 2.
You need to consider 2 factors.
Update() Method is called on every frame. You are executing do-while loop in DisplayTime() which is called from Update() so do while loop will execute many time (once per frame).
do-while loop works differently from normal while loop. do-while loop first executes code inside do{} and only after checks condition and breaks loop if condition is false. so (even if condition is false) code inside do{} will run at least once once. In case of normal while if condition is false code inside loop will not run.
So in your case on first frame countDownTime is subscribed until it will become be less than 0. (So on screen you immediately see 0 instead of 2, because after first frame its
already 0).
after 1st frame on each frame when do-while is executed code inside do is executed only once and than loop immediately breaks because condition countDownTime >= is already false. So after 1st frame countDownTime -= Time.deltaTime;this code gets executed once per frame (without looping multiple times in do-while), that is why it works "correctly" afterwards.

Recursive spawner instantiating way too many gameobjects

I'm trying to make a recursive function that will instantiate a bird at a random interval of time between a min and max value, and then call itself so that the process starts over and a new bird will be instantiated after another random interval. The problem is that currently after the first random interval passes an infinite (close) number of birds are being instantiated all at once. But I don't see where I went wrong. SpawnBird() has been tested and only instantiates one bird when called on its own.
void Start()
{
minBirdWait = 10;
maxBirdWait = 25;
BirdGenerator();
}
void BirdGenerator()
{
float timer = Random.Range(minBirdWait, maxBirdWait);
Invoke("SpawnBird", timer);
BirdGenerator();
}
I ended up turning it into a coroutine and it works now.
IEnumerator BirdGenerator()
{
while(true)
{
float timer = Random.Range(minBirdWait, maxBirdWait);
yield return new WaitForSeconds(timer);
SpawnBird();
}
}

Unity 2D: How to decrement static int in while loop using deltaTime

In my Player script I set a static int called boost to be used from any script.
I have a few boost pick up coins through out the game, when the player gets one it fills their boost tank by +1
You need a min of 3 boost coins to start boosting in the vehicle. When the player is using boost, the boost will last for about as many seconds as many boost coins collected. Using the boost should decrement the boost tank.
Player Script
public static int boost;
private void OnTriggerEnter2D(Collider2D otherObject)
{
if (otherObject.tag == "Boost")
{
boost++;
GetComponent<AudioSource>().PlaySound(BoostSound, 5.7F);
Destroy(otherObject.gameObject);
}
}
Button call code
public void BoostButton()
{
StartCoroutine("Use_VehicleBoost");
}
Booster code that a button calls.
IEnumerator Use_VehicleBoost()
{
// Check which boost package player picked
int boostLevel = SecurePlayerPrefs.GetInt("BoostLevel");
if (boostLevel == 0)
{
Debug.Log("Boost Level: None");
yield return null;
}
else if (boostLevel == 1)
{
Debug.Log("Boost Level: 1");
float aceleRate = 400, maxFWD = -2500;
while (Player.boost >= 3)
{
vehicleController.GetComponent<CarMovement>().accelerationRate += aceleRate;
vehicleController.GetComponent<CarMovement>().maxFwdSpeed += maxFWD;
// Meant to slowly take one point/ Time second away from boost tank // Problem is here ----->>>
Player.boost = Player.boost - Mathf.RoundToInt(Time.deltaTime);
yield return null;
}
if (Player.boost <= 0)
{
yield return null;
}
}
yield return null;
}
Problem is here in this line
Player.boost = Player.boost - Mathf.RoundToInt(Time.deltaTime);
It is supposed to be decrementing with seconds from Player.boost. For example if player collects 3 boost coins then boost is active when used it will last for 3 seconds before turning off.
Not exactly sure on what to do here. They told me that in the while loop the deltaTime stays at value 0 because its stuck in one frame? Am I supposed to start a timer variable? Thank you.
Not exactly sure on what to do here. They told me that in the while
loop the deltaTime stays at value 0 because its stuck in one frame? Am
I supposed to start a timer variable?
Yes, I did say that but you have fixed the one frame issue with yield return null; which let's the loop to wait one frame therefore giving Time.deltaTime chance to change. Even with that being fixed, there is still 0 issue I told you but because while (Player.boost >= 3) is still true. That is true because Mathf.RoundToInt(Time.deltaTime); is returning zero. You can verify this with Debug.Log(Mathf.RoundToInt(Time.deltaTime)).
If Player.boost type is an int, change it to a float type, to gradually decrease it with Time.deltaTime directly:
while (Player.boost >= 3)
{
vehicleController.GetComponent<CarMovement>().accelerationRate += aceleRate;
vehicleController.GetComponent<CarMovement>().maxFwdSpeed += maxFWD;
// Meant to slowly take one point/ Time second away from boost tank
Player.boost = Player.boost - Time.deltaTime;
//OR Player.boost -= Time.deltaTime;
yield return null;
}
If Player.boost type is an int but you don't want to change it to float, remove the Time.deltaTime as that's used for float values then do the waiting with WaitForSeconds. With this you can subtract one from boost after each WaitForSeconds call.
while (Player.boost >= 3)
{
vehicleController.GetComponent<CarMovement>().accelerationRate += aceleRate;
vehicleController.GetComponent<CarMovement>().maxFwdSpeed += maxFWD;
// Meant to slowly take one point/ Time second away from boost tank
Player.boost = Player.boost - 1;
//OR Player.boost -= 1;
yield return new WaitForSeconds(1f);
}

Trying to program a timer to do something when it reaches 5 seconds

I can't seem to figure out what to do here. I'm creating a learn to type game where if the user doesn't get the word correct in 5 seconds, they lose a life.
Either this could be implemented by counting down to 0 from 5, or counting up to 5.
I tried using the stopwatch for c# system diagnostics, which works for everything except I can't figure out how to check when it hits 5 seconds. It is a time object you cant compare it to an int.
you have two options here.
You can check the time in the Update function with Time.time or Time.deltaTime, or you can use a Coroutine with the new WaitForSeconds(5) object.
It could be as simple as:
float time;
void Update()
{
time += Time.deltaTime;
if(time > 5)
{
LoseALife();
}
}
If you need to restart the timer each time you complete a word you might have an aditional method like:
public void RestartTimer()
{
time = 0;
}
This would make the timer go back to 0 any moment you need it.

When pausing game(using a bool and time.timescale) it doesn't pause the score counter

this will be my first question so go on easy on me please ;)
I'm building my first game in Unity using my limited knowledge, tutorials and troubleshooting on google but i can't seem to fix this issue
i have a script that counts score(with a GUIText) and another one to pause the game(using timescale) and you probably guessed it already but when i pause the game it doesn't pause the score.
Script for Pausing:
var isPaused : boolean = false;
function Update()
{
if(Input.GetKeyDown("p"))
{
Pause();
}
}
function Pause()
{
if (isPaused == true)
{
Time.timeScale = 1;
isPaused = false;
}
else
{
Time.timeScale = 0;
isPaused = true;
}
}
Script for Score:
var Counter : int = 100000;
var Substractor : int = 0;
var Score : int = Counter - Substractor;
function Update (
) {
Score--;
guiText.text = "Score: "+Score;
}
the score script is attached to a gameobject with a gui text and the script for pausing the game is attached to the player
another issue is that when i'm moving(using the arrow keys) then press pause and then unpause the player moves faster and unpredictable for a splitsecond but this is only when i press pause while pressing the arrow buttons when i only press pause there is no issue, this is a small bugg that i'l try to fix myself, just thought i'd add it here for those that have an easy answer to this issue aswell
I only know this problem from other game frameworks and I've got no experiance with unity. But it should work the same way for unity as it does in other frameworks. You have two options:
Update is called after every rendering frame! So your timescale doesn'T influence how often update is called. Instead you have to find some dt or deltaT (don't know how it is called in unity and how to get it, but I mean the time, since unities the last call to update, please tell me in the comments, so I can edit this answer).
Then don't calculate your score like:
Score--;
but use something like:
Score -= 1 * dt
or
Score -= 1 * dt * Time.timeScale
(depending how exactly unity works)
alternatively you can sourround your update-block with some if-statement:
in Score:
function Update (
) {
if (! isPaused()) { // not sure how this function / property is called in unity
Score--;
guiText.text = "Score: "+Score;
}
}