StopCoroutine not working not even StartCoroutine(string), StopCoroutine(string) in unity3d - unity3d

I can't understand why.
I already read lots of documents about it and there are lots of comments that
StopCoroutine(string) can only stop the coroutine starts with StartCoroutine(string)
the code is below.
In Class CharTouchControl.cpp
if( Input.GetKeyDown( KeyCode.Q ) )
{
_CharControl.StartCoroutine("useFever", 10);
}
_CharControl is a member of CharTouchControl and of course _CharControl is referencing the CharControl instance below.
And InClass CharControl
public IEnumerator useFever(float _duration = 10)
{
if( m_nUseFever < _nFeverMax )
{
Debug.Log("Stop!!");
StopCoroutine("useFever");
yield return new WaitForEndOfFrame();
}
m_fgCharState = CharState._FEVER;
// fever particle
m_psFeverPaticle.Simulate(4);
yield return new WaitForEndOfFrame();
} // end of useFever
When m_nUseFever < _nFeverMax is true, i can see "Stop!!" log,
but the coroutine don't stop and simulate paticles.
anybody help for this?

Just as Jerdak mentioned, you should use yield break when your inside the Coroutine.
the coroutine will automatically stop when it finishes its code block. Using yield break, should send it to the end of the code block.
Additionally you could modify your code so that it only runs the code block if the condition is true. Ive done this in the example below. If the condition is false the coroutine will just wait for the end of the frame and then exit.
public IEnumerator useFever()
{
if( m_nUseFever > _nFeverMax )
{
m_fgCharState = CharState._FEVER;
// fever particle
m_psFeverPaticle.Simulate(4);
}
yield return new WaitForEndOfFrame();
} //Coroutine automatically exits here
EDIT:
Dan Puzey brought up a good point, Are you sure shouldn't be using
Invoke("useFever",10) or InvokeRepeating ("useFever",10,1)
instead? From your code it looks like you just want to run the useFever function in 10 seconds. If thats the case, you should be invoking it instead of running it as a coroutine, and useFever should be a normal function.
Just for reference, there is also CancelInvoke("useFever")

Related

Unity | instantiate only works once

For some reason whenever I Instantiate, I can only instantiate once per build/run/game. I've tried to see if it was that I could only use different prefabs, but that isn't it. Please help, I've been trying to find an answer but no one seems to have the same problem. Thanks for reading this.
heres the project (assets and project settings) if you wanna mess around with it yourself
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CreatePipe : MonoBehaviour
{
public GameObject Pipe;
public bool yes = false;
// Start is called before the first frame update
void Start()
{
StartCoroutine("initializationn");
Debug.Log("started");
}
// Update is called once per frame
void Update()
{
// if (yes){
// inst();
// yes = false;
// }
}
public IEnumerator initializationn()
{
Debug.Log("started");
inst();
// yes = true;
yield return new WaitForSeconds(3);
inst();
// yes = true;
yield return new WaitForSeconds(3);
inst();
// yes = true;
yield return new WaitForSeconds(3);
// while (true){
// inst()
// yield return new WaitForSeconds(3);
// }
}
public void inst(){
Debug.Log("in");
var g = Instantiate(Pipe, new Vector3(7f, Random.Range(-6.5f,0f), 0f), Quaternion.identity);
g.SetActive(true);
g.name = "Pipe";
}
}
The reason only one pipe exist at at time is that the script "PipeStuff" you attached to the Prefab "Pipe", makes it a singleton. So every time you create a duplicate instance you are deleting that object since a "PipeStuff" already exist.
Suggestions:
Your script is absolutely fine there is no problem with it but perhaps you are destroying that gameObject which is intantiating those pipe prefabs thats why its not working as you intend it to, any ways check if it is so because there seems to be no other problem with your script and also you can do it in one more way by using invokeRepeating function too like:
InvokeRepeating( nameof(inst), 0, 3 );
it will call this function repeatadely after every 3 seconds and the first parameter which is 0, is used for delay at the start so if you want to Invoke that function after some seconds you can do so as well and when you are done you can destroy or disable that gameObject too, its just a basic way of doing so otherwise there are tons of ways to do so anyways...
Hope it helps... Happy Coding :)

