2

I want users to be able to create my ScriptableObject assets outside of play mode so they can bind them to their objects in the scene. Since I don't want Zenject creating the ScriptableObjects, a Factory solution is not what I'm looking for. Therefore, I must somehow get the instance of these script objects to the Zenject installer and use 'QueueForInject'. Here are the two approaches I've found:

A) Manually adding these script objects to the Installer via the Inspector Window. Example:

public class GameInstaller : MonoInstaller<GameInstaller>
{
    // Visible in the inspector window so user can add Script Objects to List
    public List<ScriptableObject> myScriptObjectsToInject; 
    
    public override void InstallBindings()
    {
        foreach (ScriptableObject scriptObject in myScriptObjectsToInject)
        {
            Container.QueueForInject(scriptObject);  
        }
    }
}

B) Use Unity's AssetDatabase and Resources APIs to find all script object instances then iterate through the list to QueueForInject. Example

// search for all ScriptableObject that reside in the 'Resources' folder
string[] guids = AssetDatabase.FindAssets("t:ScriptableObject");
foreach (string guid in guids)
{
    // retrieve the path string to the asset on disk relative to the Unity project folder
    string assetPath = AssetDatabase.GUIDToAssetPath(guid);

    // retrieve the type of the asset (i.e. the name of the class, which is whatever derives from ScriptableObject)
    System.Type myType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);

    //Find the relative path of the asset. 
    string resourcesDirectoryName = $"/Resources/";
    int indexOfLastResourceDirectory = assetPath.LastIndexOf(resourcesDirectoryName) + resourcesDirectoryName.Length;
    int indexOfExtensionPeriod = assetPath.LastIndexOf(".");
    string assetPathRelative = assetPath.Substring(indexOfLastResourceDirectory, indexOfExtensionPeriod - indexOfLastResourceDirectory);

    //Grab the instance of the ScriptableObject.
    ScriptableObject scriptObject = Resources.Load(assetPathRelative, myType) as ScriptableObject;

    if (scriptObject == null)
    {
        Debug.LogWarning(
            "ScriptableObject asset found, but it is not in a 'Resources' folder. Current folder = " +
            assetPath);
        continue;
    }
    else
    {
        Container.QueueForInject(scriptObject);  
    }
}

With option A) the end user must remember to manually place the script object in the list for every new script object that they create. I'd rather find an automated way so the user doesn't have to know/remember this extra manual process.

With option B) I get an automated solution which, is great. But the end user must remember to store each ScriptableObject asset file in a directory called "Resources" because it's the only way the Resources.Load API will find the instance. I can warn the user if not found, which is nice. But they still are forced to comply to remove the warnings.

I can live with option B if I must, but I'd really like to take it a step further and make it completely invisible to the end user. Has anyone come up with a craftier automated solution for ScriptObjects that exist outside of Play Mode?

0 Answers0