Skip to content

Commit

Permalink
Merge pull request #68 from dubit/dev_args_list_improvements
Browse files Browse the repository at this point in the history
ArgsList: added support for enums + scriptable objects
  • Loading branch information
kkjamie authored Jun 14, 2018
2 parents b2379e5 + 3d10cd0 commit 5b4ef40
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 31 deletions.
65 changes: 59 additions & 6 deletions Serialization/ArgsList.Serialization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ namespace DUCK.Serialization
public partial class ArgsList
{
private const string COMPONENT_PREFIX = "c:";
private const string ENUM_PREFIX = "e:";
private const string SCRIPTABLE_OBJECT_PREFIX = "so:";

[SerializeField]
private string[] typeOrder;
Expand Down Expand Up @@ -40,6 +42,9 @@ public partial class ArgsList
[SerializeField]
private Color[] colorArgs;

[SerializeField]
private ScriptableObject[] scriptableObjectArgs;

public void OnBeforeSerialize()
{
if (argTypes == null) return;
Expand All @@ -53,26 +58,42 @@ public void OnBeforeSerialize()
{
t = typeof(GameObject);
}
if (argLists.ContainsKey(t))
else if (t.IsSubclassOf(typeof(Enum)))
{
t = typeof(int);
}
else if (t.IsSubclassOf(typeof(ScriptableObject)))
{
t = typeof(ScriptableObject);
}
else if (argLists.ContainsKey(t))
{
return argLists[t];
}
return argLists[t] = new List<object>();
return argLists.ContainsKey(t) ? argLists[t] : argLists[t] = new List<object>();
};

for (int i = 0; i < argTypes.Count; i++)
{
var argType = argTypes[i];
var arg = args[i];
var list = lazyGetList(argType);
list.Add(arg);

if (argType.IsSubclassOf(typeof(Component)))
{
var arg = args[i];
list.Add(arg);
typeOrderList.Add(COMPONENT_PREFIX + argType.FullName);
}
else if (argType.IsSubclassOf(typeof(Enum)))
{
typeOrderList.Add(ENUM_PREFIX + argType.FullName);
}
else if (argType.IsSubclassOf(typeof(ScriptableObject)))
{
typeOrderList.Add(SCRIPTABLE_OBJECT_PREFIX + argType.FullName);
}
else
{
list.Add(args[i]);
typeOrderList.Add(argType.FullName);
}
}
Expand Down Expand Up @@ -117,6 +138,34 @@ public void OnAfterDeserialize()
localArgTypes.Add(argType);
list = serializedLists[typeof(GameObject).FullName];
}
else if (typeName.StartsWith(ENUM_PREFIX))
{
// strip off prefix
typeName = typeName.Replace(ENUM_PREFIX, "");

if (!enumTypes.ContainsKey(typeName))
{
throw new Exception("ArgsList cannot deserialize enum item of type: " + typeName + ", it was not found in the assemblies");
}

argType = enumTypes[typeName];
localArgTypes.Add(argType);
list = serializedLists[typeof(int).FullName];
}
else if (typeName.StartsWith(SCRIPTABLE_OBJECT_PREFIX))
{
// strip off prefix
typeName = typeName.Replace(SCRIPTABLE_OBJECT_PREFIX, "");

if (!scriptableObjectTypes.ContainsKey(typeName))
{
throw new Exception("ArgsList cannot deserialize ScriptableObject item of type: " + typeName + ", it was not found in the assemblies");
}

argType = scriptableObjectTypes[typeName];
localArgTypes.Add(argType);
list = serializedLists[typeof(ScriptableObject).FullName];
}
else
{
if (!supportedTypes.ContainsKey(typeName))
Expand Down Expand Up @@ -172,7 +221,11 @@ public static SupportedType Create<T>(Func<ArgsList, T[]> getList, Action<ArgsLi
{
return new SupportedType(
typeof(T),
i => getList(i).Cast<object>().ToList(),
i =>
{
var list = getList(i);
return list != null ? list.Cast<object>().ToList() : new List<object>();
},
(i, v) => setList(i, v.Cast<T>().ToArray()));
}
}
Expand Down
51 changes: 26 additions & 25 deletions Serialization/ArgsList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.UI;

namespace DUCK.Serialization
{
Expand All @@ -13,12 +11,17 @@ public partial class ArgsList : ISerializationCallbackReceiver
{
public static bool IsSupportedType(Type type)
{
return supportedTypesArray.Contains(type) || type.IsSubclassOf(typeof(Component));
return supportedTypesArray.Contains(type) ||
type.IsSubclassOf(typeof(Component)) ||
type.IsSubclassOf(typeof(ScriptableObject)) ||
type.IsSubclassOf(typeof(Enum));
}

private static readonly Dictionary<string, SupportedType> supportedTypes;
private static readonly Type[] supportedTypesArray;
private static readonly Dictionary<string, Type> componentTypes;
private static readonly Dictionary<string, Type> enumTypes;
private static readonly Dictionary<string, Type> scriptableObjectTypes;

static ArgsList()
{
Expand All @@ -33,33 +36,31 @@ static ArgsList()
SupportedType.Create(i => i.vector3Args, (i, v) => i.vector3Args = v),
SupportedType.Create(i => i.vector4Args, (i, v) => i.vector4Args = v),
SupportedType.Create(i => i.colorArgs, (i, v) => i.colorArgs = v),
SupportedType.Create(i => i.scriptableObjectArgs, (i, v) => i.scriptableObjectArgs = v),
};

supportedTypesArray = supportedTypesList.Select(t => t.Type).ToArray();
supportedTypes = supportedTypesList.ToDictionary(t => t.Type.FullName, t => t);
componentTypes = new Dictionary<string, Type>();
enumTypes = new Dictionary<string, Type>();
scriptableObjectTypes = new Dictionary<string, Type>();

// Get every type that extends component
var assemblies = new []
{
// Project assembly
Assembly.GetExecutingAssembly(),
// UnityEngine Assembly
typeof(Component).Assembly,
// UnityEngine.UI Assembly
typeof(Graphic).Assembly,
};

var componentSubTypes = assemblies.SelectMany(a => a.GetTypes())
.Where(t => t.IsSubclassOf(typeof(Component)));
var assemblies = AppDomain.CurrentDomain.GetAssemblies();

foreach (var type in componentSubTypes)
foreach (var type in assemblies.SelectMany(a => a.GetTypes()))
{
// Make sure this type has not been added already
// (it can happen, since some types are shipped in multiple assemblies)
if (!componentTypes.ContainsKey(type.FullName))
var fullTypeName = type.FullName;
if (type.IsSubclassOf(typeof(Component)))
{
componentTypes[fullTypeName] = type;
}
else if (type.IsSubclassOf(typeof(Enum)))
{
componentTypes.Add(type.FullName, type);
enumTypes[fullTypeName] = type;
}
else if (type.IsSubclassOf(typeof(ScriptableObject)))
{
scriptableObjectTypes[fullTypeName] = type;
}
}
}
Expand All @@ -71,16 +72,16 @@ public IList<Type> ArgTypes

public IList<object> AllArgs
{
get
get
{
return args.Select((a,i) =>
return args.Select((a,i) =>
{
if (a != null && argTypes[i].IsSubclassOf(typeof(Component)))
{
return ((GameObject) a).GetComponent(argTypes[i]);
}
return a;
}).ToList();
}).ToList();
}
}

Expand Down Expand Up @@ -205,7 +206,7 @@ public T Get<T>(int index)
{
return ((GameObject) args[index]).GetComponent<T>();
}

return (T) args[index];
}

