- Home /
My coroutine fader is not fading
I made a script to fade a scene to black starting from some examples, I tried to do it with coroutines rather than just doing it in update, with the hope that the fader would actually stop the calling object until it's done with the fading animation.
I also tried to dynamically generate the texture rather than making a big black texture manually.
In my controller, just before loading the new scene, I call
var fader = GameObject.FindObjectOfType<SceneFadeInOut>();
fader.StartFade();
here's the fader class
public class SceneFadeInOut : MonoBehaviour
{
private Texture2D _blackTexture;
public float FadeSpeed = 1.5f;
private bool _doFade = false;
private GUITexture _GUITexture;
public Color FadeTo;
public Color FadeFrom;
private void Awake()
{
_blackTexture = new Texture2D(1,1);
_blackTexture.SetPixel(0,0,Color.black);
_GUITexture = GetComponent<GUITexture>();
_GUITexture.texture = _blackTexture;
_GUITexture.pixelInset = new Rect(0,0,Screen.width,Screen.height);
}
public void StartFade()
{
StartCoroutine(Fade(_GUITexture, FadeSpeed, FadeFrom, FadeTo));
}
IEnumerator Fade(GUITexture mat, float duration, Color startColor, Color endColor)
{ // remember the start
float start = Time.time;
float elapsed = 0;
do
{ // calculate how far through we are
elapsed = Time.time - start;
float normalisedTime = Mathf.Clamp(elapsed / duration, 0, 1);
mat.color = Color.Lerp(startColor, endColor, normalisedTime);
// wait for the next frame
yield return null;
}
while (elapsed > duration);
}
}
When I do the transition the coroutine code seems to run but nothing really happens and then the new scene loads as normal
What could I be doing wrong?
Well, you said that you load a new level. When do you load it? Immediately after calling "StartFade"? Do you have a DontDestroyOnLoad somewhere on the script that does the fading? if not the gameobject will be destroyed when the new scene is loaded.
Or is that script in the new scene? If so, from where do you start your coroutine?
I'm calling that from another object when the new scene loads actually, I didn't get around to do the scene-end one yet
Wait, so you call "StartFade" before you call LoadLevel? That has several problems. First, as i asked already do you have a DontDestroyOnLoad call in your class somewhere? If not the gameobject will be destroyed when the new scene loads.
Second, Since you start the coroutine before you load the scene (now guessing the object will survive the level load) you will skip some of the time. That's because you grab the current time before the level load. If the level load takes 2 seconds for example then the time would jump 2 seconds ahead after the level load. Since you only use 1.5 second duration you probably won't see anything.
Have you seen this question:
http://answers.unity3d.com/questions/119918/transitions-between-changing-scenes.html
The code here seems correct. its likely either the things you're inputting into the class or the state of the ancestor gameobjects that this class is attached to that is the source of the issue. check the texture and colors your setting for the class, check if the gameobject is active in the hierarchy when the coroutine is called (also keep an eye open for canvasgroups). you could be running the coroutine while the gameobject is hidden due to a parent object being disabled or a canvas group setting the alpha to 0.
The texture is set in the awake event of the scene fade object and I'm 100% sure the caller object exists.
I could be setting the wrong texture colors on that event though, I don't know
Answer by MaddoScientisto · Oct 10, 2015 at 04:01 PM
I finally managed to find the solution so here's what I did:
First I scrapped the GUITexture and instead I made a RawImage and put it inside a canvas, then I attached the script to it. The RawImage doesn't need to have a texture set, just the color (to black).
Then the script is as follows:
public class SceneFadeInOut : MonoBehaviour
{
private RawImage _image;
private void Awake()
{
_image = GetComponent<RawImage>();
}
public IEnumerator FadeIn(float speed)
{
yield return StartCoroutine(Fade(_image, speed, Color.black, Color.clear));
}
public IEnumerator FadeOut(float speed)
{
yield return StartCoroutine(Fade(_image, speed, Color.clear, Color.black));
}
IEnumerator Fade(RawImage mat, float duration, Color startColor, Color endColor)
{
float start = Time.time;
float elapsed = 0;
while (elapsed < duration) {
// calculate how far through we are
elapsed = Time.time - start;
float normalisedTime = Mathf.Clamp(elapsed / duration, 0, 1);
mat.color = Color.Lerp(startColor, endColor, normalisedTime);
// wait for the next frame
yield return null;
}
}
}
And that's it, it just works, just call startcoroutine with fadein or fadeout and everything works neatly!
@dudester gave wrong info. If you're just waiting for the next frame, all you need is yield return null
.
Technically, WaitForEndOfFrame is at the end of the current frame. Not the next frame.
yield return null
also has the benefit of not allocating a new object every frame.
When are you calling FadeIn and FadeOut, that's the actual complicated part as you might get strange things like seeing the fade in only starting after all objects are loaded and displayed or the fade out never happening or fading out when the next level is loading.
When are you calling FadeOut, making sure the level stays loaded while doing so and only loads the next when the fade is done? Thats's the tricky part.
Answer by GameDev_Chuck · May 25, 2016 at 07:04 PM
A little late to the party, but here's my contribution. I use a ui Panel instead of a 2D Texture
using UnityEngine;
using System.Collections;
public class FadePanel : MonoBehaviour
{
// Add a fade panel to your UI Canvas
// Make the panel black (On image component,
// set image to none, make color black)
// Add a Canvas Group component to the panel
// Set the Alpha of the canvas group to 0
// Add this script to the panel
public CanvasGroup canvasGroup; // Drop the panel with the canvas
// group on this field in the inspector
// or make it private and use GetComponent
// in Awake()
public float fadeSpeed = 1f;
void Start() // If you want the panel to fade in when the scene loads
{
StartCoroutine("Fade");
}
public void FadePanelIn() // If you want to fade to black at some other time
{
StartCoroutine("Fade");
}
IEnumerator Fade()
{
while (canvasGroup.alpha != 1)
{
canvasGroup.alpha = Mathf.MoveTowards(canvasGroup.alpha, 1, fadeSpeed * Time.deltaTime);
yield return null;
}
}
}
So when are you calling fade to black then, I notice you didn't flesh that bit out.
Answer by dudester · Oct 09, 2015 at 01:08 PM
this in incorrect , the script does not run continuously due to the while being in the wrong place it should be like so
IEnumerator Fade(GUITexture mat, float duration, Color startColor, Color endColor)
{while (elapsed > duration){
// remember the start
float start = Time.time;
float elapsed = 0;
// calculate how far through we are
elapsed = Time.time - start;
float normalisedTime = Mathf.Clamp(elapsed / duration, 0, 1);
mat.color = Color.Lerp(startColor, endColor, normalisedTime);
// wait for the next frame
// yield return null; incorrect will stop coroutine
yield return new WaitForEndOfFrame();
}
}
I tried your code but the while up there seems to be marked wrong (did you mix up the while and do while syntax?) and WaitForUpdate doesn't exist, WaitForFixedUpdate exists though so I'm trying with that.
With this code I can see that it's actually doing the cycle correctly but the texture doesn't display, so I have a problem there too I guess
IEnumerator Fade(GUITexture mat, float duration, Color startColor, Color endColor)
{ // remember the start
float start = Time.time;
float elapsed = 0;
while (elapsed < duration) {
// calculate how far through we are
elapsed = Time.time - start;
float normalisedTime = $$anonymous$$athf.Clamp(elapsed / duration, 0, 1);
mat.color = Color.Lerp(startColor, endColor, normalisedTime);
// wait for the next frame
yield return new WaitForFixedUpdate();
}
}
appologys yes i had them mixed up i forgot to remove that from it , ill edit my answer to reflect that , also sorry i was thinking of the wrong thing its WaitForEndOfFrame(); not WaitForUpdate(); damn i really need to double check these things before hand , its better to use WaitForEndOfFrame(); cause it means your texture will fade correctly , also as to your problem are you just fading the alpha value? cause if not you need to do that .
Actually the original code of the OP is much more correct than yours. You put the declaration and initialization of the start variable inside the while which will make the while loop to never end.
I admit that the "do while" loop is usually rarely used, but it's still correct.
do
{
}
while(condition);
In this case here it doesn't matter if you put the condition athte beginning or at the end. However there are other cases where it does matter. A do while loop will always run through the loop body at least once, even when the condition is false:
do
{
Debug.Log("You'll see that")
}
while(false);
while(false)
{
Debug.Log("You'll NOT see that")
}
I think you're mistaken.
yield return null
doesn't stop the coroutine. It waits for the next update.
yield break
breaks out of the coroutine.
Your answer
Follow this Question
Related Questions
Fading In and Out the GUI 1 Answer
How do I fade a light point? 3 Answers
My image does not get transparent all the way! 1 Answer
Fade Out over length of object on Material? 0 Answers
Fading Not Working 1 Answer