0

I've been following along with a simple RTS tutorial, but I've hit some kind of weird problem where the building scriptable objects I've created and placed in the Assets/Resources/ScriptableObjects/Units/Buildings folder are not being loaded at run time. Here is DataHandler.cs

public class DataHandler
{
    public static void LoadGameData()
    {
        Globals.BUILDING_DATA = Resources.LoadAll<BuildingData>("ScriptableObjects/Units/Buildings") as BuildingData[];
    }
}

Here is GameManager.cs which is attached to a GAME object in the scene

public class GameManager : MonoBehaviour
{
    private void Awake()
    {
        DataHandler.LoadGameData();
    }
}

Here is the Awake method for the UIManager (also attached to the GAME object)

private void Awake()
    {

        //create texts for each in-game resource (gold, wood, stone)
        _resourceTexts = new Dictionary<string, Text>();
        foreach (KeyValuePair<string, GameResource> pair in Globals.GAME_RESOURCES)
        {
            GameObject display = Instantiate(gameResourceDisplayPrefab);
            display.name = pair.Key;
            _resourceTexts[pair.Key] = display.transform.Find("Text").GetComponent<Text>();
            _SetResourceText(pair.Key, pair.Value.Amount);
            display.transform.SetParent(resourcesUIParent);
        }


        _buildingPlacer = GetComponent<BuildingPlacer>();

        //create buttons for each building type
        _buildingButtons = new Dictionary<string, Button>();
        for (int i = 0; i < Globals.BUILDING_DATA.Length; i++)
        {
            BuildingData data = Globals.BUILDING_DATA[i];
            GameObject button = Instantiate(buildingButtonPrefab);
            button.name = data.unitName;
            button.transform.Find("Text").GetComponent<Text>().text = data.unitName;
            Button b = button.GetComponent<Button>();
            _AddBuildingButtonListener(b, i);
            button.transform.SetParent(buildingMenu);

            _buildingButtons[data.code] = b;
            if (!Globals.BUILDING_DATA[i].CanBuy())
            {
                b.interactable = false;
            }
            
        }
    }

Every time I try to run the game I get this error:

NullReferenceException: Object reference not set to an instance of an object UIManager.Awake () (at Assets/Scripts/UIManager.cs:37)

which directly corresponds to the for loop. Through some experimenting I've seen that the problem is that the contents of the Buildings folder ("House" and "Tower" BuildingData objects) are not being loaded into Globals.BUILDING_DATA.

public static class Globals
{
    public static BuildingData[] BUILDING_DATA;
    public static int TERRAIN_LAYER_MASK = 1 << 8;

    public static List<UnitManager> SELECTED_UNITS = new List<UnitManager>();

    

    public static Dictionary<string, GameResource> GAME_RESOURCES = new Dictionary<string, GameResource>()
    {
        {"gold", new GameResource("Gold", 500) },
        {"wood", new GameResource("Wood", 500) },
        {"stone", new GameResource("Stone", 500) }
    };
}

Any ideas what could be going wrong? I tried to follow the tutorial as closely as possible to prevent this kind of problem.

  • I know for loading asset bundles it needs to be done in within a class that extends monobehaviour and probably there are other unity base classes that could be used. I strongly suspect it's the same here. Also it would have to be done in Awake or after. Even if the method is called from a monobehaviour if the loading code itself is in another class it won't work. – Omar Abdel Bari May 19 '21 at 01:31
  • So in simple words, scrap `DataHandler.LoadGameData();` and replace it with `Resources.LoadAll("ScriptableObjects/Units/Buildings") as BuildingData[];` . If that works, kindly let me know. Alternatively, you can load it in a monobehaviour or similar and then save it in a class similar to `DataHandler`. However I generally would advise against putting Unity objects like this in regular classes, since it has to be managed by UnityObjects anyway and if you have to unload the objects it's probably easier to deal with them in Unity objects as well. – Omar Abdel Bari May 19 '21 at 01:33
  • Thanks for the quick reply. I tried moving the Resources.LoadAll command into GameManager.cs but there's been no change. Still getting NullReferenceException – agentsmith200 May 19 '21 at 01:54
  • Did you try debugging the code to see what values are preset after loading the resources? – Omar Abdel Bari May 19 '21 at 02:26
  • NullReferenceException: Object reference not set to an instance of an object UIManager.Awake () (at Assets/Scripts/UIManager.cs:37) – agentsmith200 May 19 '21 at 02:31
  • I just tried putting the Resources.LoadAll statement in the UIManager Awake method itself and it seems to work now. It's really bizarre though, I'm following this tutorial exactly and they don't seem to be having this same problem. – agentsmith200 May 19 '21 at 02:34
  • I've had nothing but problems doing stuff like this in non Unity object classes. The thing I don't like is they don't explicitly mention it in the documentation. You have to catch the exception to learn how to fix it. – Omar Abdel Bari May 19 '21 at 02:37
  • The choice of duplicate for this post is not a valid one IMO. This is not a standard solution for `NullReferenceException`. Though I bet there are some existing posts for the same unity issue. – Omar Abdel Bari May 19 '21 at 02:39

0 Answers0