I’m building a level editor for my rhythm game. Each level is a ScriptableObject named LevelData that can be saved as a .lvl file using the JSONUtility methods.
LevelData class:
using System;
using System.Collections.Generic;
using UnityEngine;
[Serializable]
[CreateAssetMenu(fileName = "New Level Data")]
public class LevelData : ScriptableObject
{
public string fileName = "new_level";
public AudioClip songClip;
public int totalBars;
public float bpm;
[SerializeReference] public List<ObstacleEvent> obstacleEvents = new List<ObstacleEvent>();
public void Reset()
{
fileName = "new_level";
songClip = null;
totalBars = 0;
bpm = 0;
obstacleEvents.Clear();
}
}
LevelData when saved example:
Example
To manage the LevelData files, I have a LevelSelector that lists all .lvl files inside a “levels” folder.
LevelSelector print:
When the user clicks on the level “EDIT” button, a .lvl file is loaded, it overwrites a LevelData instance and gets passed to a LevelEditor scene.
Loading and overwriting LevelData process:
public async Task<string> GetLevelDataAsString(string levelFileName)
{
string result;
using (var reader = File.OpenText($"{Application.persistentDataPath}/levels/{levelFileName}.lvl"))
{
result = await reader.ReadToEndAsync();
}
return result;
}
public void OverwriteLevelData(LevelData oldLevelData, string newData)
{
JsonUtility.FromJsonOverwrite(newData, oldLevelData);
}
Everything works fine on Unity, but after building to the .exe file, a strange thing happens.
On build: When I try to edit an existing level before doing anything, a NullReferenceException gets thrown and part of the LevelData does not load properly on the LevelEditor scene (I made it so logs get printed on the build btw). But, if I try to open the same level after returning to the LevelSelector scene, everything works as expected and no exceptions appear.
The logged exception (first time opening):

Second time opening (nothing goes wrong, thats the obstaclePrefab.name):
![]()
The Start method where the exception gets thrown (inside the LevelEditor script):
private void Start()
{
levelData = VariablesManager.Instance.currentLevelData;
if (levelData.obstacleEvents.Count > 0)
Debug.Log(levelData.obstacleEvents[0].obstaclePrefab.name);
levelName.text = $"<b>Current loaded level:</b> {levelData.fileName}";
timingDisplay = FindObjectOfType<TimingDisplay>();
currentTiming = new Timing(1, 1);
UpdateAll();
}
(without this Debug.Log call, the exception doesn’t get thrown)
I’ve been searching for two days and found nothing helpful or at least no situation that looks like mine.
Thanks for taking your time <3

