So I have a problem with delaying a method from happening in a if statement, this if statement is in another method called OnCollisionExit(). The purpose of this mess is to try to prolong another method in another if statement in another method called OnCollisionStay().
I've made a timer, which actually works. The problem (I think) is that OnCollisionExit() runs through its code only one time... therefore the timer doesn't work (it doesn't reach zero). If you don't undrestand you will do when you read the code
timer method (it is run in the Update() method)
private void Counter()
{
if (counterEnabled)
{
remainingTime = remainingTime - 1 * Time.deltaTime;
}
if (remainingTime <= 0)
{
remainingTime = defaultTime;
counterEnabled = false;
}
}
this is the OnCollisionExit() method
void OnCollisionExit(Collision collision)
{
counterEnabled = true;
if (collision.gameObject == thing && counterEnabled == false)
{
//this is what am trying to delay
DontDoSomething();
}
}
this is the OnCollisionStay() method
void OnCollisionStay(Collision collision)
{
if (collision.gameObject == thing)
{
//this is what Iam trying to prolong
DoSomething();
}
}
if you don't know how the timer works... it works like this:
when OnCollisionExit() runs, it will make a bool (counterEnabled) true
when this happens, a variable(remainingTime) of value 2f will get subtracted by 1 every second
when remainingTime reaches zero or anything less, remainingTime will be equal to defaultTime which is simply a variable holding remainingTime original value (2f), and will turn counterEnabled false.
when counterEnabled turns false, it will allow the if statement in OnCollisionExit() to be true
You could use a coroutine instead :
void OnCollisionExit(Collision collision)
{
StartCoroutine (Countdown());
}
IEnumerator Countdown()
{
yield return new WaitForSeconds (2f);
DontDoSomething ();
}
Related
Sometimes when I step on a red box the timer doens't stop and the buff is always on. Why is this happening? I think this is something related with the couroutines, but I can't figure why. Here is the main code:
private void OnCollisionEnter(Collision coll)
{
if (coll.transform.CompareTag("Player") && !_isFalling)
{
StartCoroutine(HexFalling());
if (type == HexGrid.TypeHex.good)
{
StartCoroutine(coll.gameObject.GetComponent<ThirdPersonMovement>().BuffSpeed(3f, 0.5f));
}
else if (type == HexGrid.TypeHex.bad)
{
StartCoroutine(coll.gameObject.GetComponent<ThirdPersonMovement>().BuffSpeed(0.5f, 1.5f));
}
}
}
And here is the code for one coroutine:
private IEnumerator HexFalling()
{
GetComponent<Renderer>().material = _matIsFalling;
yield return new WaitForSeconds(_timeToFall);
_isFalling = true;
gameObject.SetActive(false);
}
You are deactivating the object that is used for running your coroutine. It is interrupting the coroutine execution.
Also, your coroutine HexFalling() can be called multiple times because _isFalling is not set to true immediately. It will be set to true only in _timeToFall seconds after the collision.
I need to pause some code in an if statement in c#. Thread.Sleep() is NOT what I want as it pauses everything. I just want to temporarily hold the code in an if statement where it is for a few seconds.
I have tried Thread.Sleep() but it is not what I want.
This is in my Update() method.
if (jumps == jumpsToFatigue)
{
// wait for jumpFatigueWait (variable that is in seconds)
jumps = 0f;
}
I want something that can replace the comment that will pause THIS if statement ONLY for the time specified in my variable.
Sorry cant test now but you can try,Edited
IEnumerator WaitForYourSecond()
{
yield return new WaitForSeconds(2f);
jump = 0;
}
void Update()
{
if (jumps == jumpsToFatigue)
{
StartCoroutine("WaitForYourSecond");
}
}
I'm using the IEnumerator function and have had some issues with my if statement working:
IEnumerator Spawning()
{
Debug.Log("Spawning being called...");
if (GameObject.Find("FPSController").GetComponent<BoxCollide>().hitTrigger == true)
{
Debug.Log("CUSTOMER SHOULD SPAWN!");
while (countStop == false) {
yield return new WaitForSeconds(2);
Debug.Log("Fisher Spawned!");
counter++;
spawnNewCharacter.SpawnCharacter();
if (counter >= 3){
countStop = true;
}
}
}
}
After some debugging, it turns out that my if statement actually works. This issue is actually the IEnumerator function being called as soon as I run my game. I need a way to call this IEnumerator function only when my hitTrigger == true, but I can't seem to get anything to work.
I've tried this on top of the IEnumerator function:
void Update()
{
if (GameObject.Find("FPSController").GetComponent<BoxCollide>().hitTrigger == true)
{
Debug.Log("Spawning now...");
StartCoroutine(Spawning());
}
}
But still can't even get any of the Debug.Log's to come through. Would appreciate some help on this!
Side Information
Find() and GetComponent()
You don't want to use GameObject.Find(...) in the Update method, as it's an expensive call. The Update method is called each frame, so you'd call GameObject.Find(...) 60 times in 1 second at 60fps.
So when you use GetComponent() or Find() you want to save a reference to these objects like shown in the snippets below.
Better locations to use methods like GetComponent() or GameObject.Find() are the Awake() and Start() methods.
Awake
Awake is used to initialize any variables or game state before the
game starts. Awake is called only once during the lifetime of the
script instance. Awake is called after all objects are initialized so
you can safely speak to other objects or query them using eg.
GameObject.FindWithTag. [...]
Explanation is taken from the linked documentation.
Start
Start is called on the frame when a script is enabled just before any
of the Update methods is called the first time. Like the Awake
function, Start is called exactly once in the lifetime of the script.
However, Awake is called when the script object is initialised,
regardless of whether or not the script is enabled. Start may not be
called on the same frame as Awake if the script is not enabled at
initialisation time.
Explanation is also taken from the linked documentation
Possible Solution
Add the first Component (FPSControllerCollission) onto the object that holds your FPSController.
It makes use of unities OnTriggerEnter & OnTriggerExit methods.
This script is gonna set the IsTriggered bool to true, when a trigger entered the space of the box collider.
Note: A collider acts as a trigger, when the "Is Trigger" checkbox on
the component is checked.
You can do similar with OnCollisionEnter/Exit to recognize, when a Collider enters the space of the box collider.
Note that the following is only an example and you'll have to tweak /
integrate it into your code
[RequireComponent(typeof(BoxCollider))]
public class FPSControllerCollission : MonoBehaviour {
public bool IsTriggered;
private void OnTriggerEnter(Collider other) {
this.IsTriggered = true;
}
private void OnTriggerExit(Collider other) {
//Maybe check the tag/type of the other object here
this.IsTriggered = false;
}
}
The following SpawnController class could be integrated in the class you allready have.
public class SpawnController : MonoBehaviour {
private FPSControllerCollission _fpsControllerCollission;
private void Awake() {
this._fpsControllerCollission = FindObjectOfType<FPSControllerCollission>();
}
private void Update() {
if (this._fpsControllerCollission.IsTriggered) {
StartCoroutine(nameof(Spawning));
}
}
IEnumerator Spawning() {
Debug.Log("Spawning being called...");
if (this._fpsControllerCollission == true) {
Debug.Log("CUSTOMER SHOULD SPAWN!");
bool countStop = false;
int counter;
while (countStop == false) {
yield return new WaitForSeconds(2);
Debug.Log("Fisher Spawned!");
counter++;
spawnNewCharacter.SpawnCharacter();
if (counter >= 3) {
countStop = true;
}
}
}
}
}
I'm creating a Pop up menu Option in Unity. Now my Problem here is that the coroutine i made in void update is being called so many times. What i mean by that is on my Unity Console the Debug.Logs are incrementing . It should not right because its already coroutine. Could some help me understand more coroutine and help me solve my little problem .
Here is my code:
[SerializeField]
GameObject Option;
[SerializeField]
Button btn,btn2;
[SerializeField]
GameObject open, close;
[SerializeField]
GameObject[] opt;
bool startFinding = false;
void Start()
{
Option.SetActive(false);
Button popUp = btn.GetComponent<Button>();
Button popUp2 = btn2.GetComponent<Button>();
popUp.onClick.AddListener(PopUpOption);
popUp2.onClick.AddListener(ClosePopUp);
}
void Update()
{
if (startFinding)
{
StartCoroutine(GameOptions());
}
}
IEnumerator GameOptions()
{
//Get All the tags
opt = GameObject.FindGameObjectsWithTag("MobileOptions");
if (opt[0].GetComponent<Toggle>().isOn == true && opt[1].GetComponent<Toggle>().isOn == true)
{
Debug.Log("Disable first the check box then choose only 1 option between" + "'rendering'"+ "and" + "'livestreaming'");
}
//Livestreaming
if (opt[0].GetComponent<Toggle>().isOn == true)
{
Debug.Log("Livestreaming Activate");
} else
{
Debug.Log("Livestreaming Deactivate");
}
//Rendering
if (opt[1].GetComponent<Toggle>().isOn == true)
{
Debug.Log("Rendering Activate");
} else
{
Debug.Log("Rendering Deactivate");
}
//Fog
if (opt[2].GetComponent<Toggle>().isOn == true)
{
Debug.Log("Fog Activated");
} else
{
Debug.Log("Fog Deactivated");
}
//Camera Effect
if (opt[3].GetComponent<Toggle>().isOn == true)
{
Debug.Log("Camera Effect Activated");
} else {
Debug.Log("Camera Effect Deactivated");
}
yield return null;
}
void PopUpOption()
{
startFinding = true;
//Disable The Mobile Option Button
open.SetActive(false);
//Enable the Close Option Button
close.SetActive(true);
//activate the Mobile Options
Option.SetActive(true);
}
void ClosePopUp()
{
startFinding = false;
//eanble the mobile option button
open.SetActive(true);
//disable the close option button
close.SetActive(false);
//deactivate the Mobile Option
Option.SetActive(false);
}
Here is how coroutines work:
Let's say I have a couroutine function called MyRoutine (in your case, you called it GameOptions)
private IEnumerator MyRoutine()
Then, anywhere in my code, calling
StartCoroutine(MyRoutine));
Is going to simply call MyRoutine like any usual method. So if you call it in update, it will be called all the time, as any method would. This is not what you want. What make coroutines special is that you can use the yield keyword in them. There are many ways to use it but the most used (and simple) one is to do yield return null
yield return null means "Stop this coroutine, but resume the execution on next frame". You don't need to call any other function (certainly not StartCoroutine). The execution will resume next frame.
To go back to what you posted in your question, you wrote yield return null at the end. So your method is executing, and just at the end, stops and resumes next frame, but since there is nothing left to do, it exits on the next frame.
A typical way to use coroutines is to have the yield return null in a while loop, so when it resumes, it continues the loop. Here is an example that do it
private IEnumerator MyRoutine()
{
while(running) //running is a member bool that you could set to false to exit
{
// Do all the stuff you want to do in ONE frame
// ...
yield return null;
}
}
Typically, the StartCoroutine would be called in the Start() function, or later when an event is triggered.
If you want to know more about coroutine, or check that you understood them properly, check out this page: https://docs.unity3d.com/Manual/Coroutines.html
or this video https://unity3d.com/learn/tutorials/topics/scripting/coroutines
// Edit: quickly present one useful option
In the snippet above, the while loop is very similar to the Update function (the inside of the loop is executed each frame). One nice option is to replace
yield return null
by
yield return new WaitForSeconds(waitTime)
where waitTime is a the time you want to wait before resuming, in seconds
// End of edit
Do not use StartCoroutine() in the Update method. Call it in another method and use a while loop inside your coroutine function if needed. Just control your StartCoroutine() outside of Update method
Update is called every frame, if your condition is ever true, you launch your coroutine every frame.
Just set down your flag to only join 1 time.
void Update()
{
if (startFinding)
{
startFinding = false;
StartCoroutine(GameOptions());
}
}
How can I create a delay after the fade in, so that the text stays on screen for a few seconds? I used an IEnumerator and a coroutine, but it does nothing. I also tried placing it right after the first else.
What happens at the moment is the text fades out before having the chance to fade in. The text appears momentarily in semi-clear and disappears. It's for a Unity project.
Also, Thread.Sleep won't do.
Here's the piece of code in question:
IEnumerator Pause ()
{
yield return new WaitForSecondsRealtime(5);
}
void OnTriggerStay2D(Collider2D interCollider)
{
if (Input.GetKeyDown(KeyCode.E))
{
displayInfo = true;
}
else
{
displayInfo = false;
}
}
void FadeText()
{
if (displayInfo == true)
{
text1.text = string1;
text1.color = Color.Lerp(text1.color, Color.white, fadeTime * Time.deltaTime);
}
else
{
StartCoroutine(Pause());
text1.color = Color.Lerp(text1.color, Color.clear, fadeTime * Time.deltaTime);
}
}
Your code should read:
void Update()
{
if (fadingOut)
{
// fade out with lerp here
}
}
IEnumerator Pause()
{
yield return new WaitForSecondsRealtime(5);
fadingOut = true;
}
void FadeText()
{
if (displayInfo == true)
{
text1.text = string1;
text1.color = Color.Lerp(text1.color, Color.white, fadeTime * Time.deltaTime);
}
else
{
StartCoroutine(Pause());
}
}
You have the right idea of using a coroutine, but didn't quite get the execution right. When you invoke a method on coroutine, it will be executed in parallel with the main thread. In your code, the Pause() method is running alongside the Color.Lerp. If you want the fading to wait until after the pause is complete, they must be on the same coroutine.
Edit:
As pointed out, this won't work if you're calling FadeText() on each frame. But this shows you how you can easily set a flag and wait until the pause time is complete before fading.
You just need to add the text fade to the coroutine.
IEnumerator Pause()
{
yield return new WaitForSecondsRealtime(5);
text1.color = Color.Lerp(text1.color, Color.clear, fadeTime * Time.deltaTime);
}
And just start the coroutine in your else statement. This way it will execute the wait for seconds, and then the fade whenever you call it.
Most easy way is to use LeanTween asset. It's free and have a lot of other usefull features that I use in EVERY project.
It's really awesome lib.
LeanTween.DelayedCall(1f,()=>{ /*any code that will be called after 1 second will pass*/ });
or
LeanTween.DelayedCall(1f, SomeMethodWithoutParams());