I'm new to unity and I'm developing a 2d game. I want my game to update the "scoretext" every 5 seconds and I want it to update for +1. But my code is not working. It is updating in like 0.000...1 second (I mean the program doesn't wait 5 seconds to update). How can I fix this? Thanks!
public Text scoretext;
public int score;
IEnumerator scoreup()
{
score++;
scoretext.text = score.ToString();
yield return new WaitForSeconds(5);
}
void Update()
{
StartCoroutine("scoreup");
}
Solution 1:
Simple use of InvokeRepeating code:
void ScoreUp()
{
score++;
scoretext.text = score.ToString();
}
void Start()
{
InvokeRepeating(nameof(ScoreUp), 0f, 5f); // 0=delay, 5f = repeat Time
}
Solution 2:
Use an IEnumerator:
IEnumerator ScoreUp(int amount = 1)
{
while (true)
{
score += amount;
scoretext.text = score.ToString();
yield return new WaitForSeconds(5);
}
}
private void Start()
{
StartCoroutine(ScoreUp(2));
}
Related
I have object 1 with rigid body and box collider, I want it to hit object 2 (also has collider and rigid body) so I can use the collision to stop object 1 for 5 seconds then have it move through object 2 whish has kinematics on
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TRAINMOVE : MonoBehaviour
{
private int speed = 200;
private Rigidbody rb;
// Start is called before the first frame update
void Start()
{
StartCoroutine(WaitBeforeMove());
}
// Update is called once per frame
private void Awake()
{
rb = GetComponent<Rigidbody>();
}
private void Update()
{
rb.AddForce(transform.up * speed * Time.deltaTime);
}
//void OnCollision(Collision stop)
// {
// if (stop.gameObject.name == "train trigger station1")
// {
// speed = 0;
// WaitBeforeMove();
// speed = 200;
// rb.AddForce(transform.up * speed * Time.deltaTime);
// }
// else
// {
// speed = 200;
// }
// }
IEnumerator WaitBeforeMove()
{
yield return new WaitForSeconds(5);
}
}
Thats not exactly how Coroutines work.
The method/message you are looking for is called OnCollisionEnter. If it is not exactly written like that and with the expected signature then Unity doesn't find it and never invokes it.
A Coroutine does not delay the outer method which runs/starts it.
A Coroutine has to be started using StartCoroutine otherwise it does nothing (or at least it only runs until the first yield statement).
private void OnCollisionEnter(Collision stop)
{
if (stop.gameObject.name == "train trigger station1")
{
StartCoroutine(WaitBeforeMove());
}
}
private IEnumerator WaitBeforeMove()
{
speed = 0;
// Note that without this you would have no force applied but still the remaining velocity it already has
rb.velocity = Vector3.zero;
yield return new WaitForSeconds(5f);
speed = 200;
}
The method/message OnCollisionEnter can be a Courine itself! So in your specific case you actually can just do
// By making this an IEnumerator Unity automatically starts it as a Coroutine!
private IEnumerator OnCollisionEnter(Collision stop)
{
if (stop.gameObject.name == "train trigger station1")
{
speed = 0;
rb.velocity = Vector3.zero;
yield return new WaitForSeconds(5f);
speed = 200;
}
}
Finally instead of permanently continue to add 0 force I would in general rather use a simple bool flag and pause the adding of force entirely.
You should AddForce not every frame but rather in FixedUpdate.
And then you should not multiply force by Time.deltaTime. Since it is applied in fixed time intervals in FixedUpdate it is already a frame-rate independent event.
private bool stopped;
private void FixedUpdate()
{
if(stopped) return;
rb.AddForce(transform.up * speed);
}
// By making this an IEnumerator Unity automatically starts it as a Coroutine!
private IEnumerator OnCollisionEnter(Collision stop)
{
if (stop.gameObject.name == "train trigger station1")
{
stopped = true;
rb.velocity = Vector3.zero;
yield return new WaitForSeconds(5f);
stopped = false;
}
}
I suggest instead of comparing a name you should rather use a Tag and use stop.gameObject.CompareTag("Station")
I just got into Game Dev and I need my script to enable when my player steps a cube trigger, but I don't know how.
This is the Script that I typed and I can't figure out if I should erase the Void Start or not?
public class CountdownTimer : MonoBehaviour {
public bool timerIsRunning;
public Text timeText;
public float timeRemaining = 10;
private void Start()
{
timerIsRunning = true;
}
void Update(){
if (timerIsRunning)
{
if (timeRemaining > 0)
{
timeRemaining -= Time.deltaTime;
DisplayTime(timeRemaining);
}
else
{
Debug.Log("Time has run out!");
timeRemaining = 0;
timerIsRunning = false;
}
}
}
void DisplayTime(float timeToDisplay){
timeToDisplay += 1;
float minutes = Mathf.FloorToInt(timeToDisplay / 60);
float seconds = Mathf.FloorToInt(timeToDisplay % 60);
timeText.text = string.Format("{0:00}:{1:00}", minutes, seconds);
}
}
I think you can make good use of a trigger collider. To do that, you need to add a collider of your choice to your zone and set it as a trigger. Be sure to have a collider and a rigidbody on your player.
Then in your script you can use the function OnTriggerEnter who will be called when an object enter in your zone defined by your trigger collider.
For your countdown method, you can make good use of a couroutine. It will look like this :
IEnumerator DisplayTime() {
timeIsRunning = true;
for (int i = 0; i < timeRemaining; i++) {
// your code to change the text
yield return new WaitForSeconds(1);
}
timeIsRunning = false;
}
private void OnColliderEnter(Collider other) {
if(!timeIsRunning) {
StartCouroutine(DisplayTime());
}
}
What this code does is when an object enter in the trigger, it starts a routine who will update the time displayed each second until the countdown is finished.
With that you won't need your Start and your Update methods anymore.
If you want more informations on trigger collider : https://docs.unity3d.com/ScriptReference/Collider-isTrigger.html
If you want more informations on couroutines : https://docs.unity3d.com/Manual/Coroutines.html
If you want more informations on the method OnTriggerEnter : https://docs.unity3d.com/ScriptReference/Collider.OnTriggerEnter.html
I am currently doing the tutorial learn with code from unity, in this section there are bonus challenges, that do not help you in resolving it. It says that i have to prevent the player from spamming the spacebar key to spawn dogs.
I am new to C#, i started to looking online but i see something about CoRoutines and i still dont know what that is, is there a simple way to do this, searching online i found something like this, but i cant make it work.
I also tried to make some conditional like canSpawn but i do not know how to implement it well, and Unity gives me an error that i cant use && between a bool and a keypress event
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerControllerX : MonoBehaviour
{
public GameObject dogPrefab;
public float time = 2.0f;
public float timer = Time.time;
// Update is called once per frame
void Update()
{
timer -= Time.deltaTime;
if (timer > time)
{
// On spacebar press, send dog
if (Input.GetKeyDown(KeyCode.Space))
{
spawnDog();
}
timer = time;
}
void spawnDog()
{
Instantiate(dogPrefab, transform.position, dogPrefab.transform.rotation);
}
}
You were close. One thing that might make it easier to understand the logic, is to just count up instead of trying to count down. So, in your case, the code would look like this:
void Update ( )
{
timer += Time.deltaTime;
if ( timer >= time )
{
if ( Input.GetKeyDown ( KeyCode.Space ) )
{
spawnDog ( );
timer = 0;
}
}
}
void spawnDog ( )
{
Instantiate ( dogPrefab, transform.position, dogPrefab.transform.rotation );
}
The timer keeps being added to, and when it's greater than your time value (in this case 2.0f), it allows you to press a key. IF a key is then pressed, the timer is reset to 0, and the player needs to wait time time (2.0f) before being able to press the space key again.
I used Coroutines for this task, it has a little bit more code but it works nicely.
public class PlayerControllerX : MonoBehaviour
{
public GameObject dogPrefab;
private bool isCoolDown = false;
private float coolDown = 1f;
private void Update( )
{
if (Input.GetKeyDown(KeyCode.Space))
{
if (isCoolDown == false)
{
SpawnDog( );
StartCoroutine(CoolDown( ));
}
}
}
IEnumerator CoolDown( )
{
isCoolDown = true;
yield return new WaitForSeconds(coolDown);
isCoolDown = false;
}
private void SpawnDog( )
{
Instantiated(dogPrefab, transform.position, dogPrefab.transform.rotation);
}
}
I am using my phone. I am sorry if i made some syntax error.
bool isReadyForInstantiate;
void Start(){
isReadyForInstantiate = true;
}
void Update(){
if(isReadyForInstantiate && Input.GetKeyDown(KeyCode.Space)){
StartCoroutine(PreventSpam());
Instantiate(dogPrefab, transform.position, Quaternion.identity);
}
}
IEnumerator PreventSpam(){
isReadyForInstantiate = false;
yield return new WaitForSeconds(2);
isReadyForInstantiate = true;
}
here my solution based on a StopWatch:
using UnityEngine;
using System.Diagnostics; // hides UnityEngine.Debug. if needed use qualified call
public class PlayerControllerX : MonoBehaviour
{
public GameObject dogPrefab;
public double dogDelayMillis = 2000d;
private Stopwatch stopWatch;
private void Start()
{
stopWatch = new Stopwatch();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
if (stopWatch.IsRunning)
{
if (stopWatch.Elapsed.TotalMilliseconds > dogDelayMillis)
{
stopWatch.Reset();
SpawnDog();
}
}
else
{
SpawnDog();
}
}
}
private void SpawnDog()
{
stopWatch.Start();
Instantiate(dogPrefab, transform.position, dogPrefab.transform.rotation);
}
}
Another example just for fun
public GameObject dogPrefab;
[Range(0f,2f)]
private float timer = 1.0f;
private float waitTime = 1.0f;
// Update is called once per frame
void Update()
{
// Delay Input Timer - only execute the spacebar command if timer has caught up with waitTime
if (timer < waitTime)
{}
// On spacebar press, send dog
else if (Input.GetKeyDown(KeyCode.Space))
{
Instantiate(dogPrefab, transform.position, dogPrefab.transform.rotation);
// Resets Timer
timer = 0;
}
// Run Timer every frame
timer += Time.deltaTime;
Debug.Log(timer);
}
}
I was stuck on the same exact thing, thank you. The code below is what I went with because it's short and sweet.
public GameObject dogPrefab;
[Range(0f,2f)]
private float timer = 1.0f;
private float waitTime = 1.0f;
// Update is called once per frame
void Update()
{
// Delay Input Timer - only execute the spacebar command if timer has caught up with waitTime
if (timer < waitTime)
{}
// On spacebar press, send dog
else if (Input.GetKeyDown(KeyCode.Space))
{
Instantiate(dogPrefab, transform.position, dogPrefab.transform.rotation);
// Resets Timer
timer = 0;
}
// Run Timer every frame
timer += Time.deltaTime;
Debug.Log(timer);
}
}
I created a bonus for the game, which increase the main player’s size when you pick him. I wrote a script, but timer doesn't work when the player and bonus are collide, how to fix it?
When you start coroutine in Start() method, the timer work correctly, but I want to start timer after collision.
Bonus: player pick him and start timer for 10 sec. In this period, player's size change for 10 sec, then return to the original size.
public GameObject effect;
public GameObject Player;
bool bol = false;
float Timeremaining = 3f;
float CurrentTime;
public void OnCollisionEnter2D(Collision2D col)
{
if (col.gameObject.name == "Cube")
{
bol = true;
Destroy(this.gameObject);
Instantiate(effect, transform.position, Quaternion.identity);
RipplePostProcessor.bloom++;
Debug.Log(bol);
StartTimeEvent();
}
}
public void StartTimeEvent()
{
if (bol == true)
StartCoroutine(StartCountDown());
else
StopCoroutine("StartCountDown");
}
public IEnumerator StartCountDown()
{
CurrentTime = Timeremaining;
while (CurrentTime > 0)
{
Debug.Log("CountDown :" + CurrentTime);
Player.transform.localScale = new Vector3(5, 5, 5);
yield return new WaitForSeconds(1.0f);
CurrentTime--;
if (CurrentTime < 1)
{
bol = false;
Player.transform.localScale = new Vector3(3, 3, 1);
Debug.Log(bol);
}
}
}
How can I put a sleep function between the TextUI.text = ...., to wait 3 seconds between each phrase?
public Text GuessUI;
public Text TextUI;
[...truncated...]
TextUI.text = "Welcome to Number Wizard!";
TextUI.text = ("The highest number you can pick is " + max);
TextUI.text = ("The lowest number you can pick is " + min);
I already tried various things but none have worked, such as this:
TextUI.text = "Welcome to Number Wizard!";
yield WaitForSeconds (3);
TextUI.text = ("The highest number you can pick is " + max);
yield WaitForSeconds (3);
TextUI.text = ("The lowest number you can pick is " + min);
In bash, it would be:
echo "Welcome to Number Wizard!"
sleep 3
echo "The highest number you can pick is 1000"
sleep 3
.....
but I can't figure out how to do this in Unity with C#
There are many ways to wait in Unity. They are really simple but I think it's worth covering most ways to do it:
1.With a coroutine and WaitForSeconds.
This is by far the simplest way. Put all the code that you need to wait for some time in a coroutine function then you can wait with WaitForSeconds. Note that in coroutine function, you call the function with StartCoroutine(yourFunction).
Example below will rotate 90 deg, wait for 4 seconds, rotate 40 deg and wait for 2 seconds, and then finally rotate rotate 20 deg.
void Start()
{
StartCoroutine(waiter());
}
IEnumerator waiter()
{
//Rotate 90 deg
transform.Rotate(new Vector3(90, 0, 0), Space.World);
//Wait for 4 seconds
yield return new WaitForSeconds(4);
//Rotate 40 deg
transform.Rotate(new Vector3(40, 0, 0), Space.World);
//Wait for 2 seconds
yield return new WaitForSeconds(2);
//Rotate 20 deg
transform.Rotate(new Vector3(20, 0, 0), Space.World);
}
2.With a coroutine and WaitForSecondsRealtime.
The only difference between WaitForSeconds and WaitForSecondsRealtime is that WaitForSecondsRealtime is using unscaled time to wait which means that when pausing a game with Time.timeScale, the WaitForSecondsRealtime function would not be affected but WaitForSeconds would.
void Start()
{
StartCoroutine(waiter());
}
IEnumerator waiter()
{
//Rotate 90 deg
transform.Rotate(new Vector3(90, 0, 0), Space.World);
//Wait for 4 seconds
yield return new WaitForSecondsRealtime(4);
//Rotate 40 deg
transform.Rotate(new Vector3(40, 0, 0), Space.World);
//Wait for 2 seconds
yield return new WaitForSecondsRealtime(2);
//Rotate 20 deg
transform.Rotate(new Vector3(20, 0, 0), Space.World);
}
Wait and still be able to see how long you have waited:
3.With a coroutine and incrementing a variable every frame with Time.deltaTime.
A good example of this is when you need the timer to display on the screen how much time it has waited. Basically like a timer.
It's also good when you want to interrupt the wait/sleep with a boolean variable when it is true. This is where yield break; can be used.
bool quit = false;
void Start()
{
StartCoroutine(waiter());
}
IEnumerator waiter()
{
float counter = 0;
//Rotate 90 deg
transform.Rotate(new Vector3(90, 0, 0), Space.World);
//Wait for 4 seconds
float waitTime = 4;
while (counter < waitTime)
{
//Increment Timer until counter >= waitTime
counter += Time.deltaTime;
Debug.Log("We have waited for: " + counter + " seconds");
//Wait for a frame so that Unity doesn't freeze
//Check if we want to quit this function
if (quit)
{
//Quit function
yield break;
}
yield return null;
}
//Rotate 40 deg
transform.Rotate(new Vector3(40, 0, 0), Space.World);
//Wait for 2 seconds
waitTime = 2;
//Reset counter
counter = 0;
while (counter < waitTime)
{
//Increment Timer until counter >= waitTime
counter += Time.deltaTime;
Debug.Log("We have waited for: " + counter + " seconds");
//Check if we want to quit this function
if (quit)
{
//Quit function
yield break;
}
//Wait for a frame so that Unity doesn't freeze
yield return null;
}
//Rotate 20 deg
transform.Rotate(new Vector3(20, 0, 0), Space.World);
}
You can still simplify this by moving the while loop into another coroutine function and yielding it and also still be able to see it counting and even interrupt the counter.
bool quit = false;
void Start()
{
StartCoroutine(waiter());
}
IEnumerator waiter()
{
//Rotate 90 deg
transform.Rotate(new Vector3(90, 0, 0), Space.World);
//Wait for 4 seconds
float waitTime = 4;
yield return wait(waitTime);
//Rotate 40 deg
transform.Rotate(new Vector3(40, 0, 0), Space.World);
//Wait for 2 seconds
waitTime = 2;
yield return wait(waitTime);
//Rotate 20 deg
transform.Rotate(new Vector3(20, 0, 0), Space.World);
}
IEnumerator wait(float waitTime)
{
float counter = 0;
while (counter < waitTime)
{
//Increment Timer until counter >= waitTime
counter += Time.deltaTime;
Debug.Log("We have waited for: " + counter + " seconds");
if (quit)
{
//Quit function
yield break;
}
//Wait for a frame so that Unity doesn't freeze
yield return null;
}
}
Wait/Sleep until variable changes or equals to another value:
4.With a coroutine and the WaitUntil function:
Wait until a condition becomes true. An example is a function that waits for player's score to be 100 then loads the next level.
float playerScore = 0;
int nextScene = 0;
void Start()
{
StartCoroutine(sceneLoader());
}
IEnumerator sceneLoader()
{
Debug.Log("Waiting for Player score to be >=100 ");
yield return new WaitUntil(() => playerScore >= 10);
Debug.Log("Player score is >=100. Loading next Level");
//Increment and Load next scene
nextScene++;
SceneManager.LoadScene(nextScene);
}
5.With a coroutine and the WaitWhile function.
Wait while a condition is true. An example is when you want to exit app when the escape key is pressed.
void Start()
{
StartCoroutine(inputWaiter());
}
IEnumerator inputWaiter()
{
Debug.Log("Waiting for the Exit button to be pressed");
yield return new WaitWhile(() => !Input.GetKeyDown(KeyCode.Escape));
Debug.Log("Exit button has been pressed. Leaving Application");
//Exit program
Quit();
}
void Quit()
{
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
}
6.With the Invoke function:
You can call tell Unity to call function in the future. When you call the Invoke function, you can pass in the time to wait before calling that function to its second parameter. The example below will call the feedDog() function after 5 seconds the Invoke is called.
void Start()
{
Invoke("feedDog", 5);
Debug.Log("Will feed dog after 5 seconds");
}
void feedDog()
{
Debug.Log("Now feeding Dog");
}
7.With the Update() function and Time.deltaTime.
It's just like #3 except that it does not use coroutine. It uses the Update function.
The problem with this is that it requires so many variables so that it won't run every time but just once when the timer is over after the wait.
float timer = 0;
bool timerReached = false;
void Update()
{
if (!timerReached)
timer += Time.deltaTime;
if (!timerReached && timer > 5)
{
Debug.Log("Done waiting");
feedDog();
//Set to false so that We don't run this again
timerReached = true;
}
}
void feedDog()
{
Debug.Log("Now feeding Dog");
}
There are still other ways to wait in Unity but you should definitely know the ones mentioned above as that makes it easier to make games in Unity. When to use each one depends on the circumstances.
For your particular issue, this is the solution:
IEnumerator showTextFuntion()
{
TextUI.text = "Welcome to Number Wizard!";
yield return new WaitForSeconds(3f);
TextUI.text = ("The highest number you can pick is " + max);
yield return new WaitForSeconds(3f);
TextUI.text = ("The lowest number you can pick is " + min);
}
And to call/start the coroutine function from your start or Update function, you call it with
StartCoroutine (showTextFuntion());
You were correct to use WaitForSeconds. But I suspect that you tried using it without coroutines. That's how it should work:
public void SomeMethod()
{
StartCoroutine(SomeCoroutine());
}
private IEnumerator SomeCoroutine()
{
TextUI.text = "Welcome to Number Wizard!";
yield return new WaitForSeconds (3);
TextUI.text = ("The highest number you can pick is " + max);
yield return new WaitForSeconds (3);
TextUI.text = ("The lowest number you can pick is " + min);
}
With .Net 4.x you can use Task-based Asynchronous Pattern (TAP) to achieve this:
// .NET 4.x async-await
using UnityEngine;
using System.Threading.Tasks;
public class AsyncAwaitExample : MonoBehaviour
{
private async void Start()
{
Debug.Log("Wait.");
await WaitOneSecondAsync();
DoMoreStuff(); // Will not execute until WaitOneSecond has completed
}
private async Task WaitOneSecondAsync()
{
await Task.Delay(TimeSpan.FromSeconds(1));
Debug.Log("Finished waiting.");
}
}
this is a feature to use .Net 4.x with Unity please see this link for description about it
and this link for sample project and compare it with coroutine
But becareful as documentation says that This is not fully replacement with coroutine
Bear in mind that coroutine stack ! If starting your coroutines in Update(), you might end up with loads of coroutines waiting inline and executing almost at the same time, just after your wait.
To avoid this, a good approach is to use a boolean, preventing from "stacking" coroutines :
bool isRunning = false;
IEnumerator MyCoroutine(){
isRunning = true;
print("started");
yield return new WaitForSeconds(3);
print("3 seconds elapsed");
yield return new WaitForSeconds(3);
print("more 3 seconds");
yield return new WaitForSeconds(2);
print("ended");
isRunning = false;
}
void Update(){
if (!isRunning) StartCoroutine(MyCoroutine());
}
Source : https://answers.unity.com/questions/309613/calling-startcoroutine-multiple-times-seems-to-sta.html
here is more simple way without StartCoroutine:
float t = 0f;
float waittime = 1f;
and inside Update/FixedUpdate:
if (t < 0){
t += Time.deltaTIme / waittime;
yield return t;
}
Use async and await
public void Start() {
doTask();
}
async void doTask() {
Debug.Log("Long running task started");
// wait for 5 seconds, update your UI
await Task.Delay(TimeSpan.FromSeconds(5f));
// update your UI
Debug.Log("Long running task has completed");
}
//Here is a example of some of my code to wait in Unity I have made using a value and update dating it every update, once it its the value the if statement is looking for it will run the task.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnterCarCollider : MonoBehaviour
{
public GameObject player;
//Calls & Delcares vehicle objects
public GameObject Camera;
public VehicleControl ascript;
public Collider enterDriverCollider;
public Collider parkBreakCollider;
public GameObject enterVehicleDriverToolTip;
public int forStayInTime = 32;
public int timeInActiveTriggeredCollider;
private void Start()
{
ascript = GetComponent<VehicleControl>();
timeInActiveTriggeredCollider = 0;
}
private void OnTriggerStay(Collider other)
{
if (forStayInTime <= timeInActiveTriggeredCollider)
{
if (Input.GetKey(KeyCode.E))
{
ascript.enabled = !ascript.enabled;
Camera.active = true;
player.active = false;
enterDriverCollider.enabled = false;
parkBreakCollider.enabled = false;
}
// TODO: Enter car message
enterVehicleDriverToolTip.active = true;
}
timeInActiveTriggeredCollider++;
}
private void OnTriggerExit(Collider other)
{
enterVehicleDriverToolTip.active = false;
timeInActiveTriggeredCollider = 0;
}
private void Update()
{
if (enterDriverCollider.enabled is false)
{
timeInActiveTriggeredCollider = 0;
}
}
}