Coroutine not waiting for yield WaitForSeconds [JS]
I have been working on a Sanity level of a player and I've been using Coroutines to drain 5 sanity every second and that Coroutine I am using is just ignoring WaitForSeconds. I need help.
function SanityDraining(){
if(lanternLight.enabled == false){
StartCoroutine(sanityDrain(1.0));
}else{
StopCoroutine(sanityDrain(1.0));
}
}
function sanityDrain(waitTime : float){
playerSanity = playerSanity - sanityLoss;
yield WaitForSeconds(waitTime);
}
I have called the Sanity Draining function in Update function and its working, everything is working but its just ignoring yield. I have a script that I've done with players light, also using coroutines and it works perfectly. I dont know what seems to be the problem here.
Answer by Dave-Carlile · Sep 15, 2015 at 12:09 PM
Are you expecting the Coroutine to continue draining the sanity over time? That's what would happen if you're calling that in update. But as a Coroutine it's only going to subtract sanityLoss
one time, wait for 1 second, then quit. If you want it to continue draining over time then the Coroutine needs to be a loop...
function sanityDrain(waitTime : float)
{
while (playerSanity > 0)
{
playerSanity -= sanityLoss;
yield WaitForSeconds(waitTime);
}
}
This will loop while sanity is greater than 0, waiting for waitTime
seconds for each iteration.
It is working but its still not waiting for that one second. It just drains all the sanity to 0 in a second.
Add a Debug.Log at the beginning and log the value of the 3 variables (playerSanity, sanityLoss, waitTime). $$anonymous$$ake sure they're actually what you think they are.
Also, how do you call SanityDraining? Is that only being called once? If you're accidentally calling that each frame you're going to start multiple Coroutines and each one of them is going to reduce playerSanity and it will go to 0 very fast. Add a Debug.Log there too and make sure it's only called once when the lantern state changes.
Debug.Log shows no problems. I called SanityDraining in Update function.
function Update () {
SanityDraining();
if(playerSanity <= 0){
Application.LoadLevel("Death");
}
}
Calling that in update is your problem. It's going to start a coroutine every frame. So for example by frame 3 you'll have 3 of those coroutines running, and each of them will be looping and reducing playerSanity. So it's going to be reduced to 0 very fast - nearly instantly.
You only want to call SanityDraining when the state of the lanternLight changes. You want to call it at the same time you change the value of lanternLight.enabled - presumably where you're checking for input to turn the lamp on or off.
I don't know how your trigger is set up, but it would seem it's still causing your coroutine to be started multiple times. Add a Debug.Log right before your call to StartCoroutine and you should see that it's being started multiple times.
If you double click on the error message it will take you to the line that's causing the problem. You're accessing a variable that is null.
Coroutine is started 2 times when lantern is turned off and every time i move it adds starts another coroutine every second.
#pragma strict
var plSanity : player_Sanity;
var lanternTrigger : GameObject;
var lanternLight : Light;
public var playerSanity : int = 100;
public var sanityLoss : int = 1;
function Start () {
lanternLight = GetComponentInParent.<Light>();
lanternTrigger.SetActive(false);
}
function Update () {
if(lanternLight.enabled == false){
lanternTrigger.SetActive(true);
}else{
lanternTrigger.SetActive(false);
}
if(playerSanity <= 0){
Application.LoadLevel("Death");
}
}
function OnTriggerEnter(){
if(lanternTrigger.activeSelf == true){
Debug.Log("Coroutines are multiplied");
StartCoroutine("sanityDrain");
}else{
StopCoroutine("sanityDrain");
}
}
function sanityDrain()
{
while (playerSanity > 0)
{
playerSanity = playerSanity - sanityLoss;
yield WaitForSeconds(1);
}
}
function OnGUI () {
GUI.Box(Rect(10,40,100,25),"Sanity:");
GUI.Box(Rect(110,40,100,25),playerSanity.ToString());
}
Right, your trigger is being triggered as you move. Where do you actually turn the lamp on or off? It seems that it would make more sense to start the sanity drain there. Otherwise, you can check for state changes by tracking the last state...
Go back to the version without the trigger, but with the looping coroutine...
// declare a variable in your class to hold the last state
bool lastState;
// initialize the last state to the current lantern state
function Start()
{
...
lastState = lanternLight.enabled;
}
// modify your SanityDraining function to only do things on state changes
function SanityDraining()
{
// if the light is still in the last state then don't do anything
if (lanternLight.enabled == lastState) return;
// otherwise the light state has changed so we want to kick off
// the appropriate coroutine
if (!lanternLight.enabled)
{
StartCoroutine(sanityDrain(1.0));
}
else
{
StopCoroutine(sanityDrain(1.0));
}
// finally we need to remember the last state so we won't
// do anything here until that state changes again
lastState = lanternLight.enabled;
}
Your answer
Follow this Question
Related Questions
How to implement pausing of WaitForSeconds based coroutine? 1 Answer
Call Coroutine step after step in foreach loop 0 Answers
Coroutine WaitForSeconds ignoring StopAllCoroutines... How can I do it? 0 Answers
Wait time after coroutine's wait seconds is complete 0 Answers
Coroutine not working 1 Answer