CustomAssets
Askowl Custom Assets
AssetEditor.cs
1 using System;
2 using System.Collections.Generic;
3 using CustomAsset;
4 using CustomAsset.Mutable;
5 using UnityEditor;
6 using UnityEngine;
7 using GameObject = UnityEngine.GameObject;
8 using Object = UnityEngine.Object;
10 
11 namespace Askowl {
12  public class AssetEditor : IDisposable {
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>();
16 
17  public static AssetEditor Instance => Cache<AssetEditor>.Instance;
18 
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);
24  }
25 
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);
38  } else {
39  throw new Exception($"{assetType.Name} is not a MonoBehaviour, ScriptableObject or Resource");
40  }
41  return this;
42  }
43 
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);
52  return this;
53  }
54 
55  public bool Exists(string assetName) => assets.ContainsKey(assetName);
56 
57  public SerializedObject SerialisedAsset(string assetName) {
58  if (!Exists(assetName)) throw new Exception($"No asset '{assetName}' set in CreateAssetDictionary");
59  return assets[assetName].serializedAsset;
60  }
61 
62  public Object Asset(string assetName) => SerialisedAsset(assetName).targetObject;
63 
64  public string AssetPath(string assetName) => assets[assetName].path;
65 
66  protected void SetActiveObject(string assetName) => Selection.activeObject = Asset(assetName);
67 
68  private static Type ScriptableType(string name) => ScriptableObject.CreateInstance(name)?.GetType();
69 
70  public AssetEditor SetFieldToAssetEditorEntry(string assetName, string fieldName, string assetField) =>
71  SetField(assetName, fieldName, Asset(assetField));
72 
73  public AssetEditor SetField(string assetName, string fieldName, SerializedObject fieldValue) =>
74  SetField(assetName, fieldName, fieldValue.targetObject);
75 
76  public AssetEditor SetField(string assetName, string fieldName, string fieldValue) {
77  Field(assetName, fieldName).stringValue = fieldValue;
78  return this;
79  }
80 
81  public AssetEditor SetField(string assetName, string fieldName, Object fieldValue) {
82  Field(assetName, fieldName).objectReferenceValue = fieldValue;
83  return this;
84  }
85 
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}'");
92  return property;
93  }
94 
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);
100  }
101 
102  public AssetEditor InsertIntoArrayField(
103  string assetName, string fieldName, string assetField, int index = 0) =>
104  InsertIntoArrayField(assetName, fieldName, Asset(assetField), index);
105 
106  public AssetEditor InsertIntoArrayField(
107  string assetName, string fieldName, SerializedObject fieldValue, int index = 0) =>
108  InsertIntoArrayField(assetName, fieldName, fieldValue.targetObject, index);
109 
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();
115  return this;
116  }
117 
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();
123  }
124 
125  public T FindProperty<T>(string assetName, string fieldName = "value") where T : class =>
126  FindProperty(assetName, fieldName).exposedReferenceValue as T;
127 
128  private SerializedProperty FindProperty(SerializedObject asset, string fieldName) {
129  var property = asset.FindProperty(fieldName);
130  if (property == default) throw new Exception($"No property '{fieldName}'");
131  return property;
132  }
133 
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}'");
137  return property;
138  }
139 
140  public AssetEditor Save() {
141  var manager = GetCustomAssetManager();
142  Object item = null;
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);
149  }
150  entry.Value.serializedAsset.ApplyModifiedPropertiesWithoutUndo();
151  }
152  AssetDatabase.SaveAssets();
153  AssetDb.Instance.Select(item).Dispose();
154  return this;
155  }
156 
157  private SerializedObject GetCustomAssetManager() =>
158  GetPrefabMonoBehaviour("Custom Asset Managers", typeof(Managers), "");
159 
160  private SerializedObject GetPrefabMonoBehaviour(string prefabName, Type monoBehaviour, string path) {
161  var gameObject = GameObject.Find(prefabName);
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;
167  }
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;
173  }
174  public void Dispose() {
175  Save();
176  Cache<AssetEditor>.Dispose(this);
177  }
178  }
179 }
MonoBehaviour container for all manager custom assets for a game //#TBD#//
Definition: Managers.cs:7
CustomAsset that contains a string. Events are triggered every time the string changes ...