From de7503bb4a216e60594b44c40e1da66a81e096c8 Mon Sep 17 00:00:00 2001 From: Pascal Serrarens Date: Fri, 9 Jan 2026 10:08:06 +0100 Subject: [PATCH] Add perception array UI --- Assets/NanoBrain/PercepteiArray.cs | 67 +++++++++++--- Assets/NanoBrain/Perceptoid.cs | 45 ++++++---- .../VisualEditor/Editor/NanoBrainInspector.cs | 89 +++++++++++++------ Assets/Scenes/Boids/Boids.unity | 2 +- Assets/Scenes/Boids/SwarmingBrain.asset | 26 ++++-- 5 files changed, 161 insertions(+), 68 deletions(-) diff --git a/Assets/NanoBrain/PercepteiArray.cs b/Assets/NanoBrain/PercepteiArray.cs index cf4b5e2..a46f2d0 100644 --- a/Assets/NanoBrain/PercepteiArray.cs +++ b/Assets/NanoBrain/PercepteiArray.cs @@ -1,22 +1,61 @@ +using UnityEngine; + +[System.Serializable] public class PercepteiArray { - public ArrayPerceptoid[] perceptei; + [SerializeReference] + public Perceptoid[] perceptei; public string name; - public PercepteiArray(NanoBrain brain, int thingType, string baseName, uint count) { - this.name = baseName; - this.perceptei = new ArrayPerceptoid[count]; - for (uint i = 0; i < count; i++) { - this.perceptei[i] = new ArrayPerceptoid(brain, thingType, $"{baseName}[{i}]") { - array = this - }; - } + // public PercepteiArray(NanoBrain brain, int thingType, string baseName, uint count) { + // this.name = baseName; + // this.perceptei = new Perceptoid[count]; + // for (uint i = 0; i < count; i++) { + // this.perceptei[i] = new Perceptoid(brain, thingType, $"{baseName}[{i}]") { + // array = this + // }; + // } + // } + public PercepteiArray(Perceptoid perceptoid) { + this.name = perceptoid.baseName; + this.perceptei = new Perceptoid[1]; + this.perceptei[0] = perceptoid; + } + + public void AddPerceptoid() { + if (this.perceptei.Length == 0) { + Debug.LogError("Empty perceptoid array, cannot add"); + return; + } + int newLength = this.perceptei.Length + 1; + Perceptoid[] newPerceptei = new Perceptoid[newLength]; + + for (int i = 0; i < this.perceptei.Length; i++) + newPerceptei[i] = this.perceptei[i]; + newPerceptei[newLength - 1] = new Perceptoid(this); + + this.perceptei = newPerceptei; + } + + public void RemovePerceptoid() { + int newLength = this.perceptei.Length - 1; + if (newLength == 0) { + Debug.LogWarning("Perceptoid array cannot be empty"); + return; + } + Perceptoid[] newPerceptei = new Perceptoid[newLength]; + for (int i = 0; i < newLength; i++) + newPerceptei[i++] = this.perceptei[i]; + // Delete the last perception + Nucleus.Delete(this.perceptei[newLength]); + + this.perceptei = newPerceptei; } } -public class ArrayPerceptoid : Perceptoid { - public PercepteiArray array; +// public class ArrayPerceptoid : Perceptoid { +// public PercepteiArray array; - public ArrayPerceptoid(NanoBrain brain, int thingType, string name = "sensor") : base(brain, thingType, name) { - } +// public ArrayPerceptoid(NanoBrain brain, int thingType, string name = "sensor") : base(brain, thingType, name) { +// } -} \ No newline at end of file +// } \ No newline at end of file diff --git a/Assets/NanoBrain/Perceptoid.cs b/Assets/NanoBrain/Perceptoid.cs index 62f0b11..3f1d0b1 100644 --- a/Assets/NanoBrain/Perceptoid.cs +++ b/Assets/NanoBrain/Perceptoid.cs @@ -1,5 +1,4 @@ using UnityEngine; -using LinearAlgebra; [System.Serializable] public class Perceptoid : Neuroid { @@ -8,16 +7,24 @@ public class Perceptoid : Neuroid { public Receptor receptor; public string baseName; + public int thingId; + + //[SerializeField] + // Needs serialization!!!! + [SerializeReference] + public PercepteiArray array; + #region Serialization [SerializeField] public int thingType; - public int thingId; public override void Rebuild(NanoBrain brain) { base.Rebuild(brain); this.receptor = Receptor.GetReceptor(brain, thingType); this.receptor.perceptei.Add(this); + if (string.IsNullOrEmpty(this.baseName)) + this.baseName = this.name; } public override void Deserialize(Nucleus nucleus) { @@ -62,30 +69,30 @@ public class Perceptoid : Neuroid { this.thingType = thingType; this.receptor = Receptor.GetReceptor(brain, thingType); this.receptor.perceptei.Add(this); + this.array = new PercepteiArray(this); } - // public void Replace(int thingType, string name = "sensor") { - // this.name = name; + public Perceptoid(PercepteiArray array) : base(array.name) { + this.array = array; + Perceptoid source = array.perceptei[0]; + this.brain = source.brain; + if (this.brain != null) { + this.brain.perceptei.Add(this); + } + else + Debug.LogError("No neuroid network"); - // this.thingType = thingType; - // this.receptor.thingType = thingType; - // this.receptor.localPosition = Vector3.zero; - - // this.outputValue = Vector3.zero; - // this.receivers = new(); - // } + this.nucleusType = nameof(Perceptoid); + this.name = source.baseName; + this.baseName = source.baseName; + this.thingType = source.thingType; + this.receptor = Receptor.GetReceptor(this.brain, this.thingType); + this.receptor.perceptei.Add(this); + } public override void UpdateState() { Vector3 result = this.receptor.localPosition; UpdateResult(result); } - - // public static Perceptoid GetPerception(NanoBrain brain, int thingType = 0) { - // foreach (Nucleus nucleus in brain.nuclei) { - // if (nucleus is Perceptoid perceptoid && (thingType == 0 || perceptoid.receptor.thingType == thingType)) - // return perceptoid; - // } - // return null; - // } } diff --git a/Assets/NanoBrain/VisualEditor/Editor/NanoBrainInspector.cs b/Assets/NanoBrain/VisualEditor/Editor/NanoBrainInspector.cs index e8f7daf..cb39199 100644 --- a/Assets/NanoBrain/VisualEditor/Editor/NanoBrainInspector.cs +++ b/Assets/NanoBrain/VisualEditor/Editor/NanoBrainInspector.cs @@ -278,19 +278,19 @@ public class NanoBrainInspector : Editor { Vector3 pos = new(250, margin + row * spacing, 0.0f); Handles.color = Color.white; Handles.DrawLine(parentPos, pos); - if (synapse.nucleus is ArrayPerceptoid perceptoid) { - if (drawnArrays.Contains(perceptoid.array)) - // We already drawn this array - continue; + // if (synapse.nucleus is Perceptoid perceptoid && perceptoid.array != null) { + // // if (drawnArrays.Contains(perceptoid.array)) + // // // We already drawn this array + // // continue; - drawnArrays.Add(perceptoid.array); - DrawArray(perceptoid.array, pos, size); - } - else { + // drawnArrays.Add(perceptoid.array); + // DrawArray(perceptoid.array, pos, size); + // } + // else { - DrawNucleus(synapse.nucleus, pos, maxValue, size); - row++; - } + DrawNucleus(synapse.nucleus, pos, maxValue, size); + row++; + // } } } @@ -298,16 +298,35 @@ public class NanoBrainInspector : Editor { if (nucleus.isSleeping) Handles.color = Color.darkRed; else { - float brightness = nucleus.outputValue.magnitude / maxValue; - Handles.color = new Color(brightness, brightness, brightness); + if (Application.isPlaying) { + float brightness = nucleus.outputValue.magnitude / maxValue; + Handles.color = new Color(brightness, brightness, brightness, 1f); + } + else + Handles.color = Color.black; } Handles.DrawSolidDisc(position, Vector3.forward, size); - Vector3 labelPos = position - Vector3.down * (size + 0.2f); // below disc along up axis + + Handles.color = Color.white; + // Position the label in front of the disc + Vector3 labelPosition = position + (Vector3.forward * 0.1f); + GUIStyle style = new(EditorStyles.label) { - alignment = TextAnchor.UpperCenter, + alignment = TextAnchor.MiddleCenter, normal = { textColor = Color.white }, - fontStyle = FontStyle.Bold + fontStyle = FontStyle.Bold, }; + if (nucleus is Perceptoid perceptoid) { + if (perceptoid.array == null || perceptoid.array.perceptei == null || perceptoid.array.perceptei.Length == 0) + perceptoid.array = new PercepteiArray(perceptoid); + + if (perceptoid.array.perceptei.Length > 1) { + Handles.Label(labelPosition, perceptoid.array.perceptei.Length.ToString(), style); + } + } + + style.alignment = TextAnchor.UpperCenter; + Vector3 labelPos = position - Vector3.down * (size + 0.2f); // below disc along up axis Handles.Label(labelPos, nucleus.name, style); Rect neuronRect = new(position.x - size, position.y - size, size * 2, size * 2); @@ -326,21 +345,18 @@ public class NanoBrainInspector : Editor { } } - private void DrawArray(PercepteiArray array, Vector3 position, float size) { - Vector3 offset = new(size/4, size/4, 0); - Handles.color = Color.darkGray; - Handles.DrawSolidDisc(position + offset * 2, Vector3.forward, size); - Handles.color = Color.lightGray; - Handles.DrawSolidDisc(position + offset, Vector3.forward, size); - Handles.color = Color.white; + private void DrawArray(PercepteiArray array, Vector3 position, float size) { + Vector3 offset = new(size / 4, size / 4, 0); + Handles.color = Color.black; Handles.DrawSolidDisc(position, Vector3.forward, size); - Vector3 labelPos = position - Vector3.down * (size + 0.2f); // below disc along up axis - GUIStyle style = new GUIStyle(EditorStyles.label) { + GUIStyle style = new(EditorStyles.label) { alignment = TextAnchor.UpperCenter, normal = { textColor = Color.white }, fontStyle = FontStyle.Bold }; + Handles.Label(position, array.perceptei.Length.ToString(), style); + Vector3 labelPos = position - Vector3.down * (size + 0.2f); // below disc along up axis Handles.Label(labelPos, array.name, style); // To do: add HandleClick (see above) to expand the array @@ -403,8 +419,18 @@ public class NanoBrainInspector : Editor { return; this.currentNucleus.name = EditorGUILayout.TextField(this.currentNucleus.name); - if (this.currentNucleus is Perceptoid currentPerceptoid) { - currentPerceptoid.receptor.thingType = EditorGUILayout.IntField("Thing Type", currentPerceptoid.receptor.thingType); + if (this.currentNucleus is Perceptoid perceptoid) { + perceptoid.receptor.thingType = EditorGUILayout.IntField("Thing Type", perceptoid.receptor.thingType); + + if (perceptoid.array == null || perceptoid.array.perceptei == null || perceptoid.array.perceptei.Length == 0) + perceptoid.array = new PercepteiArray(perceptoid); + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.IntField("Array size", perceptoid.array.perceptei.Length); + if (GUILayout.Button("Add")) + perceptoid.array.AddPerceptoid(); + if (GUILayout.Button("Del")) + perceptoid.array.RemovePerceptoid(); + EditorGUILayout.EndHorizontal(); } else if (this.currentNucleus is Neuroid neuroid) { EditorGUILayout.BeginHorizontal(); @@ -427,12 +453,21 @@ public class NanoBrainInspector : Editor { foreach (Synapse synapse in synapses) { if (synapse.nucleus != null) { EditorGUILayout.Space(); + EditorGUI.BeginDisabledGroup(synapse.nucleus.isSleeping); if (Application.isPlaying) EditorGUILayout.FloatField(synapse.nucleus.name, synapse.nucleus.outputValue.magnitude * synapse.weight); else { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(synapse.nucleus.name); + // if (synapse.nucleus is Perceptoid perceptoid) { + // if (perceptoid.array == null || perceptoid.array.perceptei == null || perceptoid.array.perceptei.Length == 0) { + // perceptoid.array = new PercepteiArray(perceptoid); + // } + // EditorGUILayout.IntField(perceptoid.array.perceptei.Length); + // if (GUILayout.Button("Add")) + // perceptoid.array.AddPerceptoid(); + // } if (GUILayout.Button("Disconnect")) synapse.nucleus.RemoveReceiver(this.currentNucleus); EditorGUILayout.EndHorizontal(); diff --git a/Assets/Scenes/Boids/Boids.unity b/Assets/Scenes/Boids/Boids.unity index de7d29d..2c0ccaf 100644 --- a/Assets/Scenes/Boids/Boids.unity +++ b/Assets/Scenes/Boids/Boids.unity @@ -375,7 +375,7 @@ MonoBehaviour: m_EditorClassIdentifier: Assembly-CSharp::SwarmControl speed: 1 inertia: 0.7 - alignmentForce: 0.5 + alignmentForce: 0.4 cohesionForce: 0.1 separationForce: -0.1 avoidanceForce: 1 diff --git a/Assets/Scenes/Boids/SwarmingBrain.asset b/Assets/Scenes/Boids/SwarmingBrain.asset index c78bca5..92d4f4b 100644 --- a/Assets/Scenes/Boids/SwarmingBrain.asset +++ b/Assets/Scenes/Boids/SwarmingBrain.asset @@ -142,8 +142,8 @@ MonoBehaviour: inWeight: 0 outWeight: 0 - serializedVersion: 3 - time: 1 - value: 1 + time: 1000 + value: 1000 inSlope: 1 outSlope: 0 tangentMode: 0 @@ -250,8 +250,8 @@ MonoBehaviour: inWeight: 0 outWeight: 0 - serializedVersion: 3 - time: 1 - value: 1 + time: 1000 + value: 1000 inSlope: 1 outSlope: 0 tangentMode: 0 @@ -3786,7 +3786,10 @@ MonoBehaviour: exponent: 1 - id: 763145504 _name: Inverse Boid D - synapses: [] + synapses: + - nucleusId: -1095934288 + weight: 1 + curveMax: 1 receivers: - nucleusId: 1641120128 nucleusType: @@ -4956,7 +4959,10 @@ MonoBehaviour: exponent: 1 - id: 1469392160 _name: Inverse Boid E - synapses: [] + synapses: + - nucleusId: -1413006832 + weight: 1 + curveMax: 1 receivers: - nucleusId: 1641120128 nucleusType: @@ -6126,7 +6132,10 @@ MonoBehaviour: exponent: 1 - id: -1828317248 _name: Inverse Bodi F - synapses: [] + synapses: + - nucleusId: -61245040 + weight: 1 + curveMax: 1 receivers: - nucleusId: 1641120128 nucleusType: @@ -7387,6 +7396,7 @@ MonoBehaviour: synapses: [] receivers: - nucleusId: 1938577052 + - nucleusId: 763145504 nucleusType: Perceptoid isSleeping: 0 _curvePreset: 0 @@ -7408,6 +7418,7 @@ MonoBehaviour: synapses: [] receivers: - nucleusId: 1938577052 + - nucleusId: 1469392160 nucleusType: Perceptoid isSleeping: 0 _curvePreset: 0 @@ -7429,6 +7440,7 @@ MonoBehaviour: synapses: [] receivers: - nucleusId: 1938577052 + - nucleusId: -1828317248 nucleusType: Perceptoid isSleeping: 0 _curvePreset: 0