U
04 / 22 · 12 min
FAQ

Black screen on a phone while the editor runs fine

the short answer

Why is my Unity game a black screen on the phone?

A black screen on a device is four different bugs with one face: the boot scene missing from Build Scenes, a scene with no enabled camera, a full-screen UI Image on top of everything, or a shader the phone will not run. Sort them by sound and timing in sixty seconds, then let adb logcat -s Unity name the cause.

A black screen on a phone is not one bug — it is four that look identical from the outside. Three of them you can sort out before opening a single file: does the sound keep running, does the picture come back after a few seconds, and does logcat say anything. Identify yours, then fix that one instead of rebuilding ten times on a guess.

1 — Split the black screens into three

After its logo, Unity itself paints black for a second or two, and a phone reaches the first scene far slower than your editor does. Before changing anything, spend ten seconds on which kind of black you actually have. Each one brings its own list of causes.

  • Black forever, the first scene never shows up: either the scene the phone boots into is not the one you think, or nothing in it is being drawn at all. Sections 2 and 3.

  • Black for a few seconds, then the game appears: a normal boot with no loading screen in front of it. Phone storage and a cold IL2CPP start are slower than the editor's cache, which you never wait for.

  • Black while the music keeps playing: the logic is alive and only the picture is missing. That is the camera, UI painted over it, or a shader the device refuses to run — sections 3, 4 and 5.

2 — The first scene is not in the build

A scene that is not in the Build Scenes list is not in the game. Not in the APK, not on the phone, not reachable by name — in a build that file simply does not exist. This one panel is why the sentence 'fine in the editor, black on the phone' exists at all.

  1. 1

    Open File▸Build Profiles (Build Settings before Unity 6), pick your platform, and look at the Scene List. Every scene the game can reach has to be on it — not only the one you start on.

  2. 2

    The row at index 0 is the scene the built game boots into. The editor ignores that number and opens whatever you last had in front of you — which is exactly why this bug only shows up on a device.

  3. 3

    Add every scene explicitly, then drag the menu or boot scene to index 0. A scene you never added can still be open in the editor and looking perfect.

If your code calls LoadScene for a name that is not in the list, Unity logs an error and the load does not happen — quietly, with no popup and no exception. Everything the code had already done stays done: the menu it hid, the camera it switched off, the objects it destroyed. You are not looking at a rendering failure but at the wreckage of a load that never arrived.

Prove which scene opened

One line ends this whole category of guesswork. Put a Debug.Log in the Awake of something in every scene, printing the scene's own name along with SceneManager.sceneCountInBuildSettings — how many scenes the build actually carries. logcat then answers both what the phone opened and whether what you wanted was even shipped.

Cover the wait instead of showing it

The second kind of black is not broken, it is unpolished: the engine is working and nothing is on screen while it does. The pattern is one object — a black veil that outlives the scene change, waits for the load, then fades out over the new scene.

FadeLoader.cs — put it on the Canvas itself, with a full-screen black Image as that Canvas's only child
using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;

// CanvasGroup and this script go on a ROOT Canvas. DontDestroyOnLoad does
// nothing to an object that has a parent, so the Canvas is what has to
// survive. Its only child is a black Image stretched over the whole screen.
[RequireComponent(typeof(CanvasGroup))]
public class FadeLoader : MonoBehaviour
{
    public float fadeTime = 0.35f;

    CanvasGroup veil;

    void Awake()
    {
        veil = GetComponent<CanvasGroup>();
        veil.alpha = 0f;

        // A faded-out veil must not swallow taps meant for the game.
        veil.interactable = false;
        veil.blocksRaycasts = false;

        DontDestroyOnLoad(gameObject);
    }

    public void Load(string sceneName)
    {
        StartCoroutine(Transition(sceneName));
    }

    IEnumerator Transition(string sceneName)
    {
        // 1. Go to black while the old scene is still there.
        yield return To(1f);

        AsyncOperation op = SceneManager.LoadSceneAsync(sceneName);

        // A scene missing from the Build Scenes list cannot load in a build:
        // Unity logs the error and hands back null. Without this guard the
        // next line throws and the veil stays up forever.
        if (op == null)
        {
            Debug.LogError("Not in Build Profiles: " + sceneName);
            yield return To(0f);
            yield break;
        }

        // 2. Wait. The veil is up, so the wait reads as black, not as broken.
        while (!op.isDone)
            yield return null;

        // 3. Fade the new scene in.
        yield return To(0f);
    }

