Database empty on build (solved)

I’m having an issue where when I build the game (File > Build and run), and I run the game, I get errors that I don’t experience in the editor/game view (the game runs fine).

The build compiles just fine, no errors or anything unusual there. Though when I look at the logs when I run the games build outside the inspector, its showing that it cannot find the ‘grass’ tile and ‘animated cursor’ tile in the Tiles_List(which is a scriptable object attached to a MonoBehaviour on awake).

This is showing me (I think) that the database is completely empty, is this just how scriptable objects work? are they not for deployment?

I’ll attach some screenshots and code below (with the tile controller script, just pay attention to the init and OnEnable stuff)

Game Manager Script

using System.Collections;
using System.Collections.Generic;
using UnityEngine.Events;
using UnityEngine;

public class Game_Manager : MonoBehaviour {
    [Header("Tile/sprite/worldgen related controller", order = 2)]
    public TileController tileController;
    [Header("Input related controller", order = 2)]
    public InputController inputController;
    [Header("State related controller", order = 2)]
    public StateController stateController;

    public static Game_Manager Game; 

    private void Awake()
    {
            if (Game == null)
            {
                Game = this;                                             
                DontDestroyOnLoad(Game);                    
            }
            else if (Game != this)
            {
                Destroy(Game);
            }

        // Init Controllers (otherwise they cannot access ANY gameobject variables/scripts due to component load order
        tileController.init();

        // some test functions to test everything is working
        TileController.SendTask.create_World("grass");
        TileController.SendTask.setCursorDefault(new Vector3Int(1, 1, 0));

        //      Grouping tiles example code.
        //   TileController.SendTask.set_GroupTile(new Vector3Int(3, 3, 0), 0, "dirt", "ui");
        //   TileController.SendTask.set_GroupTile(new Vector3Int(3, 3, 0), 0, "dirt_lower", "ui");
    }

    private void Update()
    {
        InputController.SendTask.CheckInput();
    }
}

Tile Controller Script

using System.Collections;
using System.Collections.Generic;
using UnityEngine.Tilemaps;
using UnityEngine;

[CreateAssetMenu]
public class TileController : ScriptableObject {

    public static TileController SendTask;
    // Databases 
    private TileDatabase tileDatabase;
    private Tiles_List tiles; 
    private Tile_Generation tileGen;        // Do'ers
    private World_Setup worldConfig;        // Setup

    private void OnEnable()
    {
        if (SendTask == null) { SendTask = this; }     
    }

    public void init()
    {
        worldConfig = Game_Manager.Game.GetComponentInChildren<World_Setup>();
        tileDatabase = Game_Manager.Game.GetComponentInChildren<TileDatabase>();
        if(tileDatabase == null) { Debug.LogError("Critical Error in Initilization - TileDatabase does not have an instance. "); }

        tileGen = Game_Manager.Game.GetComponentInChildren<Tile_Generation>();
        tiles = Tiles_List._instance;               // Instanced as not attached to any GameObject                     
        if(tiles == null) { Debug.LogError("Critical Error in Initilization - Tiles_List not initiated. A new instance will be created"); tiles = ScriptableObject.CreateInstance<Tiles_List>(); }
    }

 
    // creates standard world full of dirt
    public void create_World(string worldTiles)
    {
        tileGen.GenerateWorld(worldConfig.World_Size.x, worldConfig.World_Size.y, getTile(worldTiles), tileGen.worldLayer);
    }

    // code to clear a tile
    public void clear_Layer(string _layer)
    {
        tileGen.ClearLayer(_layer);
    }

    // code to set a tile
    public void set_Tile(Vector3Int position, string tileName, string _layer)
    {
        if (getTile(tileName) != null)
        {
            tileGen.SetTile(position, getTile(tileName), tileGen.set_Layer(_layer));
            return;
        }     
        Debug.LogError("Controller: Set_Tile : - Unable to locate : " + tileName + " in the tile database. Please check name matches");
    }

    // code to combine and set a group of tiles into one (animates)
    public void set_GroupTile(Vector3Int position, int posInDB, string tileToAdd, string layer)
    {            
        tileDatabase.addToGroupTile(posInDB, getTile(tileToAdd).m_Sprite);     
        tileGen.SetTile(position, tileDatabase.GroupedTiles[posInDB], tileGen.set_Layer(layer));
    }

    public void setCursorDefault(Vector3Int defaultPos)
    {
        tileGen.setCursorLocation(defaultPos);     
        tileGen.SetTile(defaultPos, getTile("animated cursor"), tileGen.set_Layer("ui"));
    }

    public void moveCursor(Vector3Int direction)
    {

        Vector3Int newPosition = tileGen.getCursorLocation();
        newPosition.x += direction.x;
        newPosition.y += direction.y;

        tileGen.clearTile(tileGen.getCursorLocation(), tileGen.set_Layer("ui"));    // clears the previous position
        set_Tile(newPosition, "animated Cursor", "ui");
             
        tileGen.setCursorLocation(newPosition);     
    }

    public Vector3Int getCursorPosition()
    {
        return tileGen.getCursorLocation();
    }

    public GameTile getTileAtCursor(string layer)
    {
        Tilemap _layer = tileGen.set_Layer(layer);
        return (GameTile) _layer.GetTile(getCursorPosition());
    }

    // private code to assist with data location
    private GameTile getTile(string tilename)
    {
        for (int i = 0; i < tiles.TileList.Count; i++)
        {
            if (tiles.TileList[i].name.ToUpper() == tilename.ToUpper())
            {
                return tiles.TileList[i];
            }
        }
        Debug.LogError("Controller: findTile : - Unable to locate : " + tilename + " in the tile database. Please check name matches");
        return null;
    }

 
}

Just to update, I’ve ruled out any other scripts which may be calling functions before the Tiles_List has been instantiated.

On simple inspection your gameobject in the list is called “Grass” yet you are looking for “grass”. Same with “Animated Cursor”. Be case sensitive.

Thanks for the reply, I’ve accommodated for this in code to convert it to upper case for both search and match criterias, definitley would have caught me out though hadn’t I done that!.

 private GameTile getTile(string tilename)
    {
        for (int i = 0; i < tiles.TileList.Count; i++)
        {
            if (tiles.TileList[i].name.ToUpper() == tilename.ToUpper())
            {
                return tiles.TileList[i];
            }
        }
        Debug.LogError("Controller: findTile : - Unable to locate : " + tilename + " in the tile database. Please check name matches");
        return null;
    }

Good news, I found the problem. I didn’t attach the Tiles_List to a MonoBehaviour as previously thought, so the whole time it was linking to an empty script instance with no attached variables, and not the one I’d dragged/dropped stuff into via the inspector window.

Really really weird that it still worked in the editor though (I guess it does some magic and looks for any asset of in my folder and attaches that).