Expand Down
49 changes: 49 additions & 0 deletions Serialization/Editor/ArgsListTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using DUCK.Tween.Serialization;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.UI;
Expand All @@ -9,6 +10,14 @@ namespace DUCK.Serialization.Editor
[TestFixture]
public class ArgsListTests
{
public class TestScriptableObject : ScriptableObject{}

public enum TestEnum
{
A,
B
}

[Test]
public void ExpectConstructorNotToThrow()
{
Expand Down Expand Up @@ -437,6 +446,46 @@ public void ExpectPolymorphicComponentSerializationToBeSupported()
Assert.AreEqual(image, resultArgsList[1]);
}

[Test]
public void ExpectEnumSerializationToBeSupported()
{
var argsList = new ArgsList();

// test that it doesn't throw when specifying the type

Assert.DoesNotThrow(() => argsList.SetTypes(new List<Type> {typeof(TestEnum)}));

// Add some data, serialize and deserialize
var value = TestEnum.A;
argsList.Set(0, value);
var json = JsonUtility.ToJson(argsList);
var resultArgsList = JsonUtility.FromJson<ArgsList>(json);
var result = resultArgsList.Get<TestEnum>(0);

// Now test the value is what it should be
Assert.AreEqual(value, result);
}

[Test]
public void ExpectScriptableObjectSerializationToBeSupported()
{
var argsList = new ArgsList();

// test that it doesn't throw when specifying the type
Assert.DoesNotThrow(() => argsList.SetTypes(new List<Type> {typeof(TestScriptableObject)}));

// Add some data, serialize and deserialize
var value = ScriptableObject.CreateInstance<TestScriptableObject>();
argsList.Set(0, value);

var json = JsonUtility.ToJson(argsList);
var resultArgsList = JsonUtility.FromJson<ArgsList>(json);
var result = resultArgsList.Get<TestScriptableObject>(0);

// Now test the value is what it should be
Assert.AreEqual(value, result);
}

[Test]
public void ExpectSerializationOfMultipleTypesToBeSupported()
{
Expand Down
6 changes: 6 additions & 0 deletions Utils/Editor/EditorGUIHelpers/DrawFieldByTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ public static object FieldByType(string label, object obj, Type type)
return EditorGUILayout.ObjectField(label, (UnityEngine.Object) obj, type, true);
}

// special case for enum fields
if (type.IsSubclassOf(typeof(Enum)))
{
return EditorGUILayout.EnumPopup(label, (Enum)Enum.ToObject(type, (int)obj));
}

// check we can deal with this type of field
if (!drawerFunctions.ContainsKey(type))
{
Expand Down

0 comments on commit 5b4ef40

Please sign in to comment.