← All posts

The 5 Unity Problems That Eat Entire Weeks (and How to Fix Them)

· Updated July 15, 2026

UnityDebuggingBuild PipelineBest Practices
Five Unity problems that eat entire weeks

I’ve been building Unity games for over a decade, and the surprising part isn’t how much the engine has changed. It’s how little the painful parts have. The problems that cost me weeks in 2014 are the same ones costing my clients weeks today: builds that break even though the editor was fine, projects that balloon to sizes nobody can explain, codebases nobody dares to refactor, scenes two people can’t touch at once, and failures that don’t announce themselves until a player finds them.

This is the field guide I wish someone had handed me earlier. Five problems, why they happen, and what actually works against them.

1. It works in the editor but breaks in the build

A few years back I shipped a puzzle game where the daily-reward popup simply never appeared on Android. In the editor it worked every single time. No exception in logcat, no error during the build. The culprit was IL2CPP code stripping: the reward UI was instantiated through reflection, the linker decided the class was unreachable, and Unity happily shipped the game without it.

That’s the pattern with editor-versus-build bugs. The build isn’t broken loudly; it’s broken silently, and the editor never lies to you the same way twice. The usual suspects, roughly in the order I check them:

Managed code stripping. IL2CPP removes code it believes is unreachable. Anything touched only through reflection, or deserialized from AssetBundles, is a candidate for deletion. A link.xml that preserves the affected assemblies fixes it, but you have to know to look.

Case sensitivity. Resources.Load("UI/Popup") works on Windows and macOS, where file systems forgive you, and returns null on Android and Linux, where they don’t. Same for Addressables keys and StreamingAssets paths.

Scenes missing from Build Settings. Somebody deletes or renames a scene, Build Settings keeps the stale entry, and SceneManager.LoadScene throws at runtime on a code path QA didn’t hit. I’ve seen this one survive all the way to a store submission.

#if UNITY_EDITOR leaks. Code that quietly depends on something editor-only compiles fine in the editor and vanishes in the player. Usually it’s a utility method someone moved without checking who called it.

The boring advice is the correct advice: make a device build in week one, not week eight, and read the actual logs. On Android that’s adb logcat -s Unity; on desktop it’s the player log, which contains the stack trace the screen never showed you. Half of these failures are also detectable before you ever hit Build, which is a theme I’ll come back to. And if you’re fighting this one right now, I’ve written a full diagnostic guide for editor-versus-build bugs.

2. The build is 400 MB and nobody can say why

A client once brought me a hybrid-casual game whose APK had crossed 400 MB. Nobody had done anything wrong, exactly. Four artists and two years had just happened to it.

Unity gives you a surprisingly good forensic tool for this and buries it: after every build, the Editor log contains a full listing of what shipped, sorted by size. Open ~/Library/Logs/Unity/Editor.log (or %LOCALAPPDATA%\Unity\Editor\Editor.log on Windows) and search for “sorted by uncompressed size”. The top twenty lines usually explain everything.

In my experience the whales are always the same. Textures imported at 4096 that render at a tenth of that size on screen. Music sitting in memory as uncompressed PCM because nobody set the clip to streaming Vorbis. The same texture imported three times in three folders by three people. One video file someone dropped in for a prototype and forgot.

One misconception worth clearing up while you’re in there: assets that nothing references don’t ship in your build, unless they live in Resources or StreamingAssets. Deleting unused assets shrinks your repository and your import times, which matters, but what shrinks the APK is fixing the assets that do ship: oversized imports, uncompressed audio, duplicates.

Doing this audit by hand is a solid afternoon of spreadsheet work. It’s one of the reasons I built a build-weight analyzer into RefSafe Pro, which walks the dependency graph from your Build Settings scenes and lists the heaviest assets actually shipping, next to an import-settings audit with one-click fixes. The Editor log trick is free, though, and you should use it either way; the complete audit workflow is in how to reduce Unity build size.

