any way i can make this code print "attacking" and than for 2 seconds then print it again and so on. this script just waits for 2 seconds the first time. and then keeps printing it with every frame
public class ZombieAI : MonoBehaviour
{
// Update is called once per frame
void Update()
{
StartCoroutine(time());
}
IEnumerator time()
{
yield return new WaitForSeconds(4);
Debug.Log("Attacking");
}
}
You can implement it by doing this
public class ZombieAI : MonoBehaviour
{
// Update is called once per frame
void Start()
{
StartCoroutine(time());
}
IEnumerator time()
{
Debug.Log("Attacking");
yield return new WaitForSeconds(2);
StartCoroutine(time());
}
}
You have to start the coroutine from inside the coroutine. However, if you want to implement this WITHOUT coroutines, do this:
public class ZombieAI : MonoBehaviour
{
float timer = 0;
public float attackTimer = 2;
// Update is called once per frame
void Update()
{
timer += Time.deltaTime;
if (timer >= attackTimer)
{
Debug.Log("Attacking!");
timer = 0;
}
}
}
Coroutines can cause headaches, but use whichever one you want :)
Firstly, do not put StartCoroutine() in Update(). That will begin a new coroutine every frame.
Apart from that, you just need to put the action inside a loop, e.g. a for loop. If you want it to keep looping indefinitely until you manually stop the coroutine, you can use:
public class ZombieAI : MonoBehaviour
{
void Start() // Or anywhere that will only be called once when you want to start the coroutine)
{
StartCoroutine(time());
}
IEnumerator time()
{
while( true ) {
yield return new WaitForSeconds(2);
Debug.Log("Attacking");
}
}
}
Related
I made a player in my game, such that it goes into slow-motion when you hold down the space bar. But I want the player to only be available to be in slow-motion for 5 seconds at a time. After 10 seconds the player will be available to go into slow-motion again.
Here is the code for the script
using UnityEngine;
public class SlowMotion : MonoBehaviour
{
public float slowMotionTimescale;
private float startTimescale;
private float startFixedDeltaTime;
void Start()
{
startTimescale = Time.timeScale;
startFixedDeltaTime = Time.fixedDeltaTime;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
StartSlowMotion();
}
if (Input.GetKeyUp(KeyCode.Space))
{
StopSlowMotion();
}
}
private void StartSlowMotion()
{
Time.timeScale = slowMotionTimescale;
Time.fixedDeltaTime = startFixedDeltaTime * slowMotionTimescale;
}
private void StopSlowMotion()
{
Time.timeScale = startTimescale;
Time.fixedDeltaTime = startFixedDeltaTime;
}
}
You can use IEnumerator to run time-dependent methods. The method description is as follows:
public bool inTimer; // are slow motion is in timer?
public IEnumerator StartTimer()
{
inTimer = true;
StartSlowMotion();
yield return new WaitForSeconds(5f); // wait to end slow motion
StopSlowMotion();
yield return new WaitForSeconds(5f); // wait time to finish
inTimer = false;
}
In addition, you need to consider the condition not in timer.
if (Input.GetKeyDown(KeyCode.Space) && !inTimer)
{
StartCoroutine(StartTimer()); // how to run timer
}
I am trying to set a gameobject to active on collision, wait a second and then set it to inactive again, but after the WaitForSeconds() line the execution seems to stop. I have no experience with C# and Unity so this may be a beginner mistake. Any idea why this could be happening?
private IEnumerator OnCollisionEnter2D(Collision2D collidedWith) {
if (collidedWith.gameObject.tag == "Shape") {
collidedWith.transform.GetChild(0).gameObject.SetActive(true);
Object.Destroy(this.gameObject);
yield return new WaitForSeconds(1);
Debug.Log("Should have waited for 1 second");
collidedWith.transform.GetChild(0).gameObject.SetActive(false);
}
}
You do
Object.Destroy(this.gameObject);
on this object which is running the Coroutine -> The routine is interrupted in that very same moment (when it reaches the first yield statement) -> it never actually starts to wait ;)
You should rather have a component on the object you collide with and make sure the Coroutine is run on that one instead.
E.g. like
// Have this on your Shape objects
public class Shape : MonoBehaviour
{
private bool isCollided;
public void Collided()
{
if(!isCollided) StartCoroutine(Routine());
}
private IEnumerator Routine()
{
if(isCollided) yield break;
isCollided = true;
var child = transform.GetChild(0).gameObject;
child .SetActive(true);
yield return new WaitForSeconds(1);
child.SetActive(false);
isCollided = false;
}
}
and then rather do e.g.
private void OnCollisionEnter2D(Collision2D collidedWith)
{
if (collidedWith.gameObject.TryGetComponent<Shape>(out var shape))
{
shape.Collided();
Destroy(gameObject);
}
}
I created a function that will run until an images fillamount is at 0. However when I call this function from another class the unity editor completing freezes. I cant even stop pause play mode.
The class that calls the cooldown function.
Cooldown cooldown;
cooldown = gameObject.GetComponentInChildren<Cooldown>();//Sets cooldown to have the same values as the prefab.
public void gatherCooldown()
{
cooldown.resourceCooldown();
}
Cooldown Class
public class Cooldown : MonoBehaviour
{
public Image imageCooldown;
public float cooldown = 5;
public bool isCooldown
public void resourceCooldown()
{
while (imageCooldown.fillAmount >= 0)
{
imageCooldown.fillAmount -= 1 / cooldown * Time.deltaTime;
}
}
}
You are currently using a blocking method. To fix that you could use a Corountine instead to decreasing your images fillAmount.
Coroutine Example:
private IEnumerator DecreaseFillAmount() {
// Entered the Coroutine
isCooldown = true;
// Repeat until the fillAmount is smaller than or equal to 0
while (imageCooldown.fillAmount > 0) {
imageCooldown.fillAmount -= (1 / cooldown) * Time.deltaTime;
yield return null;
}
// Left the Coroutine
isCooldown = false;
}
You also need to make sure that you call your Coroutin the right way, you achieve this with StartCoroutine().
Calling the Coroutine:
public void resourceCooldown() {
// Call the DecreaseFillAmount Coroutine.
StartCoroutine(DecreaseFillAmount());
}
Your while is freezing the main thread.
This should probably rather be a Coroutine
public class Cooldown : MonoBehaviour
{
public Image imageCooldown;
public float cooldown = 5;
public bool isCooldown;
private IEnumerator CooldownRoutine()
{
isCooldown = true;
while (imageCooldown.fillAmount >= 0)
{
imageCooldown.fillAmount -= 1 / cooldown * Time.deltaTime;
// Tells Unity to "pause" this routine, render this frame
// And continue from here in the next frame
yield return null;
}
isCooldown = false;
}
public void resourceCooldown()
{
StartCoroutin(CooldownRoutine ());
}
}
You will see under the void start function the currentWave. I want it to increment by 1 every 20 seconds. but not sure where and how to go about doing so. Below you will see my declared variables. I have left out the other section of code as it is not necessary for what I need.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Spawner : MonoBehaviour
{
private int currentWave;
private float startTime;
private float currentTime;
Above is my declared variables, and below is my stary function with currentWave set to 1, which is the integer I want to change every 20 seconds.
void Start()
{
currentWave = 0;
startTime = Time.time;
StartCoroutine(SpawnEnemy(TimeFrame[currentWave]));
}
void Update()
{
currentTime = Time.time - startTime;
Debug.Log(currentTime);
}
}
I used my update function to get the current "running time" of the program.
Use a coroutine:
private IEnumerator waveIncrementer;
void Start()
{
currentWave = 0;
startTime = Time.time;
StartCoroutine(SpawnEnemy(TimeFrame[currentWave]));
waveIncrementer = IncrementWave();
StartCoroutine(waveIncrementer);
}
IEnumerator IncrementWave()
{
WaitForSeconds waiter = new WaitForSeconds(20f);
while (true)
{
yield return waiter;
currentWave++;
}
}
if you want it to increment immediately, put currentWave++ before yield return waiter;:
IEnumerator IncrementWave()
{
WaitForSeconds waiter = new WaitForSeconds(20f);
while (true)
{
currentWave++;
yield return waiter;
}
}
Then, you can stop it with StopCoroutine(waveIncrementer);
I think what you are looking for is InvokeRepeating
You don't need to update it every frame. Just compute it when you need it. Change the field to a property, and return a computed value.
public class Spawner : MonoBehaviour
{
private float startTime;
private float CurrentTime
{
get
{
return Time.time - startTime;
}
}
Look at this simple code:
function Start()
{
yield WaitForSeconds (4);
Application.LoadLevel(1);
}
It works! I'm trying to make something similiar using C#, but the processor just ignore the WaitForSeconds. This is my code in C#:
using UnityEngine;
using System.Collections;
public class openingC : MonoBehaviour
{
void Start()
{
executeWait(5);
Application.LoadLevel(1);
}
void executeWait(float aux)
{
StartCoroutine(Wait(aux));
}
IEnumerator Wait(float seconds)
{
yield return new WaitForSeconds(seconds);
}
}
Can someone explain to me why it's not working? Thanks for your time.
public class openingC : MonoBehaviour
{
void Start()
{
executeWait(5);
Application.LoadLevel(1);
}
void executeWait(float aux)
{
StartCoroutine(Wait(aux));
}
IEnumerator Wait(float seconds)
{
yield return new WaitForSeconds(seconds);
}
}
First, the Start method runs, executeWait is called, the program jumps to the method. It finds the coroutine and starts running it until a yield is found or the end of the method. Yield returns to the program, the pointer goes back up to executeWait and finishes the method. The pointer goes back up to Start and calls for Application.LoadLevel.
You want to hang the LoadLevel call.
public class openingC : MonoBehaviour
{
void Start()
{
StartCoroutine(Wait(5));
}
//You don't need executeWait
IEnumerator Wait(float seconds)
{
yield return new WaitForSeconds(seconds);
Application.LoadLevel(1);
}
}
Try this:
Thread.Sleep(500); //500 millisecond waiting...
Reference
This should also be okay.
IEnumerator Start()
{
yield return new WaitForSeconds(4);
Application.LoadLevel (1);
}