Screen Fader
Simple screen fade-in/fade-out with configurable color and duration. Great for scene transitions.
How to Use
Create a UI Canvas with a full-screen Image
Attach ScreenFader and assign the Image
FadeIn: ScreenFader.Instance.FadeIn()
FadeOut: ScreenFader.Instance.FadeOut()
Scene transition: ScreenFader.Instance.FadeOutAndIn(0.5f, () => LoadScene())
Features
- Smooth alpha fade-in and fade-out with configurable duration
- FadeOutAndIn combo with callback action at the midpoint
- Configurable fade color for black, white, or custom transitions
- Uses unscaledDeltaTime so fades work even when Time.timeScale is 0
- Raycast blocking during fade to prevent input on covered UI
- Singleton pattern with DontDestroyOnLoad for cross-scene persistence
When to Use This
Use in any game that transitions between scenes, levels, or game states — platformers, RPGs, horror games, visual novels, and menu-heavy applications. Perfect for hiding loading seams during scene changes. The FadeOutAndIn method with a callback is ideal for triggering scene loads at the midpoint of a fade.
Common Mistakes
The fadeImage must be a UI Image on a Canvas set to Screen Space - Overlay, otherwise it won't cover the full screen. If the Image's RaycastTarget is left on permanently, it will block all UI clicks beneath it — the script manages this automatically, but manually toggling it can cause conflicts. Forgetting to call FadeIn() after a scene load leaves the screen black.
Source Code
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class ScreenFader : MonoBehaviour
{
public static ScreenFader Instance { get; private set; }
[SerializeField] private Image fadeImage;
[SerializeField] private float defaultDuration = 0.5f;
[SerializeField] private Color fadeColor = Color.black;
private void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
fadeImage.color = fadeColor;
fadeImage.raycastTarget = false;
}
public Coroutine FadeIn(float duration = -1f)
{
return StartCoroutine(Fade(1f, 0f, duration < 0 ? defaultDuration : duration));
}
public Coroutine FadeOut(float duration = -1f)
{
return StartCoroutine(Fade(0f, 1f, duration < 0 ? defaultDuration : duration));
}
public Coroutine FadeOutAndIn(float duration = -1f, System.Action onMiddle = null)
{
float d = duration < 0 ? defaultDuration : duration;
return StartCoroutine(FadeOutInRoutine(d, onMiddle));
}
private IEnumerator Fade(float from, float to, float duration)
{
fadeImage.raycastTarget = true;
float elapsed = 0f;
while (elapsed < duration)
{
elapsed += Time.unscaledDeltaTime;
float alpha = Mathf.Lerp(from, to, elapsed / duration);
fadeImage.color = new Color(fadeColor.r, fadeColor.g, fadeColor.b, alpha);
yield return null;
}
fadeImage.color = new Color(fadeColor.r, fadeColor.g, fadeColor.b, to);
fadeImage.raycastTarget = to > 0.5f;
}
private IEnumerator FadeOutInRoutine(float halfDuration, System.Action onMiddle)
{
yield return Fade(0f, 1f, halfDuration);
onMiddle?.Invoke();
yield return Fade(1f, 0f, halfDuration);
}
}