I'm creating a Unity application targeting the Samsung Gear VR. I currently have two scenes:
The initial scene
Second scene, with big quantity of data (it takes too much time to load the scene).
From the first scene, I want to load the second scene in background, and switch to it once it has been loaded. While the new scene is loading in background, the user should keep the ability to move their head to see any part of the VR environment.
I'm using SceneManager.LoadSceneAsync but it's not working:
// ...
StartCoroutiune(loadScene());
// ...
IEnumerator loadScene(){
AsyncOperation async = SceneManager.LoadAsyncScene("Scene", LoadSceneMode.Single);
async.allowSceneActivation = false;
while(async.progress < 0.9f){
progressText.text = async.progress+"";
}
while(!async.isDone){
yield return null;
}
async.allowSceneActivation = true;
}
With that code, the scene never changes.
I've tried this the typical SceneManager.LoadScene("name") in which case the scene changes correctly after 30 seconds.
This should work
while(async.progress < 0.9f){
progressText.text = async.progress.ToString();
yield return null;
}
Secondly, I've seen cases where isDone is never set to true, unless the scene has activated. Remove these lines:
while(!async.isDone){
yield return null;
}
On top of that, you are locking your code in that first while loop. Add a yield so the application can continue loading your code.
So your entire code looks like this:
IEnumerator loadScene(){
AsyncOperation async = SceneManager.LoadAsyncScene("Scene", LoadSceneMode.Single);
async.allowSceneActivation = false;
while(async.progress <= 0.89f){
progressText.text = async.progress.ToString();
yield return null;
}
async.allowSceneActivation = true;
}
The biggest culprit to your problem is the locking in the first while loop, though.
Related
This has been fustrating me as a beginner/learning coder. I've been trying to make it so when my game finishes loading using an async method, it loads a button so you can continue. Instead, it ignores the function altogether, causing the button to still be there ingame and glitch out when you press it too early. I have my code right here; how can I fix this?
IEnumerator LoadSceneAsynchronously(int levelIndex)
{
AsyncOperation operation = SceneManager.LoadSceneAsync(levelIndex);
while (!operation.isDone)
{
Debug.Log(operation.progress);
loading.GetComponent<Animator>().Play("loading");
text.GetComponent<Animator>().Play("idle");
yield return null;
}
if (!operation.isDone)
{
yield return new WaitForSeconds(5);
loading.GetComponent<Animator>().Play("loading disappear");
text.GetComponent<Animator>().Play("appear text");
text.GetComponent<Animator>().Play("blink text")
yield return null;
}
}
public void DestroyLoading()
{
gameObject.SetActive(false);
GameIsDoneLoading = true;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space) && !GameIsDoneLoading == true)
{
Debug.Log("Space key was pressed.");
text.GetComponent<Animator>().Play("disappear text");
logo.GetComponent<Animator>().Play("fpsa logo disappear");
}
}
When you 'yield' in a Coroutine, code execution returns to the exact same place in the next frame, so in the while loop above it just checks that condition every frame, then yields to the next frame. I believe what you want to do is...
IEnumerator LoadSceneAsynchronously(int levelIndex)
{
// start the async loading
AsyncOperation operation = SceneManager.LoadSceneAsync(levelIndex);
// start playback of your animations in the scene
loading.GetComponent<Animator>().Play("loading");
text.GetComponent<Animator>().Play("idle");
// now wait for the scene to fully load
while (!operation.isDone)
{
Debug.Log(operation.progress);
yield return null;
// after yielding, we return here and repeat the while
// loop on the next frame
}
// we have broken free of the while loop so the level has now fully loaded
// and operation.isDone is now true
// play some other animations to fade out the loading system?
loading.GetComponent<Animator>().Play("loading disappear");
text.GetComponent<Animator>().Play("appear text");
text.GetComponent<Animator>().Play("blink text")
// I'm assuming these animations take about 5 seconds to complete?
// so just wait in here for 5
yield return new WaitForSeconds(5);
// loading level and animations all done...proceed to games
DestroyLoading();
}
I'm making my first 2D topdown shooter game. I'm working on the loading part of the game. I have this functioning loading screen function (a method of my GameManager class) I made. I tried swapping between the main title scene and the first level a few times to test it. After loading the first level, then the title screen, when I try loading the first level back again the load screen is stuck.
The whole thing is working on a coroutine, and after some debugging I figured out the problem was that this coroutine stopped executing in a certain part of the code for some reason.
public static bool isLoadingScene { get; private set; } = false;
public void LoadScene(int sceneIndex)
{
// If another scene is already loading, abort
if (isLoadingScene)
{
Debug.LogWarning($"Attempting to load scene {sceneIndex} while another scene is already loading, aborting");
return;
}
isLoadingScene = true;
var sceneLoader = FindObjectOfType<SceneLoader>().gameObject;
if (sceneLoader != null)
{
// Make the loading screen appear
var animator = sceneLoader.GetComponent<Animator>();
animator.SetTrigger("appear");
// Make sure the SceneLoader object is maintained until the next scene
// in order to not make the animation stop
DontDestroyOnLoad(sceneLoader);
}
else // If a SceneLoader is not found in the current scene, throw an error
{
Debug.LogError($"SceneLoader could not be found in {SceneManager.GetActiveScene().name}");
}
// Unload active scene and load new one
StartCoroutine(LoadScene_(sceneIndex, sceneLoader));
}
IEnumerator LoadScene_(int sceneIndex, GameObject sceneLoader)
{
float t = Time.time;
// Start loading the new scene, but don't activate it yet
AsyncOperation load = SceneManager.LoadSceneAsync(sceneIndex, LoadSceneMode.Additive);
load.allowSceneActivation = false;
// Wait until the loading is finished, but also give the loading screen enough
// time to appear
while (load.progress < 0.9f || Time.time <= (t + LoadingScreen_appearTime))
{
yield return null;
}
// Activate loaded scene
load.allowSceneActivation = true;
// Unload old scene
AsyncOperation unload = SceneManager.UnloadSceneAsync(SceneManager.GetActiveScene());
// Wait until it's finished unloading
while (!unload.isDone) yield return null;
// --- THE COROUTINE STOPS HERE DURING THE 3RD LOADING --- //
// Make the loading screen disappear
sceneLoader.GetComponent<Animator>().SetTrigger("disappear");
// Wait until the disappearing animation is over, then destroy the SceneLoader object
// which I set to DontDestroyOnLoad before
yield return new WaitForSeconds(LoadingScreen_disappearTime);
Destroy(sceneLoader);
isLoadingScene = false;
}
Only during the 3rd loading, right after the while (!unload.isDone) yield return null; line the coroutine just stops executing (I know it because I inserted a Debug.Log inside the while loop and it got called, but I inserted one right after and it did NOT get called).
Any suggestions?
I figured out the problem by myself. The GameManager itself was programmed as a singleton, and since there was a GameManager in both scenes that was messing things up. Had to move the GameManager to a separate scene loaded additively.
I'm making a practice game to get used to coding where you have to shoot a bird. When you run out of bullets, you need to press the 'r' key to reload your bullets. I want there to be a delay between when the button is pressed and when the bullets reload, but so far what I found is code that freezes everything (shown below). Is there a way to keep the code from freezing everything?
Summary: The code below freezes everything (the whole game) when pressing the 'r' button. Is there code I can use that won't freeze everything and will only wait 2 seconds before running the next action?
IEnumerator TimerRoutine()
{
if (Input.GetKeyDown(KeyCode.R))
{
yield return new WaitForSeconds(2); //Fix this, freezes everything
activeBullets = 0;
}
}
Use Coroutines To set This delay
if (Input.GetKeyDown(KeyCode.R) && isDelayDone) // defined isDelayDone as private bool = true;
{
// When you press the Key
isDelayDone = false;
StartCoroutine(Delay());
IEnumerator Delay()
{
yield return new WaitForSeconds(2);
isDelayDone = true;
activeBullets = 0;
}
}
Your problem is that you are waiting 2 seconds after the key press but not waiting for the actual key event it.
Here is a modified version of your method doing what you want.
IEnumerator TimerRoutine()
{
while(activeBullets == 0) // Use the bullets value to check if its been reloaded
{
if (Input.GetKeyDown(KeyCode.R)) // Key event check each frame
{
// Key event fired so wait 2 seconds before reloading the bullets and exiting the Coroutine
yield return new WaitForSeconds(2);
activeBullets = reloadBulletsAmount;
break;
}
yield return null; // Use null for the time value so it waits each frame independant of how long it is
}
}
(I know this has an accepted answer I just feel this method would be better)
Recently I have decided to start creating a game, and during this time I have run into many problems, most I have been able to fix myself but I'm having trouble with this. Basically, I want my code to make a Box Collider appear when clicked, and then disappear after a certain amount of time.
Here's my code:
void Start()
{
StartCoroutine(bruh())
}
void Update()
{
IEnumerator bruh()
{
if (Input.GetMouseButtonDown(0))
{
this.GetComponent<BoxCollider2D>().enabled = true;
yield return new WaitForSeconds(2);
}
else
{
this.GetComponent<BoxCollider2D>().enabled = false;
}
}
}
Your co-routine wasn't doing anything after it resumed from the yield return. I assume you want;
...
this.GetComponent<BoxCollider2D>().enabled = true;
yield return new WaitForSeconds(2);
this.GetComponent<BoxCollider2D>().enabled = false;
...
Possible new up the WaitForSeconds and then return that because it looks like you are just creating it not using it.
I use the function Application.LoadLevelAsync (s); in my game but it just stops at 89%. Help me. It's my code:
public void LoadScenes (string s)
{
if (!quit) {
SoundController.PlaySound (soundGame.ButtomClick);
progressBars.gameObject.SetActive (true);
background.SetActive (true);
text.gameObject.SetActive (true);
async = Application.LoadLevelAsync (s);
async.allowSceneActivation = false;
StartCoroutine (DisplayLoadingScreen ());
}
}
IEnumerator DisplayLoadingScreen ()
{
while (!async.isDone) {
loadProgress = (int)(async.progress * 100);
text.text = "Loading Progress " + loadProgress + "%";
progressBars.size = loadProgress;
yield return null;
}
if (async.isDone) {
Debug.Log ("Loading complete");
async.allowSceneActivation = true;
}
}
When you set async.allowSceneActivation = false, the loading stops at 90% or 0.9. This is because unity saves the last 10% to actually show you the scene that has been loaded. So 0 to 0.9 is when the actual loading process takes place and the last .1% is for displaying the loaded scene.
So in your case, you are checking for async.isDone which would become true only when progress is 100% but you have also set async.allowSceneActivation = false which would stop the progress at 90%. What you might want to check is async.progress >=0.9f and then set your async.allowSceneActivation = true.
Reference: https://docs.unity3d.com/ScriptReference/AsyncOperation-progress.html
From my experience, AsyncOperation.progress can cap out at about 89% as you've discovered. It may look like it's stalled at that message, when really it's still loading for however long it needs. It won't ever reach 100% even when it's done though, so it's rather useless if you want to implement a loading bar.
As such, async.isDone might never become true.
As a workaround, you could set async.allowSceneActivation = true; sooner than later, ignoring async.progress altogether:
async = Application.LoadLevelAsync (s);
async.allowSceneActivation = true;
I tend to use a progress-agnostic rotating loading icon instead of a loading bar because of this issue.
On a side note that I haven't tested, others have said that this is an Editor-only issue; async.progress and async.isDone might actually work on standalone builds.