Closed JesseTG closed 3 years ago
Trimmer uses a custom serialization system to support polymorphic serialization. This was before Unity added support for this using the SerializeReference
attribute.
As far as I can tell, there is no way to use a property drawer without a SerializedProperty
and for a serialized property, you need to use Unity's serialization system.
A workaround could be to have a dummy ScriptableObject
that contains an AssetReference
field. Then you can create a SerializedObject
from an instance of it, get the SerializedProperty
for the field and use EditorGUILayout.PropertyField
to edit it. You should be able to set the current value on the serialized property before and read the updated value after, as well as reusing a single instance.
I might look into discarding Trimmer's own serialization system in favor of SerializeReference
, which should make it possible to use the regular property drawers by default. But I don't have an ETA for this and it wold limit Trimmer to Unity 2019.4+.
Trimmer uses a custom serialization system to support polymorphic serialization. This was before Unity added support for this using the
SerializeReference
attribute.As far as I can tell, there is no way to use a property drawer without a
SerializedProperty
and for a serialized property, you need to use Unity's serialization system.
Dang.
A workaround could be to have a dummy
ScriptableObject
that contains anAssetReference
field. Then you can create aSerializedObject
from an instance of it, get theSerializedProperty
for the field and useEditorGUILayout.PropertyField
to edit it. You should be able to set the current value on the serialized property before and read the updated value after, as well as reusing a single instance.
What type of Option
would I need? A custom Option<AssetReference>
? And would I need to create an asset for this ScriptableObject
, or could I just forget about it once I get this workaround in place?
I might look into discarding Trimmer's own serialization system in favor of
SerializeReference
, which should make it possible to use the regular property drawers by default. But I don't have an ETA for this and it wold limit Trimmer to Unity 2019.4+.
I would have no objection to this, but I'm not going to assume the same of others.
I've almost got this, but I'm having problems. Here's my base Option
:
using sttz.Trimmer;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace CorundumGames.Options.AssetReference
{
public abstract class OptionAssetReferenceBase<TAssetReference> : Option<TAssetReference>
where TAssetReference : UnityEngine.AddressableAssets.AssetReference
{
public override string Save(TAssetReference input)
{
return input.AssetGUID;
}
public override string Save()
{
return Save(Value);
}
public override void Load(string input)
{
Value = Parse(input);
}
public override TAssetReference Parse(string input)
{
if (string.IsNullOrEmpty(input))
return DefaultValue;
return ConstructAssetReference(input);
}
protected abstract TAssetReference ConstructAssetReference(string guid);
#if UNITY_EDITOR
private static BaseAssetReferenceDummy<TAssetReference> _dummyInstance;
internal abstract BaseAssetReferenceDummy<TAssetReference> CreateDummy();
public override bool EditGUI()
{
using (var changeCheck = new EditorGUI.ChangeCheckScope())
{
if (_dummyInstance == null)
{
_dummyInstance = CreateDummy();
}
using (var serializedObject = new SerializedObject(_dummyInstance))
{
using (var property =
serializedObject.FindProperty(
nameof(BaseAssetReferenceDummy<TAssetReference>.reference)
)
)
{
property.managedReferenceValue = Value;
EditorGUILayout.PropertyField(property, GUIContent.none);
Value = _dummyInstance.reference;
return changeCheck.changed;
}
}
}
}
#endif
}
}
...and here's a subclass for AssetReferenceGameObject
s:
using UnityEngine;
namespace CorundumGames.Options.AssetReference
{
using UnityEngine.AddressableAssets;
public abstract class OptionAssetReferenceGameObject : OptionAssetReferenceBase<AssetReferenceGameObject>
{
protected override AssetReferenceGameObject ConstructAssetReference(string guid)
{
return new AssetReferenceGameObject(guid);
}
#if UNITY_EDITOR
private sealed class Dummy : BaseAssetReferenceDummy<AssetReferenceGameObject>
{
}
internal override BaseAssetReferenceDummy<AssetReferenceGameObject> CreateDummy()
{
return ScriptableObject.CreateInstance<Dummy>();
}
#endif
}
}
(I have one for plain AssetReference
s as well, and it's pretty much the same except for the type arguments.)
When I set property.managedReferenceValue = Value;
inside EditGUI()
, an InvalidOperationException
is thrown and Trimmer's UI is not drawn:
InvalidOperationException: Attempting to set the managed reference value on a SerializedProperty that is set to a 'AssetReferenceGameObject'
What could I be doing wrong?
@sttz Any thoughts on this?
With SerializedObject/SerializedProperty, you edit the serialized data and need to check what the properties actually are. Only with a property of type UnityEngine.Object
can you use managedReferenceValue
.
In case of AssetReference
and its subclasses, it doesn't contain an object reference but instead two string properties "m_AssetGUID" and "m_SubObjectName", which are used to identify the object*.
To set/get those from the dummy object, you'd use something like this:
var so = new SerializedObject(dummy);
var guidProp = so.FindProperty("reference.m_AssetGUID");
var nameProp = so.FindProperty("reference.m_SubObjectName");
var guid = guidProp.stringValue;
var subName = nameProp.stringValue;
* This also means you probably need to save the combined guid/name in your option instead of only the guid. Otherwise I suspect referencing sub-assets will always revert to referencing the main asset.
Hope this helps!
I'll try that out, thanks! When I get something working I'll share my implementation here. I'll probably also polish it up in a separate package later on.
I'm in a situation where I need an
Option<T>
for a type that's very easy to serialize, but difficult to draw in the editor. Here's my use case.When using
OptionAsset<TUnity>
to conditionally include assets in builds, these assets are inserted into the first scene at build-time. However, this increases the size and load time of the first scene. In situations where this isn't acceptable, the Addressable Asset System is a good alternative; all assets are referred to withAssetReference
objects.AssetReference
s and their subclasses are plain objects, not UnityObject
s, meaning they cannot be used withOptionAsset<TUnity>
. I could just write anOptionAssetReference
that saves theAssetGUID
property. And I can.The problem is
Option.EditGUI
;AssetReference
s have a very simple representation, but their property drawers do a lot of heavy lifting as seen here:It's more than can be replicated with
EditorGUI.ObjectField
. However, allAssetReference
classes have their ownPropertyDrawer
s. Is it possible to use existingPropertyDrawer
s in custom TrimmerOption
s? If so, how?