3. Everyone is afraid to rename anything

You can date a Unity project by the file names nobody has fixed. PlayerControllerNew.cs sitting next to PlayerControllerFinal2.cs isn’t a naming problem, it’s a fear problem: at some point someone renamed something, references broke everywhere, and the team collectively decided never to touch anything again.

The fear comes from not understanding one mechanism. Unity doesn’t track assets by path; it tracks them by the GUID in each asset’s .meta file:

fileFormatVersion: 2
guid: 3f7b2c1a9d4e8f06b5a1c2d3e4f5a6b7

Every serialized reference in every scene and prefab points at that GUID. Which leads to a few rules that make renames boring instead of terrifying:

Move and rename files inside Unity (or in an IDE that understands meta files), never in Finder or Explorer. If the .meta doesn’t travel with the file, Unity generates a fresh GUID and every reference to the old one dies. Commit .meta files, always, even for folders. And keep MonoBehaviour class names matching their file names, or the script simply stops loading.

Field renames have their own trap: change hp to health and Unity silently discards every value anyone ever typed into the Inspector. There’s an attribute specifically for this, and it’s criminally underused:

[FormerlySerializedAs("hp")]
[SerializeField] private int health;

The other half of refactor fear is not knowing what depends on the thing you’re about to change. Unity’s built-in “Find References In Scene” only covers the scene you have open. For project-wide answers you need a dependency map; RefSafe Pro’s References tab does this with a searchable graph, which has turned several of my “am I about to break the game?” moments into a thirty-second check. The full rename playbook, from GUIDs to [FormerlySerializedAs], is in how to refactor a Unity project without breaking everything.

4. Two people edited the same scene

Scene merge conflicts are the closest thing game development has to stepping on a rake. A Unity scene is one huge YAML file full of numeric object IDs, and when two people edit it in parallel, git’s line-based merge produces soup. Sometimes it produces soup that loads, which is worse, because now you have a scene with quietly broken references that nobody notices until Thursday.

Nothing fully solves this, but four things together get close:

A prefab-first workflow. Keep the scene itself thin: a hierarchy of prefab instances and not much else. Content and logic live in prefabs, so people edit different files and the scene barely changes. This pairs well with data living in ScriptableObjects rather than scene objects, an approach I’ve written about before.

Additive scene splitting. Environment, lighting, and gameplay each in their own scene, loaded additively. Each scene gets an owner. The level designer and the lighting artist stop meeting in the same file.

UnityYAMLMerge. Unity ships a semantic merge tool with the editor and almost nobody wires it up. It merges by object rather than by line, and it resolves most conflicts that would wreck a textual merge:

[merge]
    tool = unityyamlmerge
[mergetool "unityyamlmerge"]
    trustExitCode = false
    cmd = 'UnityYAMLMerge' merge -p "$BASE" "$REMOTE" "$LOCAL" "$MERGED"

(The binary lives in the editor install, under Editor/Data/Tools on Windows and Unity.app/Contents/Tools on macOS.)

A social rule. “I’m in the arena scene today” said out loud in standup prevents more conflicts than any tooling. Cheap, unglamorous, effective.

Even with all four, bad merges occasionally land. The safety net is validation in CI, so a scene with dead references can’t reach main without someone being told. More on that in a moment; the complete setup, including the merge-day recovery playbook, is in my scene merge conflict survival guide.

5. The Inspector says “None” and means “gone”

Here’s the design decision that causes more silent Unity bugs than any other: a field that was never assigned and a field whose target was deleted look identical in the Inspector. Both say None. Under the hood they’re nothing alike; the second one still holds a file ID pointing at an asset that no longer exists. Unity knows the difference. It just doesn’t tell you.

The same silence covers a whole family of breakage. A UnityEvent wired in the Inspector keeps its binding as a string, so renaming the method breaks the button with zero console output; it’s a big part of why I prefer wiring events in code, which I covered in my post on event-based architecture. An Addressable AssetReference holds a GUID, so deleting the asset produces a reference that fails at load time, on device, in front of a player. Missing scripts don’t log anything until the scene loads, and prefabs nobody has opened in a month can carry them indefinitely.