    IEnumerator To(float target)
    {
        float from = veil.alpha;

        for (float t = 0f; t < fadeTime; t += Time.deltaTime)
        {
            veil.alpha = Mathf.Lerp(from, target, t / fadeTime);
            yield return null;
        }
        veil.alpha = target;
    }
}

The progress of an AsyncOperation stops at 0.9 while the scene waits its turn to appear, so a percentage bar wants op.progress / 0.9f, and allowSceneActivation = false is how you hold a finished scene back until the bar fills. That version, with a slider wired up, is in loading another scene.

3 — The camera has nothing to render

The screen is what the cameras say it is. A loaded scene with no enabled camera paints black — the editor's 'No cameras rendering' notice does not exist at runtime. Once you know a camera is there, the question changes from 'is it working' to 'what can it see'.

  • One enabled camera, tagged MainCamera. An unticked object or component renders nothing, and a camera parented under something your code disables at start goes down with it. NullReferenceException on Camera.main means nobody carries that tag.

  • The player spawns under the floor: a capsule starting below a Plane keeps falling, the follow camera goes with it, and everything leaves the far plane. In the editor you at least see the drop begin; on a phone you get black. A floor with no collider is the same story, and it is fixed in the scene, not in the camera.

  • Planes and masks: a far plane of 100 in a 500 metre level shows sky, and a camera whose Culling Mask leaves out your layers draws nothing but the skybox. Both sit on the Camera component and both cost seconds to rule out.

There is a five-second way to split those apart, worth doing before you read further. Set the camera's Clear Flags to Solid Color and its Background Color to something loud — magenta, anything you would not mistake for an empty room — then build. The screen turns loud: the camera renders fine and your black was an empty view, so look at where it stands and what it culls. The screen stays black: there is no enabled camera in the scene the phone opened, and you are back to section 2.

If your manager is a singleton and its Awake setup depends on which scene ran first, that is the real bug; the four rules that keep a GameManager honest are what fix it.

4 — UI painted over the whole screen

A Canvas in Screen Space - Overlay draws on top of whatever the cameras produce, always, regardless of where anything sits in the scene. One full-screen Image in it — a background, a fade veil, a panel you forgot — and the game is running with a lid on. Sound plus black is this section or the camera section, and nothing else.

  • The veil that never lifts: a black Image whose alpha the code was meant to bring back to 0. The code lived on an object the scene load just destroyed, or it waits on an event a mouse sends and a finger does not — reading Mouse.current in the new Input System hears nothing from touch, so the fade starts on your laptop and never on the phone.

  • Sibling order is draw order: the last child of a Canvas paints over the earlier ones, whatever the anchors say. A black Image added after the buttons, as an afterthought background, covers the buttons and everything behind them.

  • Screen Space - Camera with a plane distance inside the near clip plane, or aimed at a camera that is not the one you think: the Canvas becomes one flat quad in front of the lens, usually black, with no interest in the world behind it.

Two builds settle it. Uncheck the Canvas in the Hierarchy and build: the world appears, so it is the UI and you know which object to open. Still black: run the Clear Flags test above, and the answer is camera or scene. Neither moved anything — the UI is innocent, and the two remaining causes are the next sections.

5 — A shader the phone will not run: pink, and black

Pink and black come from the same place — the GPU was handed something it could not use — but they say different things, and the difference tells you what to fix. Pink is loud and honest: no subshader in that file supports this platform, so Unity paints its error material. Black or invisible is quieter: the shader was accepted, and the piece of it you needed at that moment was not there.

  • Pink everywhere, in the editor too: a pipeline mismatch. Project Settings > Graphics names the render pipeline the project uses — with a URP asset there, Built-in Standard materials go pink; with nothing there, a project full of URP/Lit materials does. Fix it in the materials or the pipeline, never in the lighting.

  • Pink on the phone and clean in the editor: the graphics API. Drivers disagree about what they support, and a subshader asking for shader model 4.5, compute shaders or tessellation can be refused by a phone that is happy with OpenGLES3 and grumpy with Vulkan — or the other way round.

  • Invisible or black objects on the device that were fine in the editor: the variant your runtime picked was stripped out of the build. Unity keeps only the variants it has seen used, and a keyword your code switches on at second five was never seen. Put the shader in Project Settings > Graphics > Always Included Shaders, or warm a ShaderVariantCollection while the loading screen is up.

  1. 1

    Open Project Settings▸Player, switch to the Android tab, go to Other Settings, and untick Auto Graphics API.

  2. 2

    Remove Vulkan, keep OpenGLES3, build, run. Black gone: that chipset does not like something your shaders ask for. Put Vulkan back at the bottom of the list and test once more before you decide.

  3. 3

    Still black on OpenGLES3: the graphics API was not it. Two causes ruled out in five minutes — go read the log, section 7.

