2 using System.Collections.Generic;
    13     private readonly Dictionary<string, (SerializedObject serializedAsset, string path)> assets =
    14       new Dictionary<string, (SerializedObject, string)>();
    15     private readonly Dictionary<string, bool> existingAssets = 
new Dictionary<string, bool>();
    17     public static AssetEditor Instance => Cache<AssetEditor>.Instance;
    19     public AssetEditor Add(
string assetName, 
string nameSpace, 
string path) {
    20       var  qualifiedName = 
string.IsNullOrWhiteSpace(nameSpace) ? assetName : $
"{nameSpace}.{assetName}";
    21       Type assetType     = ScriptableType(qualifiedName);
    22       if (assetType == 
default) 
throw new Exception($
"No asset '{qualifiedName}' found to add");
    23       return Add(assetName, assetType, path);
    26     public AssetEditor Add(
string assetName, Type assetType, 
string path) {
    27       if (assetType == 
default) 
throw new Exception($
"No asset '{assetName}' found to add");
    28       if (assetType.IsSubclassOf(typeof(ScriptableObject))) {
    29         var scriptableObject = ScriptableObject.CreateInstance(assetType);
    30         assets[assetName] = (
new SerializedObject(scriptableObject), path);
    31       } 
else if (assetType.IsSubclassOf(typeof(MonoBehaviour))) {
    32         GetPrefabMonoBehaviour(assetName, assetType, path);
    33       } 
else if (assetType.IsSubclassOf(typeof(
Object))) {
    34         var asset = Resources.Load(assetName, assetType);
    35         if (asset == null) 
throw new Exception($
"No resource '{assetName}' of type {assetType.Name}");
    36         var serialisedObject = 
new SerializedObject(asset);
    37         assets[assetName] = (serialisedObject, path);
    39         throw new Exception($
"{assetType.Name} is not a MonoBehaviour, ScriptableObject or Resource");
    44     public AssetEditor Load(
string assetName, 
string nameSpace, 
string path) {
    45       if (Exists(assetName)) 
return this;
    46       var name      = assetName.Contains(
".") ? assetName : $
"{assetName}.asset";
    47       var assetType = ScriptableType($
"{nameSpace}.{assetName}");
    48       var asset     = AssetDb.Load(name, assetType) ?? AssetDatabase.LoadAssetAtPath($
"{path}/{assetName}", assetType);
    49       if (asset == null) 
throw new Exception($
"No asset '{name}' found to load");
    50       existingAssets[assetName] = 
true;
    51       assets[assetName]         = (
new SerializedObject(asset), path);
    55     public bool Exists(
string assetName) => assets.ContainsKey(assetName);
    57     public SerializedObject SerialisedAsset(
string assetName) {
    58       if (!Exists(assetName)) 
throw new Exception($
"No asset '{assetName}' set in CreateAssetDictionary");
    59       return assets[assetName].serializedAsset;
    62     public Object Asset(
string assetName) => SerialisedAsset(assetName).targetObject;
    64     public string AssetPath(
string assetName) => assets[assetName].path;
    66     protected void SetActiveObject(
string assetName) => Selection.activeObject = Asset(assetName);
    68     private static Type ScriptableType(
string name) => ScriptableObject.CreateInstance(name)?.GetType();
    70     public AssetEditor SetFieldToAssetEditorEntry(
string assetName, 
string fieldName, 
string assetField) =>
    71       SetField(assetName, fieldName, Asset(assetField));
    73     public AssetEditor SetField(
string assetName, 
string fieldName, SerializedObject fieldValue) =>
    74       SetField(assetName, fieldName, fieldValue.targetObject);
    76     public AssetEditor SetField(
string assetName, 
string fieldName, 
string fieldValue) {
    77       Field(assetName, fieldName).stringValue = fieldValue;
    81     public AssetEditor SetField(
string assetName, 
string fieldName, 
Object fieldValue) {
    82       Field(assetName, fieldName).objectReferenceValue = fieldValue;
    86     public SerializedProperty Field(
string assetName, 
string fieldName = 
"value") {
    87       if (!assets.ContainsKey(assetName)) 
throw new Exception($
"No asset '{assetName}' set in CreateAssetDictionary");
    88       fieldName = CamelCase(fieldName);
    89       var 
property = FindProperty(assetName, fieldName);
    90       if (property == 
default) FindProperty(assetName, 
char.ToUpper(fieldName[0]) + fieldName.Substring(1));
    91       if (property == 
default) 
throw new Exception($
"No property '{fieldName}' in '{assetName}'");
    95     private string CamelCase(
string name) {
    96       name = name.Replace(
" ", 
"");
    97       if (name.Length == 0) 
return "";
    98       if (name.Length == 1) 
return $
"{char.ToLower(name[0])}";
    99       return char.ToLower(name[0]) + name.Substring(1);
   103       string assetName, 
string fieldName, 
string assetField, 
int index = 0) =>
   104       InsertIntoArrayField(assetName, fieldName, Asset(assetField), index);
   107       string assetName, 
string fieldName, SerializedObject fieldValue, 
int index = 0) =>
   108       InsertIntoArrayField(assetName, fieldName, fieldValue.targetObject, index);
   110     public AssetEditor InsertIntoArrayField(
string assetName, 
string fieldName, 
Object fieldValue, 
int index = 0) {
   111       var serialisedProperty = FindProperty(assetName, fieldName);
   112       serialisedProperty.InsertArrayElementAtIndex(index);
   113       serialisedProperty.GetArrayElementAtIndex(index).objectReferenceValue = fieldValue;
   114       SerialisedAsset(assetName).ApplyModifiedPropertiesWithoutUndo();
   118     public void InsertIntoArrayField(SerializedObject asset, 
string fieldName, 
Object fieldValue, 
int index = 0) {
   119       var serialisedProperty = FindProperty(asset, fieldName);
   120       serialisedProperty.InsertArrayElementAtIndex(index);
   121       serialisedProperty.GetArrayElementAtIndex(index).objectReferenceValue = fieldValue;
   122       asset.ApplyModifiedPropertiesWithoutUndo();
   125     public T FindProperty<T>(
string assetName, 
string fieldName = 
"value") where T : 
class =>
   126       FindProperty(assetName, fieldName).exposedReferenceValue as T;
   128     private SerializedProperty FindProperty(SerializedObject asset, 
string fieldName) {
   129       var 
property = asset.FindProperty(fieldName);
   130       if (property == 
default) 
throw new Exception($
"No property '{fieldName}'");
   134     private SerializedProperty FindProperty(
string assetName, 
string fieldName) {
   135       var 
property = SerialisedAsset(assetName).FindProperty(fieldName);
   136       if (property == 
default) 
throw new Exception($
"No property '{fieldName}' in '{assetName}'");
   141       var    manager = GetCustomAssetManager();
   143       foreach (var entry 
in assets) {
   144         if (entry.Value.serializedAsset.targetObject.GetType().IsSubclassOf(typeof(ScriptableObject))) {
   145           if (!existingAssets.ContainsKey(entry.Key))
   146             AssetDatabase.CreateAsset(item = entry.Value.serializedAsset.targetObject, entry.Value.path);
   147           if (entry.Key.EndsWith(
"Manager"))
   148             InsertIntoArrayField(manager, 
"managers", entry.Value.serializedAsset.targetObject);
   150         entry.Value.serializedAsset.ApplyModifiedPropertiesWithoutUndo();
   152       AssetDatabase.SaveAssets();
   153       AssetDb.Instance.Select(item).Dispose();
   157     private SerializedObject GetCustomAssetManager() =>
   158       GetPrefabMonoBehaviour(
"Custom Asset Managers", typeof(
Managers), 
"");
   160     private SerializedObject GetPrefabMonoBehaviour(
string prefabName, Type monoBehaviour, 
string path) {
   162       if (gameObject == 
default) {
   163         var prefab = Resources.Load(prefabName);
   164         if (prefab == null) 
throw new Exception($
"No prefab named {prefabName}");
   165         gameObject      = (
GameObject) 
Object.Instantiate(prefab, Vector3.zero, Quaternion.identity);
   166         gameObject.name = prefabName;
   168       var component = gameObject.GetComponentInChildren(monoBehaviour);
   169       if (component == 
default) 
throw new Exception($
"No component '{monoBehaviour.Name}' in '{gameObject.name}'1");
   170       var serialisedObject = 
new SerializedObject(component);
   171       assets[prefabName] = (serialisedObject, path);
   172       return serialisedObject;
   174     public void Dispose() {
   176       Cache<AssetEditor>.Dispose(
this);
 
MonoBehaviour container for all manager custom assets for a game //#TBD#// 
 
CustomAsset that contains a string. Events are triggered every time the string changes  ...