fix: better serialization

main
taoria 3 years ago
parent 02467dc073
commit 69346b388b
  1. 16
      TNode/Attribute/BlackboardSection.cs
  2. 3
      TNode/Attribute/BlackboardSection.cs.meta
  3. 3
      TNode/Editor/Serialization.meta
  4. 10
      TNode/Editor/Serialization/BlackboardDataWrapper.cs
  5. 3
      TNode/Editor/Serialization/BlackboardDataWrapper.cs.meta
  6. 53
      TNode/Editor/Serialization/DataWrapper.cs
  7. 3
      TNode/Editor/Serialization/DataWrapper.cs.meta
  8. 110
      TNode/Editor/Serialization/NodeDataWrapper.cs
  9. 0
      TNode/Editor/Serialization/NodeDataWrapper.cs.meta
  10. 2
      TNode/JsonSerialize/JsonSerializeTool.cs
  11. 24
      TNode/JsonSerialize/UnityObjectConverter.cs
  12. 3
      TNode/JsonSerialize/UnityObjectConverter.cs.meta
  13. 4
      TNode/Models/BlackboardData.cs
  14. 51
      TNode/Models/GraphData.cs
  15. 3
      TNode/Tools.meta
  16. 90
      TNode/Tools/NodeDataWrapper.cs
  17. 4
      TNodeGraphViewImpl/Editor/Cache/NodeEditorExtensions.cs
  18. 26
      TNodeGraphViewImpl/Editor/GraphBlackboard/DefaultGraphBlackboardView.cs
  19. 1
      TNodeGraphViewImpl/Editor/Inspector/NodeInspector.cs
  20. 5
      TNodeGraphViewImpl/Editor/Inspector/NodeInspectorInNode.cs
  21. 22
      TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs
  22. 1
      TNodeGraphViewImpl/Editor/NodeViews/DefaultNodeView.cs
  23. 1
      TNodeGraphViewImpl/Editor/NodeViews/DragNodeView.cs
  24. 7
      TNodeGraphViewImpl/Editor/NodeViews/NodeView.cs

@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
namespace TNode.Attribute{
/// <summary>
/// Use this attribute to declare a blackboard section ,a blackboard section is a group of variables with same types
/// </summary>
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
[BaseTypeRequired(typeof(List<>))]
public class BlackboardSection:System.Attribute{
}
}

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 773d073006dc4dd488e18b38165efd5a
timeCreated: 1656942977

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4c1e0017367a4d448a68ed34b7540782
timeCreated: 1657690936

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using TNode.Models;
using UnityEngine;
namespace TNode.Editor.Serialization{
public class BlackboardDataWrapper:DataWrapper<BlackboardDataWrapper,BlackboardData>{
}
}

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ac3fada821244e69b6b9a27a7b94eeee
timeCreated: 1657691334

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using TNode.Models;
using UnityEngine;
namespace TNode.Editor.Serialization{
[Serializable]
public class DataWrapper<TWrapper,TData>:ScriptableObject where TWrapper:DataWrapper<TWrapper,TData> where TData:IModel,new(){
[SerializeReference]
public TData data;
private static readonly Dictionary<TData,TWrapper> Cache = new ();
public static TWrapper Get(TData data){
if (data.GetType().IsGenericType){
return CreateInstance<TWrapper>();
}
if(Cache.ContainsKey(data)){
return Cache[data];
}
var wrapper = CreateInstance<TWrapper>();
wrapper.data = data;
Cache.Add(data,wrapper);
return wrapper;
}
public event Action<DataWrapper<TWrapper,TData>> OnValueChanged;
public void SetValue(string path, object value){
var fieldInfo = data.GetType().GetField(path);
fieldInfo.SetValue(data,value);
OnValueChanged?.Invoke(this);
}
public object GetValue(string path){
var fieldInfo = data.GetType().GetField(path);
return fieldInfo.GetValue(data);
}
public static implicit operator TData(DataWrapper<TWrapper,TData> wrapper){
if (wrapper == null)
return default(TData);
return wrapper.data;
}
/// <summary>
/// Use this to get the wrapped data directly.
/// </summary>
/// <param name="unWrapper"></param>
/// <returns></returns>
public static implicit operator DataWrapper<TWrapper,TData>(TData unWrapper){
if (unWrapper == null)
return null;
return Get(unWrapper);
}
}
}

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3b4407f1670d4359b807377900c83583
timeCreated: 1657693507

