Adding JSON serialization UI

This commit is contained in:
Pascal Serrarens 2026-07-10 10:07:22 +02:00
parent 8afd5e006f
commit 27f4237890
7 changed files with 129 additions and 17 deletions

View File

@ -0,0 +1,72 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace NanoBrain.Unity {
[CustomPropertyDrawer(typeof(ClusterJson))]
class ClusterJson_Drawer : PropertyDrawer {
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
EditorGUI.BeginProperty(position, label, property);
ClusterJson clusterJson = property.objectReferenceValue as ClusterJson;
if (clusterJson == null) {
clusterJson = ScriptableObject.CreateInstance<ClusterJson>();
property.objectReferenceValue = clusterJson;
property.serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(property.serializedObject.targetObject);
}
SerializedObject so = new(clusterJson);
SerializedProperty jsonFileProp = so.FindProperty(nameof(ClusterJson.jsonFile));
EditorGUI.BeginChangeCheck();
TextAsset current = jsonFileProp.objectReferenceValue as TextAsset;
Rect fieldRect = new(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
TextAsset newAsset = (TextAsset)EditorGUI.ObjectField(fieldRect, label, current, typeof(TextAsset), false);
if (EditorGUI.EndChangeCheck()) {
jsonFileProp.objectReferenceValue = newAsset;
so.ApplyModifiedProperties();
}
bool showWarning = IsInvalidJson(jsonFileProp);
if (showWarning) {
Rect helpRect = new(
position.x,
position.y + EditorGUIUtility.singleLineHeight + 2f,
position.width,
EditorGUIUtility.singleLineHeight * 2f
);
propertyExtraHeight = 2 + 2 * EditorGUIUtility.singleLineHeight;
EditorGUI.HelpBox(helpRect, "Selected asset is not a .json file.", MessageType.Error);
} else
propertyExtraHeight = 0;
EditorGUI.EndProperty();
}
private float propertyExtraHeight = 0;
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
return EditorGUIUtility.singleLineHeight + propertyExtraHeight;
}
private readonly Dictionary<EntityId, bool> _invalidCache = new();
private bool IsInvalidJson(SerializedProperty jsonFileProp) {
TextAsset current = jsonFileProp.objectReferenceValue as TextAsset;
if (current == null) return false;
EntityId id = current.GetEntityId();
if (_invalidCache.TryGetValue(id, out bool invalid))
return invalid;
string path = AssetDatabase.GetAssetPath(current);
invalid = !path.EndsWith(".json", System.StringComparison.OrdinalIgnoreCase);
_invalidCache[id] = invalid;
return invalid;
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 879b585246de16598959c21b724cf105

View File

@ -25,7 +25,8 @@ namespace NanoBrain.Unity {
return height; return height;
SerializedProperty prefabProp = property.FindPropertyRelative(nameof(Cluster.prefab)); SerializedProperty prefabProp = property.FindPropertyRelative(nameof(Cluster.prefab));
if (prefabProp.objectReferenceValue != null && Cluster_Drawer.currentClusterView.isOpen) { if (//prefabProp.objectReferenceValue != null &&
Cluster_Drawer.currentClusterView.isOpen) {
height = graphHeight; height = graphHeight;
} }
else { else {
@ -92,18 +93,19 @@ namespace NanoBrain.Unity {
} }
// If a brain has been selected // If a brain has been selected
if (prefabProp.objectReferenceValue != null) { //if (prefabProp.objectReferenceValue != null) {
// Graph is not shown when multi-editing // Graph is not shown when multi-editing
if (property.serializedObject.targetObjects.Length == 1) { if (property.serializedObject.targetObjects.Length == 1) {
UnityEngine.Object targetObject = property.serializedObject.targetObject; UnityEngine.Object targetObject = property.serializedObject.targetObject;
Cluster_Drawer.selectedTarget = targetObject; Cluster_Drawer.selectedTarget = targetObject;
Cluster cluster = SerializedPropertyUtility.GetManagedObjectForProperty(targetObject, property.propertyPath) as Cluster; Cluster cluster = SerializedPropertyUtility.GetManagedObjectForProperty(targetObject, property.propertyPath) as Cluster;
if (cluster.version != cluster.prefab.version) { if (cluster != null) {
// Debug.Log($"prefab version: {cluster.prefab.version} cluster version: {cluster.version}"); // if (cluster.version != cluster.prefab.version) {
clusterView.initialized = false; // // Debug.Log($"prefab version: {cluster.prefab.version} cluster version: {cluster.version}");
EditorApplication.delayCall += () => InstantiateCluster(property, clusterView); // clusterView.initialized = false;
} // EditorApplication.delayCall += () => InstantiateCluster(property, clusterView);
// }
// foldout header rect // foldout header rect
Rect headerRect = new(fieldRect.x, fieldRect.yMax + 4f, fieldRect.width, EditorGUIUtility.singleLineHeight); Rect headerRect = new(fieldRect.x, fieldRect.yMax + 4f, fieldRect.width, EditorGUIUtility.singleLineHeight);
@ -122,8 +124,10 @@ namespace NanoBrain.Unity {
clusterView.Render(drawRect); clusterView.Render(drawRect);
//Debug.Log(prefab.cluster.defaultOutput.outputMagnitude); //Debug.Log(prefab.cluster.defaultOutput.outputMagnitude);
} }
} }
} }
//}
EditorGUI.indentLevel = indent; EditorGUI.indentLevel = indent;
EditorGUI.EndProperty(); EditorGUI.EndProperty();

View File

@ -0,0 +1,10 @@
using UnityEngine;
namespace NanoBrain.Unity {
[System.Serializable]
public class ClusterJson : ScriptableObject {
public TextAsset jsonFile;
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8ea9c456ab9da37daaa610edaadc38bb

View File

@ -1,4 +1,5 @@
using System; using System;
using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
#if UNITY_MATHEMATICS #if UNITY_MATHEMATICS
@ -112,6 +113,24 @@ namespace NanoBrain {
ClonePrefab(); ClonePrefab();
} }
public Cluster(ClusterJson json) {
ClusterData clusterData = JsonUtility.FromJson<ClusterData>(json.jsonFile.text);
this.name = clusterData.name;
// First create all neurons
foreach (NeuronData neuronData in clusterData.neurons) {
Neuron newNeuron = new(this, neuronData.name);
}
// Now create the synapses between them
foreach (NeuronData neuronData in clusterData.neurons) {
Neuron receiver = this.GetNeuron(neuronData.name);
foreach (SynapseData synapseData in neuronData.synapses) {
Neuron sender = this.GetNeuron(synapseData.neuronName);
sender?.AddReceiver(receiver, synapseData.weight);
}
}
}
/// <summary> /// <summary>
/// Clone a prefab. /// Clone a prefab.
/// </summary> /// </summary>
@ -785,6 +804,11 @@ namespace NanoBrain {
System.IO.File.WriteAllText(path, json); System.IO.File.WriteAllText(path, json);
} }
public ClusterData Import(string path) {
string json = File.ReadAllText(path);
return JsonUtility.FromJson<ClusterData>(json);
}
#endregion #endregion
} }
@ -800,8 +824,9 @@ namespace NanoBrain {
if (nucleus is Neuron neuron) { if (nucleus is Neuron neuron) {
NeuronData neuronData = new(neuron); NeuronData neuronData = new(neuron);
this.neurons.Add(neuronData); this.neurons.Add(neuronData);
} else if (nucleus is Cluster extCluster) { }
ExternalClusterData clusterData = new (extCluster); else if (nucleus is Cluster extCluster) {
ExternalClusterData clusterData = new(extCluster);
this.clusters.Add(clusterData); this.clusters.Add(clusterData);
} }
} }

View File

@ -39,9 +39,6 @@ namespace NanoBrain {
Neuron, Neuron,
MemoryCell, MemoryCell,
Cluster, Cluster,
//Receptor,
//ClusterReceptor,
//ClusterArray,
} }
#region Update #region Update