You can hand-roll detection for the simplest case. This is a script I’ve rewritten in some form at three different studios:

using UnityEditor;
using UnityEngine;

public static class MissingScriptFinder
{
    [MenuItem("Tools/Find Missing Scripts in Open Scenes")]
    private static void Find()
    {
        foreach (var go in Object.FindObjectsByType<GameObject>(
                     FindObjectsInactive.Include, FindObjectsSortMode.None))
        {
            int missing = GameObjectUtility
                .GetMonoBehavioursWithMissingScriptCount(go);
            if (missing > 0)
                Debug.LogWarning($"{go.name} has {missing} missing script(s)", go);
        }
    }
}

Twenty lines, and genuinely useful. Also nowhere near enough: it only sees scenes that are open, and only missing scripts. It knows nothing about the other forty scenes, every prefab and ScriptableObject in the project, broken UnityEvents, dangling Addressables, or the scene someone deleted from disk but not from Build Settings.

After rewriting that script for the third time, I accepted the obvious and built the full version: RefSafe Pro scans the entire project for all of it, without opening a single scene by hand. (And if you want the serialization internals, including why None can mean two completely different things, that’s its own post.)

Let the machine do the checking

The thread running through all five problems is that none of them are hard to detect. They’re just impossible to detect by hand at any realistic project size, and they all stay invisible until the most expensive possible moment.

So the actual fix is procedural, not technical: make validation something that happens automatically, on every build or every commit, instead of something a human has to remember during crunch. That can be as small as the missing-script menu item above wired into a pre-build hook, or as complete as a full-project scan gating your CI pipeline with an exit code.

If you want the ready-made version, RefSafe Pro is on the Unity Asset Store, and I’ve written up how it covers each of these failure modes. Either way, build the habit before the habit is forced on you by a lost weekend. I speak from experience.

One time sink didn’t make this list because it deserved its own space entirely: the editor itself getting slower every month until a 34-second Play button feels normal. That one’s covered in Unity editor slow? How to fix import, domain reload, and compile times.

Common questions

Why does my Unity game work in the editor but break in the build?

Usually because IL2CPP stripped code it thought was unreachable, a file path differs in case on device, a scene is missing from Build Settings, or editor-only code leaked into a runtime path. Make a device build in the first week and read the real logs: adb logcat -s Unity on Android, or the player log on desktop. The editor is far more forgiving than the player, which is exactly why it hides these failures.

Do unused assets increase Unity build size?

No, assets that nothing references do not ship in the build, with two exceptions: anything in Resources or StreamingAssets ships regardless. Deleting unused assets shrinks your repository and import times, but the build only shrinks when you fix assets that do ship, like oversized texture imports, uncompressed audio, and duplicates.

How do I rename files in Unity without breaking references?

Rename and move files inside Unity or an IDE that understands .meta files, never in Finder or Explorer, so the GUID travels with the asset. Commit .meta files for everything, including folders. For renamed serialized fields, add the [FormerlySerializedAs] attribute so Inspector values survive the change.

How do I fix Unity scene merge conflicts in git?

Wire up UnityYAMLMerge, the semantic merge tool that ships inside every Unity editor install, so conflicts merge by object instead of by line. Then reduce how often conflicts happen at all: keep scenes thin with a prefab-first workflow, split levels into additive scenes with one owner each, and announce in standup who is working in which scene.

How do I find missing scripts in a Unity project?

For open scenes, a short editor script using GameObjectUtility.GetMonoBehavioursWithMissingScriptCount can list every offender. That only covers loaded scenes and only missing scripts, though; for project-wide checks across every scene, prefab, and ScriptableObject, you need a full-project scan, which is what I built RefSafe Pro to automate.