@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using TNode.Models;
using UnityEngine;
using UnityEngine.Serialization;
namespace TNode.Editor.Serialization{
[Obsolete]
public class NodeDataWrapper<T> : ScriptableObject where T : NodeData{
public T Data;
private static readonly Dictionary<T,NodeDataWrapper<T>> Cache = new ();
public event Action<NodeDataWrapper<T>> OnValueChanged;
public static NodeDataWrapper<T> Get(T data){
if(Cache.ContainsKey(data)){
return Cache[data];
}
var wrapper = ScriptableObject.CreateInstance<NodeDataWrapper<T>>();
Cache.Add(data,wrapper);
return wrapper;
}
public NodeDataWrapper(T data){
this.Data = data;
}
public void SetValue(string path, object value){
var fieldInfo = Data.GetType().GetField(path);
fieldInfo.SetValue(Data,value);
OnValueChanged?.Invoke(this);
}
public object GetValue(string path){
var fieldInfo = Data.GetType().GetField(path);
return fieldInfo.GetValue(Data);
}
public static implicit operator T(NodeDataWrapper<T> wrapper){
if (wrapper == null)
return null;
return wrapper.Data;
}
public static implicit operator NodeDataWrapper<T>(T unWrapper){
if (unWrapper == null)
return null;
return Get(unWrapper);
}
}
public class NodeDataWrapper:DataWrapper<NodeDataWrapper,NodeData>{
}
/// <summary>
/// Scriptable object wrapper enable property drawer for t-node
/// instance create automatically when using get function,generic node data is not support yet because of unity serialization system.
/// TODO : support generic node data
/// </summary>
// public class NodeDataWrapper:ScriptableObject{
// [SerializeReference]
// public NodeData data;
// private static readonly Dictionary<NodeData,NodeDataWrapper> Cache = new ();
// public event Action<NodeDataWrapper> OnValueChanged;
// /// <summary>
// /// Create a new wrapper or get a cached wrapper for the given data
// /// </summary>
// /// <param name="data">node data,an implemented type is acceptable</param>
// /// <returns></returns>
// public static NodeDataWrapper Get(NodeData data){
// if (data.GetType().IsGenericType){
// return CreateInstance<NodeDataWrapper>();
// }
// if(Cache.ContainsKey(data)){
// return Cache[data];
// }
// var wrapper = CreateInstance<NodeDataWrapper>();
// wrapper.data = data;
// Cache.Add(data,wrapper);
// return wrapper;
// }
//
//
// public void SetValue(string path, object value){
// var fieldInfo = data.GetType().GetField(path);
// fieldInfo.SetValue(data,value);
// OnValueChanged?.Invoke(this);
// }
//
// public object GetValue(string path){
// var fieldInfo = data.GetType().GetField(path);
// return fieldInfo.GetValue(data);
// }
// public static implicit operator NodeData(NodeDataWrapper wrapper){
// if (wrapper == null)
// return null;
// return wrapper.data;
//
// }
// /// <summary>
// /// Use this to get the wrapped data directly.
// /// </summary>
// /// <param name="unWrapper"></param>
// /// <returns></returns>
// public static implicit operator NodeDataWrapper(NodeData unWrapper){
// if (unWrapper == null)
// return null;
// return Get(unWrapper);
// }
// }
}

@ -19,7 +19,7 @@ namespace TNode.JsonSerialize{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore,
DateFormatString = "yyyy-MM-dd HH:mm:ss",
Converters = new List<JsonConverter> { new Vector3Converter() },
Converters = new List<JsonConverter> { new Vector3Converter(),new UnityObjectConverter() },
TypeNameHandling = TypeNameHandling.Auto,
ContractResolver = new WritablePropertiesOnlyResolver(),
Formatting = Formatting.Indented

@ -0,0 +1,24 @@
using System;
using Newtonsoft.Json;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
namespace TNode.JsonSerialize{
public class UnityObjectConverter:JsonConverter<Object>{
public override void WriteJson(JsonWriter writer, Object value, JsonSerializer serializer){
//Holding the object reference in a string
var go = value;
var guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(go));
writer.WriteValue(value.GetInstanceID().ToString());
}
public override Object ReadJson(JsonReader reader, Type objectType, Object existingValue, bool hasExistingValue,
JsonSerializer serializer){
//Reading the object reference from the string
var guid = reader.Value.ToString();
var go = AssetDatabase.LoadAssetAtPath<Object>(AssetDatabase.GUIDToAssetPath(guid));
return go;
}
}
}

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1c430c5760df439690d22ab18daa9d72
timeCreated: 1657700744

