我有一个类Container<TKey, TValue>,它是一个带有键TKey和值的对象,可以是TValue相同容器的列表:
using System.Collections.Generic;
using System;
using UnityEngine;
namespace Adapter.Containers
{
[Serializable]
public class Container<TKey, TValue>
{
public Container() : this(default) { }
public Container(TKey key) : this(key, default) { }
public Container(TKey key, TValue value)
{
m_Key = key;
m_Value = value;
}
[SerializeField] private TKey m_Key;
[SerializeField] private object m_Value;
public object this[TKey name]
{
get
{
if (GetContainer(name) is Container<TKey, TValue> container)
return container;
else if (GetStore(name) is TValue elementary)
return elementary;
throw new ArgumentException("Cannot get value because store unknown type");
}
set
{
if (value is Container<TKey, TValue> container)
SetContainer(name, container);
else if (value is TValue elementary)
SetStore(name, elementary);
throw new ArgumentException("Cannot set value because this type not support");
}
}
public TKey name { get => m_Key; set => m_Key = value; }
public List<Container<TKey, TValue>> container
{
get
{
if (m_Value is List<Container<TKey, TValue>>)
return m_Value as List<Container<TKey, TValue>>;
return null;
}
set
{
if (value is not null)
m_Value = value;
throw new ArgumentException("Cannot set not container value by container property");
}
}
public TValue store
{
get
{
if (m_Value is TValue)
return (TValue)m_Value;
return default;
}
set
{
if (value is not null)
m_Value = value;
throw new ArgumentException("Cannot set unacceptable type value by store property");
}
}
public ContainerType type => m_Value is List<Container<TKey, TValue>> ? ContainerType.Container : m_Value is TValue ? ContainerType.Store : ContainerType.Unknown;
public Container<TKey, TValue> GetContainer(TKey name) => container[container.FindIndex(item => item.name.Equals(name))]; // Получение по индексу просто для эстетичности
public void SetContainer(TKey name, Container<TKey, TValue> value) => container[container.FindIndex(item => item.name.Equals(name))] = value;
public TValue GetStore(TKey name) => container.Find(item => item.name.Equals(name)).store;
public void SetStore(TKey name, TValue value) => container.Find(item => item.name.Equals(name)).store = value;
}
}
我还有一个ContainerPropertyDrawer用于渲染此类的类:
using UnityEditor;
using UnityEngine;
namespace Adapter.Containers.Editor
{
[CustomPropertyDrawer(typeof(Container<,>))]
public class ContainerPropertyDrawer : PropertyDrawer
{
private ContainerType m_ContainerType = ContainerType.Unknown;
public ContainerType containerType { get => m_ContainerType; set => m_ContainerType = value; }
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
SerializedProperty keyProperty = property.FindPropertyRelative("m_Key");
SerializedProperty valueProperty = property.FindPropertyRelative("m_Value");
EditorGUI.LabelField(position, label);
EditorGUILayout.PropertyField(keyProperty);
m_ContainerType = (ContainerType)EditorGUILayout.EnumPopup(m_ContainerType);
if (m_ContainerType != ContainerType.Unknown)
if (m_ContainerType == ContainerType.Container)
EditorGUILayout.PropertyField(valueProperty, true);
else
EditorGUILayout.ObjectField(valueProperty.objectReferenceValue, typeof(object), true);
}
}
}
我遇到的问题是,首先,它会更改同一列表中的所有元素(不是特别重要),其次,不会为我显示值字段(当我尝试绘制该字段时m_ContainerType会出现错误,据我所知,unity 不想正常接收具有或不具有类型的字段?)SerializedProperty is nullm_Valueobject
序列化不保留对象的类型!只有数据,这些只是“名称:值”对,愚蠢的
json。例子:
结果:
正如您所看到的,尽管
MyAnimal我们指定了类型为 的对象Cat,但没有有关该字段的信息Breed,因为类型是Animal!在去阿里化过程中,根据字段类型重新创建对象。但我们根本无法序列化它,
System.Object不仅因为它不是[Serializable],而且因为它不包含任何数据,只有方法ToString、GetHashCode和。GetTypeEquals如果
Animal有Test或MonoBehaviour,ScriptableObject即UnityEngine.Object,那么在检查员中我们可以指出他的任何继承人。但与上面的例子不同的是,这个Animal不会在上下文中重新创建Test,它将独立于上下文创建,并且以Test值的形式存在一个指向id的指针UnityEngine.Object。这个过程可以在开发过程中观察到。有时我们重命名组件,脚本的链接在检查器中消失了,但是数据并没有消失,从中我们可以理解,在检查器中我们根本不是在处理一个类,而是在处理一个建立在该类上的数据模型,这就是它的作用
SerializedProperty。您不仅可以选择具有新名称的脚本,还可以选择任何一般脚本,并且它将具有名称匹配的那些字段的所有值。SerializedProperty.objectReferenceValue仅包含UnityEngine.Object,这可以通过将鼠标悬停在该属性上来看到,将其强制转换为 base 是没有意义的System.Object。ObjectField当然,它也不起作用;在编辑器中,Unity所有引用类型都只能是 fromUnityEngine.Object,因为Unity它只处理它的存储和生命周期。目前还不清楚如何处理这个typeof(object),也不清楚它是谁的以及和什么一起吃。您将无法从中序列化结构
Container<TKey, TValue>;您必须有一个类型化模型才能将数据反序列化到其中。你写的东西已经存在了,就是这样json。有,您可以在AssetStore其中JSON Object对值进行操作,并且有一些方法可以帮助确定它们的类型,例如IsFloat,但是没有检查器,因为......为什么需要它?这只是文本,您可以手写。在某个通用适配器的框架内,更容易使用路径对,例如
"vector.direction">"Rotation"并根据给定方案重建json以创建所需的对象。这里根本不需要检查器,除非您可以从这些对中注册一个方案,而不需要任何值。但这也很奇怪,因为这只是程序员的工作,对他们来说用代码描述这些方案应该不难,并且检查员不会以任何方式加速或促进这项工作。为了解决这个问题,我只需创建一个新的结构
Variation<TKey, TValue>(这就是为什么c#中没有像c++中这样的类?(std::variant))并object用type 替换 typeVariation<TValue, List<Container<TKey, TValue>>,最后类变成这样:容器.cs:
变体.cs:
ContainerPropertyDrawer.cs:
VariationPropertyDrawer.cs:
顺便说一句,这个项目在github上