I am fairly new to Unity and I really don't know how to directly ask this question. Basicaly I am just playing around with Unitiy in general and I am trying to create script that initially has it set to active then disappear after 3 seconds but then come back as active again, can anyone explain anything specific that has to do with this problem? This is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ScriptTwo : MonoBehaviour
{
public GameObject theBall;
// Start is called before the first frame update
void Start()
{
theBall.SetActive(true);
StartCoroutine(BallCode());
}
IEnumerator BallCode() {
yield return new WaitForSeconds(3);
theBall.SetActive(false);
Debug.Log("Set Active: False");
Debug.Log("Test Before");
yield return new WaitForSeconds(3);
theBall.SetActive(true);
}
}
What your code should be doing is:
-activating
-waiting 3 seconds
-disabling
-waiting 3 seconds
-activating
If it is doing anything other than that it is a problem with another code you wrote. If you wanted it to continue infinitly than:
IEnumerator BallCode()
{
while (true)
{
yield return new WaitForSeconds(3);
theBall.SetActive(!theBall.activeInHierarchy);
Debug.Log("Set Active: " + theBall.activeInHierarchy);
}
}
If your script is in the gameObject you are deactivating (in this instance the ball) you should move it to another gameObject.
All Unity Monobehaviour methods and by extent coroutines, are halted/stopped when the gameObject is deactivated.
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I am trying to implement it so that you can shoot 2 times per second. I am doing this by shooting, and then delaying the gun controlling script for 0.5 seconds, but it's not working. I am trying to use the WaitForSeconds function.
I expect that when I click, a shoot immediately happens, and for the next 0.5 seconds clicking will have no effect while the rest of the game happens normally, and after that time, clicking once will shoot again, causing another delay and so on.
Instead, I can shoot over and over with nothing slowing it down.
Here's my script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GunController : MonoBehaviour
{
public Transform firePoint;
public GameObject bulletPrefab;
public string gunType;
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
switch (gunType)
{
case "pistol":
StartCoroutine(ShootThenDelay(0.5f));
break;
}
}
}
IEnumerator ShootThenDelay(float seconds)
{
Shoot();
yield return new WaitForSeconds(seconds);
}
void Shoot()
{
GameObject bullet = Instantiate(bulletPrefab, firePoint.position, firePoint.rotation);
}
}
A Coroutine does not delay the method that is starting it (with one exception - see below) .. otherwise your entire app would be frozen.
You could though simply add a flag like e.g.
bool canShoot = true;
IEnumerator ShootThenDelay(float seconds)
{
canShoot = false;
Shoot();
yield return new WaitForSeconds(seconds);
canShoot = true;
}
and then check for
if (canShoot && Input.GetMouseButtonDown(0))
Alternatively in your case you actually could directly wait until the routine is finished using
// Yes! If you make Start return IEnumerator then Unity
// automatically runs it as a Coroutine
private IEnumerator Start()
{
// Looks dangerous but is totally fine for a Coroutine as long as you yield inside
while(true)
{
if (Input.GetMouseButtonDown(0))
{
switch (gunType)
{
case "pistol":
// "Runs" the shoot routine and waits until it finished
// This way a yield at the end actually has an effect
yield return ShootThenDelay(0.5f));
break;
default:
// Note: This is very important! Make sure all paths within the while yield at least for one frame!
yield return null;
break;
}
}
else
{
// Note: This is very important! Make sure all paths within the while yield at least for one frame!
yield return null;
}
}
}
IEnumerator ShootThenDelay(float seconds)
{
Shoot();
yield return new WaitForSeconds(seconds);
}
That is because after waiting for 0.5 seconds, the coroutine returns to the point in code where execution was halted, in your example it returns to the line that goes after:
yield return new WaitForSeconds(seconds);
However, there is nothing, so nothing happens. If you do something like this:
IEnumerator ShootThenDelay(float seconds)
{
Shoot();
yield return new WaitForSeconds(seconds);
Shoot();
}
bullet must be shot twice with the delay of 0.5 seconds.
I do recommend you checking the documentation next time :)
https://docs.unity3d.com/ScriptReference/WaitForSeconds.html
Im trying to make a basic platformer game and my level controller script isnt working, it needs to load loading scene first and then nextlevel but it constantly loads the nextlevel
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class LevelController : MonoBehaviour
{
[SerializeField] string nextLevel;
private Gem[] gems;
void OnEnable()
{
gems = FindObjectsOfType<Gem>();
}
IEnumerator Wait()
{
yield return new WaitForSeconds(3);
}
void Update()
{
if (ReadyToNextLevel())
{
SceneManager.LoadScene("Loading");
StartCoroutine(Wait());
SceneManager.LoadScene(nextLevel);
}
}
bool ReadyToNextLevel()
{
foreach (var gem in gems)
{
if (gem.gameObject.activeSelf)
{
return false;
}
}
return true;
}
}
There are two big issues in your code.
Starting a Coroutine does not delay the method that started the routine!
If you want something happening after the Coroutine is finished you need to either move it into the routine itself or use a callback.
However, the thing is: You are loading a new scene, namely the "Loading" scene -> The current scene is unloaded -> the object this script is on gets destroyed -> the routine would no further executed.
Except your object is DontDestroyOnLoad which seems not the case from your code.
So in order to solve both you will need to make sure this object is not destroyed when another scene is loaded, at least not until it finished the loading process.
You could do this like e.g.
public class LevelController : MonoBehaviour
{
[SerializeField] string nextLevel;
private Gem[] gems;
void OnEnable()
{
gems = FindObjectsOfType<Gem>();
// Makes sure this is not destroyed when another scene is load
DontDestroyOnLoad(gameObject);
}
bool alreadyLoading;
IEnumerator Wait(Action whenDone)
{
yield return new WaitForSeconds(3);
// invoke the callback action
whenDone?.Invoke();
}
void Update()
{
if (ReadyToNextLevel() && ! alreadyLoading)
{
alreadyLoading = true;
SceneManager.LoadScene("Loading");
StartCoroutine(Wait(() =>
{
// This will be done after the routine finished
SceneManager.LoadScene(nextLevel);
// Now we don't need this object anymore
Destroy(gameObject);
}));
}
}
bool ReadyToNextLevel()
{
foreach (var gem in gems)
{
if (gem.gameObject.activeSelf)
{
return false;
}
}
return true;
}
}
EDIT: see derHugo's answer. They are more familiar with Unity than I am and point out that your script will be unloaded on scene change.
Your Wait() coroutine yields immediately, so StartCoroutine(Wait()) will return immediately and load the next scene.
If you want to wait three seconds before loading, then put the load inside the coroutine.
Like this:
private bool isLoading;
void Update()
{
if (!isLoading && ReadyToNextLevel())
StartCoroutine(LoadNextLevel());
}
IEnumerator LoadNextLevel()
{
isLoading = true;
SceneManager.LoadScene("Loading");
yield return new WaitForSeconds(3);
SceneManager.LoadScene(nextLevel);
isLoading = false;
}
See Coroutine help for more details
there are two reasons why this doesn't work, firstly when you load into the loading scene the gameObject is destroyed because you haven't told it to DontDestroyOnLoad, so the script will be sort of 'deleted' from the current scene. Second of all, when you call the coroutine, it calls the coroutine and then immediately continuous on to the next line of code, it doesn't wait for the coroutine to finish before moving on. I think derHugo covered how to fix these.
Well, I'm trying to make an fps where you shoot tagets and appears an "Arcade-Style" score over them on unity 5, but, I don´t really know how to do it, already tried with an OnCollisionEnter(), but I did something wrong and it didn't worked, What can I do? Below you can see my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Diana : MonoBehaviour {
public GameObject diana;
public AnimationClip Score;
private Animation myAnimation;
/* IEnumerator Wait()
{
myAnimation = GetComponent<Animation>();
myAnimation.Play("DianaScore");
yield return new WaitForSeconds(3);
} */
void OnTriggerEnter(Collider other)
{
Debug.Log("Funcó wacho");
myAnimation = GetComponent<Animation>();
myAnimation.Play("DianaScore");
// StartCoroutine(Wait());
}
void Update () {
}
}
(Sorry for my bad english, I´m argentinian and new in all this coding thing)
I suggest putting this line of code: myAnimation = GetComponent(); into a Awake() function. If the above method does not work, I suggest checking that the Is Trigger is checked for OnTriggerEnter. If the console message (Debug.Log) shows, the problem might be something to do to the animation not the script. Last but not least, check that the bullet actually collides. I know this might sound crazy, but sometimes the bullet might be able to "not get detected" if it has enough velocity. Hope these suggestions work :D .
I want to achieve playing the music one by one in Unity. I tried to create an audioClip array and want to play them one by one. And i also tried to use StartCoroutine to wait one song finished and then playing the next.
I tried to create an audioClip array and want to play them one by one. And i also tried to use StartCoroutine to wait one song finished and then playing the next.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
[RequireComponent(typeof(AudioSource))]
public class AudioManager : MonoBehaviour {
public AudioClip[] _audioClips;
private AudioSource _audioSource;
void Awake(){
_audioSource = GetComponent<AudioSource>();
}
// Use this for initialization
void Start () {
for (int i = 0; i < _audioClips.Length;i++){
_audioSource.PlayOneShot(_audioClips[i]);
StartCoroutine("WaitForMusicEnd");
}
}
IEnumerator WaitForMusicEnd()
{
while (_audioSource.isPlaying)
{
yield return null;
}
}
}
However, the music will play at the same time. Please help!
you are not too far off, but this is not how coroutine work - you need to be insde a coroutine to be able to wait, here's how you do it
void Start ()
{
StartCoroutine(PlayMusic());
}
IEnumerator PlayMusic()
{
for (int i = 0; i < _audioClips.Length;i++)
{
_audioSource.PlayOneShot(_audioClips[i]);
while (_audioSource.isPlaying)
yield return null;
}
}
The control flow goes like this:
void Start()
{
StartCoroutine(Foo());
StartCoroutine(Bar());
}
IEnumerator Foo()
{
Debug.Log("FOO");
yield return null;
Debug.Log("foo");
}
IEnumerator Bar()
{
Debug.Log("BAR");
yield return null;
Debug.Log("bar");
}
// FOO
// BAR
// foo
// bar
If you watch what happens: With each coroutine start, the control goes into the coroutine, up until the first yield return. At this point we rewind the instruction pointer back to Start(), and start the second coroutine. Then Start() finishes, the frame is drawn. Unity keeps track of running coroutines, and before next frame will return control to where you left within your coroutine.
Its quite clever, as it makes spreading things in time much easier.
I searched for using WaitforSeconds and used as it was mentioned(using a return type of IEnumeration and using coroutines instead of update). but it did not work. Initially it showed Waitfor Seconds and IEnumerator were not "present in the current context". I had to r-install unity to get it fixed but this problem still remains. The following is my code. Am I using WaitforSeconds in correct way? Is it the 'if' code block that ruin my complete work(I mean have I used it in wrong place)?
using UnityEngine;
using System.Collections;
public class EnemyCreeator : MonoBehaviour
{
public float xmin,xmax,zmin,zmax;
public GameObject enemy;
public float spawnWait,StartWait,waveWait;
public int turretCount;
public int enemyCount;
int i=0;
void Update()
{
StartCoroutine (TurretWaves());
}
IEnumerator TurretWaves()
{
while (true)
{
Vector3 pos=new Vector3(Random.Range (xmin,xmax),0.5f,Random.Range(zmin,zmax));
yield return new WaitForSeconds(StartWait);
while(i<turretCount)
{
//Debug.Log ("The vaue of game time in spawnwaiting is: "+Time.time);
Instantiate (enemy, pos, Quaternion.identity);
enemyCount++;
i++;
//Debug.Log ("value of i in loop is: "+i);
//Debug.Log ("The vaue of game time is: "+Time.time);
yield return new WaitForSeconds (spawnWait);
}
//Debug.Log("cHECKing Before WAVE WAIT(value of time )is: "+Time.time);
yield return new WaitForSeconds(waveWait);
if(i>=turretCount)
{
i=0;
}
//Debug.Log("cHECKing AFTER WAVE WAIT and value of time is: "+Time.time);
//Debug.Log ("value of i outside the while loop is: "+i);
}
}
}
The code needs to wait until the spawnWait before spawning each turret and wait till the wavewait before spawning the next wave. even though the Startwait works fine, I still am unable to find the problem with others...
Thanks in advance.
Your code is correct, except
void Update()
{
StartCoroutine (TurretWaves());
}
By doing this, you are creating a new coroutine in every frame. Therefore, after Startwait seconds, each running coroutine will spawn an enemy in the following frames, making the overall script works incorrectly. But, in fact, each coroutine works as you expected.
To solve your problem, change
void Update()
to
void Awake()
or
void Start()
such that only one coroutine is started. It should work as expected.
Edit #1:
I think you misunderstood how coroutine works in Unity, because it seems that you are using Update to manually execute the code inside coroutine frame by frame.
When a coroutine is started by StartCoroutine, a "task" will be registered inside Unity. This task is like another thread (except it is still in the main thread). Unity will execute the code inside this task automatically. When it meets a yield statement, it pauses and waits for the statement to end. (Internally, it checks the state continuously and determine when it should resume.) For example, yield return new WaitForSeconds(n) will make Unity temporarily stops executing the code inside the coroutine for n seconds. And after that, it will keep going on.
The task will be deregistered until no more code should be executed (in your case, it never ends because of the while-loop), or the game object that started the coroutine is destroyed or deactivated.