6 — Weak devices, and the first frames that never arrive

The phone allocates its render targets the moment the first scene loads, at the same time it is pulling in textures, shadow maps and post-processing — a few hundred megabytes asked for inside one frame on a mid-range device. A desktop that cannot pay hands you an exception; many mobile drivers hand you a surface nothing is ever drawn to, and no message at all.

  • HDR and MSAA in the URP asset: both expensive on tile-based mobile GPUs, and both with a history of black first frames on specific Adreno and Mali drivers. Untick both on the mobile asset and build. You lose almost nothing on a 6-inch screen and you may end the whole investigation.

  • Realtime shadows and quality level: Max Distance 40, Cascade Count 1, resolution 1024, and remember that a build uses the level ticked for Android in Project Settings > Quality, not the one on the editor's toolbar. Different level on the phone means you have been testing a different game.

  • Texture memory: a build that is perfect on a flagship and black on a four-year-old mid-ranger is usually over budget. Max Size 1024, ASTC 6x6 on Android, Read/Write Enabled off — the same numbers the rest of the course uses.

  • Ask for 30 and stop chasing 60: set Application.targetFrameRate = 30 in the Awake of whatever boots the game, along with Screen.sleepTimeout. A device at its limit holding a steady 30 is playable; one stalling between 60 and 15 during boot can look dead long enough to be reported as a black screen.

7 — logcat is the only way to stop guessing

Everything above is a hypothesis. The phone already knows which one is true, and it says it out loud the moment you plug it in: one command, and the first twenty lines name the scene that loaded, the graphics API the device picked, and the exception that ate the first frame.

terminal
# start clean, then show only Unity's own lines
adb logcat -c && adb logcat -s Unity

# when the app closes itself: Unity plus the Android crash report
adb logcat -c && adb logcat Unity:V CRASH:V AndroidRuntime:E '*:S'

# write it to a file so you can search it calmly
adb logcat -s Unity > phone.txt
  • The line naming Vulkan or OpenGLES3, right after the GPU name — the API the phone actually chose. If it is not the one you tested with, the graphics API list is where you go back to, not the lightmaps.

  • Your own boot log from section 2. No line, no scene: the build did not open what you thought it opened.

  • NullReferenceException inside Awake or Start, or Scene ... has not been added to the build settings. The first means your manager reaches for something that only exists in the scene you forgot to add. The second is section 2, and the only line in this guide that is a finished answer rather than a clue.

  • Out of memory, GL_OUT_OF_MEMORY, Failed to create — section 6. Or a wall of nothing after the Unity version line: the app died before rendering ever started, which is an install, ABI or signing problem, not a black screen.

Sixty seconds to know which black screen it is

  • ✓Sound running? Then the logic is alive and the problem is rendering: camera, then UI, then shaders.
  • ✓Came back on its own after a few seconds? Nothing is broken — you have no loading screen. The veil in section 2 is the fix.
  • ✓App closed itself? That is a crash, not a black screen — run logcat before you change a single setting.
  • ✓Scene in the Build Scenes list, and at index 0? Names are case-sensitive, spelled like the .unity file.
  • ✓Clear Flags Solid Color, background magenta, build once: magenta screen = camera works and sees nothing; black = no enabled camera.
  • ✓Canvas unchecked, world suddenly visible: the UI was the lid. And Vulkan removed with the black gone is a driver problem, not your scene.

Once the picture is there, the phone starts complaining about something else: the frame rate after five warm minutes, and the UI built at the shape of your monitor. The screen-shape guide and the build-and-debug walkthrough are what follow from here.

the lesson that builds this

Build, install, debug on device

This post is a standalone recipe. In the course, the same thing is built as part of the one project that runs through all five tabs — Export to Mobile, lesson 05.