Good morning, all, I figured I’d post this here since I have been stuck on this for a week now. I have scriptable objects, in this case that show a list of passive upgrades that you can apply on level up. Somehow they all work fine in the editor, i.e. the potion fills up your health, the max health increases your max health, speed, damage etc. It all works in the editor, but suddenly in the build and play it throws this error.
Level Up! Now Level: 2
=== ShowLevelUpUI START ===
Available Upgrades: 8
Upgrade Loaded: Armor, Type: Armor, Effect: 0.05
Upgrade Loaded: Velocity, Type: AttackSpeed, Effect: 1
Upgrade Loaded: Damage, Type: Damage, Effect: 0.5
Upgrade Loaded: Speed, Type: Speed, Effect: 0.2
Upgrade Loaded: Regen++, Type: HealthRegen, Effect: 0.05
Upgrade Loaded: Drink!, Type: HealthPotion, Effect: 20
Upgrade Loaded: MaxHealth, Type: Health, Effect: 0
Upgrade Loaded: XPMagnetism, Type: XP_Magnet, Effect: 0.05
Chosen Upgrade: XPMagnetism
Chosen Upgrade: Armor
Chosen Upgrade: Speed
Assigned Button 0 to XPMagnetism
Assigned Button 1 to Armor
Assigned Button 2 to Speed
Mouse Click Registered in Build
Button 1 clicked - will apply: Armor
=== Applying Upgrade: Armor, Type: Armor ===
NullReferenceException: Object reference not set to an instance of an object
at GameManager.ApplyUpgrade (PassiveUpgrade upgrade) [0x001eb] in <60b7e24ff9bb49888a9b634c8a9fb9d2>:0
at GameManager.SelectUpgrade (PassiveUpgrade chosenUpgrade) [0x00000] in <60b7e24ff9bb49888a9b634c8a9fb9d2>:0
at GameManager+<>c__DisplayClass39_1.b__0 () [0x00056] in <60b7e24ff9bb49888a9b634c8a9fb9d2>:0
at UnityEngine.Events.InvokableCall.Invoke () [0x00010] in <9797f11fd4464efd956e83c7ca4e1986>:0
at UnityEngine.Events.UnityEvent.Invoke () [0x00022] in <9797f11fd4464efd956e83c7ca4e1986>:0
at UnityEngine.UI.Button.Press () [0x0001c] in <112f91bf5ea2491a833cfc57cb249ba4>:0
at UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) [0x00009] in <112f91bf5ea2491a833cfc57cb249ba4>:0
at UnityEngine.EventSystems.ExecuteEvents.Execute (UnityEngine.EventSystems.IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) [0x00007] in <112f91bf5ea2491a833cfc57cb249ba4>:0
at UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1] functor) [0x00067] in <112f91bf5ea2491a833cfc57cb249ba4>:0
UnityEngine.DebugLogHandler:Internal_LogException_Injected(Exception, IntPtr)
UnityEngine.DebugLogHandler:Internal_LogException(Exception, Object)
UnityEngine.DebugLogHandler:LogException(Exception, Object)
UnityEngine.Logger:LogException(Exception, Object)
UnityEngine.Debug:LogException(Exception)
UnityEngine.EventSystems.ExecuteEvents:Execute(GameObject, BaseEventData, EventFunction`1)
UnityEngine.InputSystem.UI.InputSystemUIInputModule:ProcessPointerButton(ButtonState&, PointerEventData)
UnityEngine.InputSystem.UI.InputSystemUIInputModule:ProcessPointer(PointerModel&)
UnityEngine.InputSystem.UI.InputSystemUIInputModule:Process()
UnityEngine.EventSystems.EventSystem:Update()
BREAK
As you can see I tried debugging it by adding logs and show what’s been loaded regarding the upgrades.
Here is how I declare the variables in my GameManager script:
> //New variables for Upgrade text and passive upgrades
> public GameObject levelUpPanel; // Assign in Inspector
> public TMP_Text[] upgradeTexts; // Assign the three text elements from buttons
> public Button[] upgradeOptions; // These are the actual buttons
>
> public List<PassiveUpgrade> availableUpgrades = new List<PassiveUpgrade>(); // Stores all available upgrades
>
> //Add a dictionary to store upgrade levels
> private Dictionary<PassiveUpgrade.UpgradeType, int> upgradeLevels = new Dictionary<PassiveUpgrade.UpgradeType, int>();
//NEW FUNCTIONS BELOW TO SHOW LEVEL UP UI
private IEnumerator ShowLevelUpUIDelayed()
{
yield return new WaitForSecondsRealtime(0.1f); // Small delay before showing UI
ShowLevelUpUI(); // Call the actual function
}
private void ShowLevelUpUI()
{
Debug.Log("=== ShowLevelUpUI START ===");
Debug.Log("Available Upgrades: " + availableUpgrades.Count);
foreach (var upg in availableUpgrades)
{
if (upg != null)
Debug.Log($"Upgrade Loaded: {upg.upgradeName}, Type: {upg.upgradeType}, Effect: {upg.effectValue}");
else
Debug.LogError("Null upgrade found in availableUpgrades list!");
}
Time.timeScale = 0f; // Pause the game
levelUpPanel.SetActive(true);
int numUpgradesToShow = Mathf.Min(availableUpgrades.Count, 3);
if (numUpgradesToShow == 0)
{
Debug.LogError("No available upgrades! Closing Level-Up UI.");
levelUpPanel.SetActive(false);
Time.timeScale = 1f;
return;
}
List<PassiveUpgrade> chosenUpgrades = new List<PassiveUpgrade>();
while (chosenUpgrades.Count < numUpgradesToShow)
{
PassiveUpgrade randomUpgrade = availableUpgrades[Random.Range(0, availableUpgrades.Count)];
if (randomUpgrade != null && !chosenUpgrades.Contains(randomUpgrade))
{
chosenUpgrades.Add(randomUpgrade);
Debug.Log($"Chosen Upgrade: {randomUpgrade.upgradeName}");
}
}
for (int i = 0; i < numUpgradesToShow; i++)
{
if (chosenUpgrades[i] == null)
{
Debug.LogError($"Chosen upgrade at index {i} is NULL!");
continue;
}
upgradeTexts[i].text = chosenUpgrades[i].upgradeName;
upgradeOptions[i].GetComponentInChildren<Image>().sprite = chosenUpgrades[i].icon;
int index = i; // Prevent closure issue
upgradeOptions[i].onClick.RemoveAllListeners();
upgradeOptions[i].onClick.AddListener(() =>
{
Debug.Log($"Button {index} clicked - will apply: {chosenUpgrades[index].upgradeName}");
SelectUpgrade(chosenUpgrades[index]);
});
Debug.Log($"Assigned Button {i} to {chosenUpgrades[i].upgradeName}");
}
// Hide unused buttons if fewer than 3 upgrades exist
for (int i = numUpgradesToShow; i < upgradeOptions.Length; i++)
{
upgradeOptions[i].gameObject.SetActive(false);
}
Canvas.ForceUpdateCanvases();
LayoutRebuilder.ForceRebuildLayoutImmediate(levelUpPanel.GetComponent<RectTransform>());
}
Above is how I show the LevelUpUI and below is how I select and apply the upgrades.
//FUNCTION TO APPLY THE CHOSEN UPGRADE
public void SelectUpgrade(PassiveUpgrade chosenUpgrade)
{
ApplyUpgrade(chosenUpgrade); // Apply the selected upgrade
levelUpPanel.SetActive(false); // Hide UI
Time.timeScale = 1f; // Resume game
}
//APPLY THE UPGRADES
private void ApplyUpgrade(PassiveUpgrade upgrade)
{
if (upgrade == null)
{
Debug.LogError("Upgrade is NULL in ApplyUpgrade!");
return;
}
Debug.Log($"=== Applying Upgrade: {upgrade.upgradeName}, Type: {upgrade.upgradeType} ===");
if (!upgradeLevels.ContainsKey(upgrade.upgradeType))
upgradeLevels[upgrade.upgradeType] = 0;
if (upgradeLevels[upgrade.upgradeType] < 10)
{
upgradeLevels[upgrade.upgradeType]++;
float totalBonus = upgrade.effectValue * upgradeLevels[upgrade.upgradeType];
switch (upgrade.upgradeType)
{
case PassiveUpgrade.UpgradeType.Speed:
playerController.moveSpeed += upgrade.effectValue;
break;
case PassiveUpgrade.UpgradeType.Damage:
playerWeapon.IncreaseDamage(upgrade.effectValue);
Debug.Log($"Damage increased! New multiplier: {playerWeapon.GetDamageMultiplier()}");
break;
case PassiveUpgrade.UpgradeType.AttackSpeed:
playerWeapon.fireRate += upgrade.effectValue;
playerWeapon.fireRate = Mathf.Clamp(playerWeapon.fireRate, 0.1f, 5f);
break;
case PassiveUpgrade.UpgradeType.Health:
float previousMax = playerHealth.maxHealth;
playerHealth.maxHealth += 20;
if (playerHealth.CurrentHealth == previousMax)
playerHealth.CurrentHealth = playerHealth.maxHealth;
GameManager.instance.UpdateHealthUI(playerHealth.CurrentHealth, playerHealth.maxHealth);
break;
case PassiveUpgrade.UpgradeType.XP_Magnet:
xpMagnetRangeBonus += upgrade.effectValue;
break;
case PassiveUpgrade.UpgradeType.Armor:
playerHealth.armor += upgrade.effectValue;
break;
case PassiveUpgrade.UpgradeType.HealthPotion:
playerHealth.RestoreHealth((int)(playerHealth.maxHealth * 0.2f));
break;
case PassiveUpgrade.UpgradeType.HealthRegen:
if (!isHealthRegenActive)
{
isHealthRegenActive = true;
StartCoroutine(HealthRegenOverTime());
}
break;
}
Debug.Log($"✅ Applied {upgrade.upgradeName}, New Level: {upgradeLevels[upgrade.upgradeType]}");
}
}
I also wanted to show below my script for the passive upgrades.
using UnityEngine;
[CreateAssetMenu(fileName = "New Passive Upgrade", menuName = "Upgrades/Passive Upgrade")]
public class PassiveUpgrade : ScriptableObject
{
public string upgradeName; // Name of the upgrade (e.g., "Muscle Arm")
public string description; // Short description (e.g., "Increases damage by 2%")
public Sprite icon; // Icon to display in the UI
public float effectValue; // The value this upgrade increases (e.g., 2% = 0.02)
public enum UpgradeType {Damage, Speed, AttackSpeed, Health, Armor, HealthRegen, HealthPotion, XP_Magnet}
public UpgradeType upgradeType; // Defines what the upgrade affects
}
I can’t for the life of me figure out where this NullReferenceException is. From my understanding it is somehwere in the ApplyUpgrade function. What gets me is, why is this working just fine in the editor, but on build it isn’t? It’s almost like a script doesn’t run, or something doesn’t get initialized correctly and it can’t find the available upgrades?
I’m not asking for your to fix my script, but rather help me narrow it down and help me understand what is actually causing it. Your help is much appreciated ![]()
