I noticed that I could declare UnityEngine.Object in my ScriptableObject and drag a folder to that field from the Project panel. So I could do AssetDatabase.GetAssetPath on it and do some preprocessing stuff. I copied this approach from SpriteAtlas’s packing box where I can drag a folder to it.
However I can drag anything that has a .meta file on that box. Is there a type other than UnityEngine.Object that allows only folder on it? I have look through the side bar of UnityEngine namespace in the docs and found nothing that is likely to be that. Thanks.
Unfortunately I don’t think so. The way I’ve handle this before is either using UnityEditor.DefaultAsset type (which will still accept some .meta files, but will filter out a lot of them) or custom property drawer where you handle the check for the type. Here’s a quick example of how I’m using it right now:
public class EntityHolder : ScriptableObject
{
[System.Serializable]
public struct Foo
{
public UnityEngine.Object obj;
}
public Foo test;
}
[CustomPropertyDrawer(typeof(EntityHolder.Foo))]
public class IngredientDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
SerializedProperty value = property.FindPropertyRelative("obj");
UnityEngine.Object prevValue = value.objectReferenceValue;
EditorGUI.BeginChangeCheck();
EditorGUI.PropertyField(position, value, true);
if (EditorGUI.EndChangeCheck())
{
if (value.objectReferenceValue != null && !System.IO.Directory.Exists(AssetDatabase.GetAssetPath(value.objectReferenceValue)))
value.objectReferenceValue = prevValue;
}
}
}
You could probably handle the drawing method with an attribute on the field instead of wrapping it on a struct/class, but for my code that is how I’m doing it right now.
Edit: Forgot to include the null check for setting back none on the field
For anyone stumbling across this in 2023 and is having the same problems as I did (“Stack Empty” when closing the folder select menu, and/or the value isn’t saved when leaving the inspector that uses the folder reference), I found the answer to the error in this comment: https://discussions.unity.com/t/691628/5
you should change this (line 75):
if (GUI.Button(objectFieldRect, "", GUI.skin.GetStyle("IN ObjectField")))
{
string path = EditorUtility.OpenFolderPanel("Select a folder", "Assets", "");
if (path.Contains(Application.dataPath))
{
path = "Assets" + path.Substring(Application.dataPath.Length);
obj = AssetDatabase.LoadAssetAtPath(path, typeof(DefaultAsset));
guid.stringValue = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(obj));
}
else Debug.LogError("The path must be in the Assets folder");
}
to this:
if (GUI.Button(objectFieldRect, "", GUI.skin.GetStyle("IN ObjectField")))
{
string path = EditorUtility.OpenFolderPanel("Select a folder", "Assets", "");
if (path.Contains(Application.dataPath))
{
path = "Assets" + path.Substring(Application.dataPath.Length);
obj = AssetDatabase.LoadAssetAtPath(path, typeof(DefaultAsset));
guid.stringValue = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(obj));
}
else Debug.LogError("The path must be in the Assets folder");
GUIUtility.ExitGUI();
}
The answer to the second problem (value not being saved) I figured by myself
To get rid of the error you need to add “GUIUtility.ExitGUI();” after you get the value from the folder panel to properly close the panel. You might need to find another similar solution if you wish to keep a different GUI window open at the same time, as this line will close every open GUI as far as I can tell.
You also need to add “guid.serializedObject.ApplyModifiedProperties();” after setting guid.stringValue so the property is updated on the source object, since local values on property drawers aren’t saved between uses.
Works perfectly after that. A great help with Addressable folder paths, btw.
I’m using Unity version 2021.3.16f1, but it might be relevant to other versions.