@ -1,5 +1,7 @@
namespace TNode.Models{
using System;
namespace TNode.Models{
[Serializable]
public class BlackboardData:IModel{
}

@ -2,37 +2,64 @@
using System.Collections.Generic;
using UnityEngine;
using Newtonsoft.Json;
using TNode.Editor;
using TNode.JsonSerialize;
using UnityEditor.Experimental.GraphView;
using UnityEngine.Serialization;
namespace TNode.Models{
[Serializable]
public class GraphData:ScriptableObject,ISerializationCallbackReceiver{
[SerializeField]
public Dictionary<string,NodeData> NodeDictionary = new Dictionary<string,NodeData>();
public List<NodeLink> nodeLinks = new();
public BlackboardData blackboardData = new();
[SerializeField]
protected List<NodeLink> nodeLinks;
[TextArea(1,10)]
[SerializeField]
//[HideInInspector]
private string jsonObject;
private string jsonNodeData;
[TextArea(1,10)]
[SerializeField]
private string jsonBlackboard;
public BlackboardData blackboardData;
public List<NodeLink> NodeLinks{
get{
return nodeLinks ??= new List<NodeLink>();
}
set => nodeLinks = value;
}
public void OnBeforeSerialize(){
jsonObject = JsonConvert.SerializeObject(NodeDictionary,JsonSerializeTool.JsonSerializerSettings);
jsonBlackboard = JsonConvert.SerializeObject(blackboardData,JsonSerializeTool.JsonSerializerSettings);
if (nodeLinks != null){
jsonNodeData = JsonConvert.SerializeObject(NodeDictionary,JsonSerializeTool.JsonSerializerSettings);
}
if (jsonBlackboard != null){
jsonBlackboard = JsonConvert.SerializeObject(blackboardData,typeof(object),JsonSerializeTool.JsonSerializerSettings);
}
}
public void OnAfterDeserialize(){
//Deserialize node dictionary
var deserializedData = JsonConvert.DeserializeObject<Dictionary<string,NodeData>>(jsonObject,JsonSerializeTool.JsonSerializerSettings);
var deserializedData = JsonConvert.DeserializeObject<Dictionary<string,NodeData>>(jsonNodeData,JsonSerializeTool.JsonSerializerSettings);
NodeDictionary = deserializedData;
//Deserialize blackboard data
var deserializedBlackboard = JsonConvert.DeserializeObject<BlackboardData>(jsonBlackboard,JsonSerializeTool.JsonSerializerSettings);
blackboardData = deserializedBlackboard;
// var deserializedBlackboard =
// JsonConvert.DeserializeObject(jsonBlackboard,JsonSerializeTool.JsonSerializerSettings);
// blackboardData = deserializedBlackboard as BlackboardData;
// Debug.Log(deserializedBlackboard);
}
public void OnEnable(){
var deserializedBlackboard =
JsonConvert.DeserializeObject(jsonBlackboard,JsonSerializeTool.JsonSerializerSettings);
blackboardData = deserializedBlackboard as BlackboardData;
Debug.Log(deserializedBlackboard);
}
}
}

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 206b9a7ba6b54706b02c6aa2cb9a18b0
timeCreated: 1656762017

@ -1,90 +0,0 @@
using System;
using System.Collections.Generic;
using TNode.Models;
using UnityEngine;
namespace TNode.Editor{
/// <summary>
/// Scriptable object wrapper enable property drawer for t-node
/// </summary>
public class NodeDataWrapper<T> : ScriptableObject where T : NodeData{
public T Data;
private static readonly Dictionary<T,NodeDataWrapper<T>> Cache = new ();
public event Action<NodeDataWrapper<T>> OnValueChanged;
public static NodeDataWrapper<T> Get(T data){
if(Cache.ContainsKey(data)){
return Cache[data];
}
var wrapper = ScriptableObject.CreateInstance<NodeDataWrapper<T>>();
Cache.Add(data,wrapper);
return wrapper;
}
public NodeDataWrapper(T data){
this.Data = data;
}
public void SetValue(string path, object value){
var fieldInfo = Data.GetType().GetField(path);
fieldInfo.SetValue(Data,value);
OnValueChanged?.Invoke(this);
}
public object GetValue(string path){
var fieldInfo = Data.GetType().GetField(path);
return fieldInfo.GetValue(Data);
}
public static implicit operator T(NodeDataWrapper<T> wrapper){
if (wrapper == null)
return null;
return wrapper.Data;
}
public static implicit operator NodeDataWrapper<T>(T unWrapper){
if (unWrapper == null)
return null;
return Get(unWrapper);
}
}
public class NodeDataWrapper:ScriptableObject{
[SerializeReference]
public NodeData Data;
private static readonly Dictionary<NodeData,NodeDataWrapper> Cache = new ();
public event Action<NodeDataWrapper> OnValueChanged;
public static NodeDataWrapper Get(NodeData data){
if (data.GetType().IsGenericType){
return ScriptableObject.CreateInstance<NodeDataWrapper>();
}
if(Cache.ContainsKey(data)){
return Cache[data];
}
var wrapper = ScriptableObject.CreateInstance<NodeDataWrapper>();
wrapper.Data = data;
Cache.Add(data,wrapper);
return wrapper;
}
public void SetValue(string path, object value){
var fieldInfo = Data.GetType().GetField(path);
fieldInfo.SetValue(Data,value);
OnValueChanged?.Invoke(this);
}
public object GetValue(string path){
var fieldInfo = Data.GetType().GetField(path);
return fieldInfo.GetValue(Data);
}
public static implicit operator NodeData(NodeDataWrapper wrapper){
if (wrapper == null)
return null;
return wrapper.Data;
}
public static implicit operator NodeDataWrapper(NodeData unWrapper){
if (unWrapper == null)
return null;
return Get(unWrapper);
}
}
}

@ -8,6 +8,7 @@ using TNode.Editor.NodeViews;
using TNode.Models;
using TNodeGraphViewImpl.Editor.GraphBlackboard;
using TNodeGraphViewImpl.Editor.NodeGraphView;
using TNodeGraphViewImpl.Editor.NodeViews;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
@ -200,7 +201,6 @@ namespace TNodeGraphViewImpl.Editor.Cache{
//Check the generic type of BaseNodeView by t
if (t.IsGenericType){
Debug.Log($"A generic type {t} is detected");
//AKA if BlackboardDragNodeData<Camera> is pulled
//Get BlackboardDragNodeData<T> as generic type
@ -209,14 +209,12 @@ namespace TNodeGraphViewImpl.Editor.Cache{
//What you want is a BaseNodeView<BlackboardDragNodeData<T>> to be created
var genericViewType = typeof(BaseNodeView<>).MakeGenericType(genericTypeDefinition);
Debug.Log($"The generic view type is {genericViewType}");
//search for the specific type of genericViewType in the dictionary
if (NodeEditorSingleton.Instance.FromGenericToSpecific.ContainsKey(genericViewType)){
var implementedType = NodeEditorSingleton.Instance.FromGenericToSpecific[genericViewType];
//The implementedType is still a generic type ,so we make it a specific type by using MakeGenericType
Debug.Log($"{implementedType}");
//Get argument type of t
var argumentType = t.GetGenericArguments()[0];
var instance = Activator.CreateInstance(implementedType.MakeGenericType(argumentType));

@ -3,27 +3,45 @@ using System.Reflection;
using TNode.Attribute;
using TNode.Editor.NodeGraphView;
using TNode.Editor.Search;
using TNode.Editor.Serialization;
using TNode.Models;
using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
namespace TNodeGraphViewImpl.Editor.GraphBlackboard{
[ViewComponent]
public class DefaultGraphBlackboardView:GraphBlackboardView<BlackboardData>{
protected override void UpdateBlackboard(BlackboardData data){
var serializedObject = new SerializedObject((BlackboardDataWrapper)data);
foreach (var field in data.GetType()
.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)){
//if the field is MonoBehaviour,add a property field for blackboard
//skip if the field is a list or Ilist
if (!typeof(IList).IsAssignableFrom(field.FieldType)){
VisualElement visualElement = new VisualElement();
var propertyField = new BlackboardPropertyField(new BlackboardProperty.BlackboardProperty(field.Name,field.FieldType));
this.Add(propertyField);
var foldoutData = new Foldout{
text = field.Name
};
var drawer = new PropertyField(serializedObject.FindProperty("data").FindPropertyRelative(field.Name),field.Name);
drawer.Bind(serializedObject);
foldoutData.Add(drawer);
visualElement.Add(propertyField);
visualElement.Add(foldoutData);
this.Add(visualElement);
}
else{
var blackboardList = new BlackboardSection{
title = field.Name
};
this.Add(blackboardList);
}
}
this.addItemRequested = (sender) => {
addItemRequested = (sender) => {
var res = ScriptableObject.CreateInstance<BlackboardSearchWindowProvider>();
//Get right top corner of the blackboard

@ -5,6 +5,7 @@ using TNode.Attribute;
using TNode.Editor.NodeViews;
using TNode.Models;
using TNodeGraphViewImpl.Editor.NodeGraphView;
using TNodeGraphViewImpl.Editor.NodeViews;
using Unity.VisualScripting;
using UnityEditor;
using UnityEditor.Experimental.GraphView;

@ -1,5 +1,6 @@
using System.Reflection;
using TNode.Attribute;
using TNode.Editor.Serialization;
using TNode.Models;
using UnityEditor;
using UnityEditor.UIElements;
@ -47,11 +48,11 @@ namespace TNode.Editor.Inspector{
var showInNodeViewAttribute = field.GetCustomAttribute<ShowInNodeViewAttribute>() != null;
if (!showInNodeViewAttribute)
continue;
var drawer = new PropertyField(serializedObject.FindProperty("Data").FindPropertyRelative(field.Name),field.Name);
Debug.Log(serializedObject.FindProperty("Data"));
var drawer = new PropertyField(serializedObject.FindProperty("data").FindPropertyRelative(field.Name),field.Name);
drawer.Bind(serializedObject);
Add(drawer);
}
}
}
}

@ -15,6 +15,7 @@ using TNode.Models;
using TNodeGraphViewImpl.Editor.Cache;
using TNodeGraphViewImpl.Editor.GraphBlackboard;
using TNodeGraphViewImpl.Editor.GraphBlackboard.BlackboardProperty;
using TNodeGraphViewImpl.Editor.NodeViews;
using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
@ -156,7 +157,7 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{
AddTNode(dataNode,nodePos);
}
foreach (var edge in _data.nodeLinks){
foreach (var edge in _data.NodeLinks){
var inputNode = _data.NodeDictionary[edge.inPort.nodeDataId];
var outputNode = _data.NodeDictionary[edge.outPort.nodeDataId];
var inputNodeView = _nodeDict[inputNode.id];
@ -194,12 +195,13 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{
private void BlackboardUpdate(){
if (_data.blackboardData == null || _data.blackboardData.GetType() == typeof(BlackboardData)){
if (_data.blackboardData == null || _data.blackboardData.GetType()==(typeof(BlackboardData))){
_data.blackboardData = NodeEditorExtensions.GetAppropriateBlackboardData(_data.GetType());
if (_data.blackboardData == null) return;
_blackboard.SetBlackboardData(_data.blackboardData);
}
_blackboard.SetBlackboardData(_data.blackboardData);
}
public virtual void DestroyInspector(){
@ -272,17 +274,23 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{
}
_data.nodeLinks = links;
_data.NodeLinks = links;
}
private void SaveGraphData(){
_data.NodeDictionary.Clear();
_data.nodeLinks.Clear();
_data.NodeLinks.Clear();
SaveNode();
SaveEdge();
SaveBlackboard();
EditorUtility.SetDirty(_data);
}
private void SaveBlackboard(){
if (_data.blackboardData == null){
_data.blackboardData = NodeEditorExtensions.GetAppropriateBlackboardData(_data.GetType());
}
}
public override List<Port> GetCompatiblePorts(Port startPort, NodeAdapter nodeAdapter){
return ports.Where(x => x.portType == startPort.portType).ToList();
@ -354,7 +362,7 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{
Add(castedBlackboard);
Rect blackboardPos = new Rect(0,0,200,700);
Rect blackboardPos = new Rect(0,0,300,700);
castedBlackboard?.SetPosition(blackboardPos);

@ -1,5 +1,6 @@
using TNode.Editor.NodeViews;
using TNode.Models;
using TNodeGraphViewImpl.Editor.NodeViews;
namespace TNode.Editor{

@ -1,5 +1,6 @@
using TNode.Attribute;
using TNode.Models;
using TNodeGraphViewImpl.Editor.NodeViews;
namespace TNode.Editor.NodeViews{
[ViewComponent]

@ -1,15 +1,15 @@
using System;
using System.Linq;
using System.Reflection;
using TNode.Attribute;
using TNode.Attribute.Ports;
using TNode.Editor.Inspector;
using TNode.Editor.Serialization;
using TNode.Models;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
using UnityEngine.UIElements;
namespace TNode.Editor.NodeViews{
namespace TNodeGraphViewImpl.Editor.NodeViews{
public abstract class BaseNodeView<T> : Node,INodeView<T> where T:NodeData,new(){
protected T _data;
@ -28,10 +28,9 @@ namespace TNode.Editor.NodeViews{
}
}
private void OnDataValueChanged(NodeDataWrapper obj){
private void OnDataValueChanged(DataWrapper<NodeDataWrapper, NodeData> obj){
Refresh();
}
public sealed override string title{
get => base.title;
set => base.title = value;

Loading…
Cancel
Save