asset based atoms generation (#187)

* Added Scriptable objects to define which types to be (re)generated

* added folder inspector to allow UnityAtoms specific actions and shortcuts to improve usability

* added command to regenerate all atom files

* storing AssemblyQualifiedName for generation
also showing value types that are not specifically marked as serializable

* fixed code style and cleanup
 - removed unused imports
 - removed commented-out code
This commit is contained in:
Oliver Biwer 2020-11-06 22:07:21 +01:00 committed by GitHub
parent 13086d0f65
commit 77faff6637
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 289 additions and 1 deletions

View File

@ -0,0 +1,32 @@
using UnityEngine;
using UnityEditor;
using System.IO;
namespace UnityAtoms.Editor
{
[CustomEditor(typeof(DefaultAsset))]
public class AtomFolderEditor : UnityEditor.Editor
{
protected override void OnHeaderGUI()
{
base.OnHeaderGUI();
if (!Directory.Exists(AssetDatabase.GetAssetPath(target))) return;
if (!target.name.ToLowerInvariant().Contains("atom")) return;
GUILayout.Label("UNITY ATOMS", (GUIStyle) "AC BoldHeader", GUILayout.ExpandWidth(true));
if (GUILayout.Button("Add Generator", (GUIStyle) "LargeButton"))
{
var asset = CreateInstance<AtomGenerator>();
var newPath = Path.Combine(
AssetDatabase.GetAssetPath(target),
"New Atom Type.asset"
);
Debug.Log(newPath);
AssetDatabase.CreateAsset(asset, newPath);
AssetDatabase.ImportAsset(newPath);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0f56d91dda58c8e4ea7e0d01fccdc636
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,145 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
using UnityAtoms;
namespace UnityAtoms.Editor
{
[CustomEditor(typeof(AtomGenerator))]
public class AtomGeneratorEditor : UnityEditor.Editor
{
private IEnumerable<IGrouping<string, Type>> _types;
private SearchTypeDropdown _typeSelectorPopup;
private void OnEnable()
{
_types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(assembly => assembly.GetExportedTypes())
.Where(x => x != null)
.Where(x => x.IsValueType || (x.Attributes & TypeAttributes.Serializable) != 0)
.Where(t => !(t.Namespace?.Contains("Microsoft") ?? false))
.Where(t => !(t.Namespace?.Contains("UnityEditor") ?? false))
.GroupBy(t => t.Namespace.Split('.')[0]);
}
public override void OnInspectorGUI()
{
Rect buttonRect = new Rect();
var rect = GUILayoutUtility.GetRect(new GUIContent("Show"), EditorStyles.toolbarButton);
if (GUILayout.Button("Select Type"))
{
var dropdown = new SearchTypeDropdown(new AdvancedDropdownState(), _types, (s) =>
{
serializedObject.FindProperty("Namespace").stringValue = s.Split(':')[0];
serializedObject.FindProperty("BaseType").stringValue = s.Split(':')[1];
var type = _types.First(grouping =>
grouping.Key == serializedObject.FindProperty("Namespace").stringValue)
.First(t => t.Name == serializedObject.FindProperty("BaseType").stringValue);
serializedObject.FindProperty("FullQualifiedName").stringValue = type.AssemblyQualifiedName;
});
dropdown.Show(rect);
}
EditorGUILayout.PropertyField(serializedObject.FindProperty("FullQualifiedName"));
var options = serializedObject.FindProperty("GenerationOptions").intValue;
var scripts = (target as AtomGenerator)?.Scripts;
for (var index = 0; index < AtomTypes.ALL_ATOM_TYPES.Count; index++)
{
var option = AtomTypes.ALL_ATOM_TYPES[index];
EditorGUILayout.BeginHorizontal();
bool b = (options & (1 << index)) == (1 << index);
EditorGUI.BeginChangeCheck();
b = EditorGUILayout.Toggle(AtomTypes.ALL_ATOM_TYPES[index].DisplayName, b);
if (EditorGUI.EndChangeCheck())
{
if (b)
{
options |= (1 << index);
// add all dependencies:
if (AtomTypes.DEPENDENCY_GRAPH.TryGetValue(option, out var list))
list.ForEach(dep => options |= (1 << AtomTypes.ALL_ATOM_TYPES.IndexOf(dep)));
}
else
{
options &= ~(1 << index);
// remove all depending:
foreach (var keyValuePair in AtomTypes.DEPENDENCY_GRAPH.Where(kv => kv.Value.Contains(option)))
{
options &= ~(1 << AtomTypes.ALL_ATOM_TYPES.IndexOf(keyValuePair.Key));
}
}
}
if (scripts != null && index < scripts.Count && scripts[index] != null)
{
EditorGUILayout.ObjectField(scripts[index], typeof(MonoScript), false, GUILayout.Width(200));
}
else
{
EditorGUILayout.LabelField(GUIContent.none, GUILayout.Width(200));
}
EditorGUILayout.EndHorizontal();
}
serializedObject.FindProperty("GenerationOptions").intValue = options;
serializedObject.ApplyModifiedProperties();
if (GUILayout.Button("(Re)Generate"))
{
(target as AtomGenerator)?.Generate();
AssetDatabase.SaveAssets();
}
}
private class SearchTypeDropdown : AdvancedDropdown
{
private readonly IEnumerable<IGrouping<string, Type>> _list;
private readonly Action<string> _func;
public SearchTypeDropdown(AdvancedDropdownState state, IEnumerable<IGrouping<string, Type>> list,
Action<string> func) : base(state)
{
_list = list;
_func = func;
}
protected override void ItemSelected(AdvancedDropdownItem item)
{
base.ItemSelected(item);
_func?.Invoke(item.name);
}
protected override AdvancedDropdownItem BuildRoot()
{
var root = new AdvancedDropdownItem("Serializable Types");
foreach (var group in _list)
{
var groupItem = new AdvancedDropdownItem(group.Key);
foreach (var type in group)
{
groupItem.AddChild(new AdvancedDropdownItem(group.Key + ":" + type.Name));
}
root.AddChild(groupItem);
}
return root;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 02004ea5719f46c3bbf2470218a5aed2
timeCreated: 1587244587

View File

@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace UnityAtoms.Editor
{
[CreateAssetMenu(fileName = "AtomGenerator", menuName = "Unity Atoms/Generation/AtomGenerator", order = 0)]
public class AtomGenerator : ScriptableObject
{
public string FullQualifiedName;
public string Namespace;
public string BaseType;
public int GenerationOptions;
// Referencing Generated Files here:
public List<MonoScript> Scripts = new List<MonoScript>(AtomTypes.ALL_ATOM_TYPES.Count);
public void Generate()
{
var type = Type.GetType($"{FullQualifiedName}");
if (type == null) throw new TypeLoadException($"Type could not be found ({FullQualifiedName})");
var isValueTypeEquatable = type.GetInterfaces().Contains(typeof(IEquatable<>));
var templateVariables = Generator.CreateTemplateVariablesMap(BaseType, Namespace, "BaseAtoms");
var capitalizedValueType = BaseType.Capitalize();
var templates = Generator.GetTemplatePaths();
var templateConditions =
Generator.CreateTemplateConditions(isValueTypeEquatable, Namespace, "BaseAtoms", BaseType);
var baseWritePath =
Path.Combine((Path.GetDirectoryName(AssetDatabase.GetAssetPath(this.GetInstanceID()))) ?? "Assets/",
"Generated");
Directory.CreateDirectory(baseWritePath);
Scripts.Clear();
var t = GenerationOptions;
var idx = 0;
while (t > 0)
{
if (t % 2 == 1)
{
var atomType = AtomTypes.ALL_ATOM_TYPES[idx];
templateVariables["VALUE_TYPE_NAME"] =
atomType.IsValuePair ? $"{capitalizedValueType}Pair" : capitalizedValueType;
var valueType = atomType.IsValuePair ? $"{capitalizedValueType}Pair" : BaseType;
templateVariables["VALUE_TYPE"] = valueType;
var resolvedRelativeFilePath = Templating.ResolveVariables(templateVariables: templateVariables,
toResolve: atomType.RelativeFileNameAndPath);
var targetPath = Path.Combine(baseWritePath, resolvedRelativeFilePath);
var newCreated = !File.Exists(targetPath);
Generator.Generate(new AtomReceipe(atomType, valueType), baseWritePath, templates,
templateConditions, templateVariables);
if (newCreated) AssetDatabase.ImportAsset(targetPath);
var ms = AssetDatabase.LoadAssetAtPath<MonoScript>(targetPath);
Scripts.Add(ms);
}
else
{
Scripts.Add(null);
}
idx++;
t >>= 1;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 734fbfb568154d32a89d792102f0c779
timeCreated: 1595876056

View File

@ -1,6 +1,5 @@
#if UNITY_2018_3_OR_NEWER
using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
@ -33,6 +32,25 @@ namespace UnityAtoms.Editor
}
}
[MenuItem("Tools/Unity Atoms/Regenerate Atoms from Assets")]
static void RegenereateAssets()
{
if (!EditorUtility.DisplayDialog("Regenerate", "This will regenerate all Atoms from Generation-Assets",
"ok", "cancel"))
{
return;
}
var guids = AssetDatabase.FindAssets($"t:{nameof(AtomGenerator)}");
foreach (var guid in guids)
{
var path = AssetDatabase.GUIDToAssetPath(guid);
AssetDatabase.LoadAssetAtPath<AtomGenerator>(path).Generate();
}
//Debug.Log(sb.ToString());
}
/// <summary>
/// Create the editor window.
/// </summary>