yield return new WaitForSeconds not working

I have 2 instances of this IEnumerator on 2 scripts. Both are private, but one is working, the other isn't.
This is the first one that is working:
private IEnumerator EndGame()
{
yield return new WaitForSeconds(3);
SceneManager.LoadScene("Menu", LoadSceneMode.Single);
}
This is the second one that isn't working:
private IEnumerator EndGame()
{
yield return new WaitForSeconds(3);
SceneManager.LoadScene("Menu", LoadSceneMode.Single);
}
They're exactly the same. I can call the IEnumerator (proven by a Debug.Log), but in one case it waits for 3 seconds, and in the other one it stops at the return statement and doesn't continue. I also tried renaming the Coroutine that wasn't working and making them both private just in case you can't have Coroutines with the same name, but that also didn't work. My time scale is constantly remaining at 1 and the object that I called the Coroutine from doesn't get destroyed.
One weird thing that does happen is the object that I call the Coroutine from does get destroyed, but after the 3 seconds is up (The highlighted player is the one calling the Coroutine, and the same thing happens on both players):

WaitForEndOfFrame replacing WaitForNewFrame

The following strategy was suggested, in order to allow some time for a big system to finish all the loading, etc...
So, the idea is to make sure I have waited for long enough for everything to be loaded, and then do my own command. Let's call it CMD() for now.
void Start()
{
StartCoroutine( TakeYourTime() );
}
IEnumerator TakeYourTime()
{
yield return new WaitForNewFrame();
yield return new WaitForNewFrame();
yield return new WaitForNewFrame();
yield return new WaitForNewFrame();
CMD();
}
But of course, Unity3D does not recognize WaitForNewFrame()!
After googling, I realized there is only WaitForEndOfFrame() available, so I replaced it in the above, thinking that it would achieve the same, but my CMD() does not take effect.
So, I am wondering if this existing command is not the same as the originally-suggested one, or if I am not waiting long enough?
yield return null;
is the command that will get the method to return on next frame and continue.
But this is no good practice as you cannot know if one , two or more frames are required.
You should have some kind of control over the flow of your program. If some actions are long and spread over several frames, provide a callback to the coroutine:
void Awake()
{
StartCoroutine(Loading(CMD));
}
private void CMD(){ //Other actions }
private IEnumerator Loading (Action action)
{
// Many long action with yield
if(action != null) { action(); }
}
If CMD() is also an IEnumerator function, you have to do another StartCoroutine() function on it, or it will not work. Based on the information available that's the only issue I can think of to explain why CMD() would not start.
If you have additional logic after that requires to wait on the completion of CMD(), you can yield return the StartCoroutine() call to pause TakeYourTime() from continuing until CMD() is completed.
void Start()
{
StartCoroutine( TakeYourTime() );
}
IEnumerator TakeYourTime()
{
yield return new WaitForEndOfFrame();
yield return new WaitForEndOfFrame();
yield return new WaitForEndOfFrame();
yield return new WaitForEndOfFrame();
yield return StartCoroutine(CMD());
//Do the rest.
}
IEnumerator CMD()
{
//Do whatever.
}
If CMD() is not an IEnumerator function, then I apologize for this incorrect answer. You can troubleshoot further using Debug.Log() calls to determine where the application is getting stuck. I would put them before, after, and on the first line of CMD()
From your comments, though, I would recommend you do something else.
You mention that when you wait longer, it works. That means you're just waiting on something to complete, and there's no issue with methods firing.
You should do something like this instead:
IEnumerator TakeYourTime()
{
while(!otherThingIsFinished())
{
yield return new WaitForEndOfFrame();
}
CMD();
}
otherThingIsFinished() should be a call to check to see if the stuff you're waiting for has completed whatever it's doing. You can have a public method that you can call that checks some private boolean that gets set when it's completed.
You can also just have the other stuff call this method directly, although that may not be the best option either. It all depends on what exactly you're trying to do.

Is it safe to start a new coroutine inside a current coroutine?

I have a grid-based game in which I programmed my movement script to move my game objects cell by cell. To achieve the cell by cell movement that I want, I had to use coroutines.
Here's a pseudo-code snippet of my code:
private Coroutine currentCoroutine;
public void Move(Vector3 velocity)
{
currentCoroutine = StartCoroutine(MoveCoroutine(velocity));
}
private IEnumerator MoveCoroutine(Vector3 velocity)
{
Vector3 endPoint;
Vector3 nextVelocity;
if(velocity == Vector3.Right)
{
endPoint = rightEndPoint;
// object should move left in the next coroutine
nextVelocity = Vector3.Left;
}
else
{
endPoint = leftEndPoint;
// object should move right in the next coroutine
nextVelocity = Vector3.Right;
}
while(currentPos != endPoint)
{
currentPos += velocity
yield return new WaitForSeconds(movementDelay);
}
currentCoroutine = StartCoroutine(MoveCoroutine(nextVelocity));
}
Basically what this does is move my object left and right. If it reaches the left edge already, I make it go right and vice-versa. I call the Move() from another script.
This code is working fine for me. However, I am not sure if starting a new coroutine inside a coroutine is safe, like what I did here. Are there any consequences when writing coroutines like this? I'm still getting used to the concept of coroutines.
Thanks
Starting a new coroutine at the end of a coroutine is safe. By the way, I noticed that your yield statement is only called inside a while loop. If there's any chance the while loop won't run at least once, you'll have an error; all coroutines must execute a yield statement.

Time Delay for a process in Unity 3D

I have to give the delay for the process to happen, which I am calling in the Update function.
I have tried CoUpdate workaround also. Here is my code:-
function Start()
{
StartCoroutine("CoStart");
}
function CoStart() : IEnumerator
{
while(true)
{
yield CoUpdate();
}
}
function CoUpdate()
{
//I have placed the code of the Update().
//And called the wait function wherever needed.
}
function wait()
{
checkOnce=1; //Whenever the character is moved.
yield WaitForSeconds(2); //Delay of 2 seconds.
}
I have to move an object when a third person controller(which is another object) moves out of a boundary. I have included "yield" in my code. But, the problem happening is: The object which was moving when I gave the code for in the Update(), is moving, but isn't stopping. And it is moving up and down. I don't know what is happening! Can someone help? Please, thanks.
I am not entirely clear what you are trying to accomplish, but I can show you how to set up a Time Delay for a coroutine. For this example lets work with a simple cool down, much like you set up in your example. Assuming you want to continuously do something every 2 seconds while your game is running a slight modification can be made to your code.
function Start()
{
StartCoroutine(CoStart);
}
function CoStart() : IEnumerator
{
while(true)
{
//.. place your logic here
// function will sleep for two seconds before starting this loop again
yield WaitForSeconds(2);
}
}
You can also calculate a wait time using some other logic
function Start()
{
StartCoroutine(CoStart);
}
function CoStart() : IEnumerator
{
while(true)
{
//.. place your logic here
// function will sleep for two seconds before starting this loop again
yield WaitForSeconds(CalculateWait());
}
}
function CalculateWait() : float
{
// use some logic here to determine the amount of time to wait for the
// next CoStart cycle to start
return someFloat;
}
If I have missed the mark entirely then please update the question with a more detail about what you are attempting to accomplish.
I am not 100% sure that I understand you question but if you want to start one object to move when the other is out of bound then just make a reference in the first object to the second object and when the first object is out of bounds (check for this in Update of the first object) call some public function StartMove on the second object.
I wouldn't suggest CoRoutines. It can sometimes crash your computer. Just define a variable and decrement it. Example:
private float seconds = 5;
then do anywhere you want to delay:
seconds -= 1 * Time.deltaTime;
if(seconds <= 0) {your code to run}
This will make a delay of 5 seconds. You can change 5 to any value to change the number of seconds. Also you can speed up the decrement by changing the value of 1. (This is mostly useful when you want to sync 2 delayed actions, by using the same variable)
Hope this helps. Happy coding :)