From 4f2f23033575c0e30d7e4b7f5b6c935c8c1396f6 Mon Sep 17 00:00:00 2001 From: Pascal Serrarens Date: Tue, 13 Jan 2026 16:33:16 +0100 Subject: [PATCH] Nucleus+Neuroid->Neuron, improved Receptor support --- Assembly-CSharp-Editor.csproj | 2 +- Assembly-CSharp.csproj | 4 +- Assets/NanoBrain/Cluster.cs | 30 +++++---- Assets/NanoBrain/INucleus.cs | 2 +- Assets/NanoBrain/Neuroid.cs | 4 +- Assets/NanoBrain/{Nucleus.cs => Neuron.cs} | 60 ++++++++++++++--- .../{Nucleus.cs.meta => Neuron.cs.meta} | 0 Assets/NanoBrain/NucleusArray.cs | 2 +- Assets/NanoBrain/Receptor.cs | 36 ++++++++++- .../VisualEditor/Editor/ClusterInspector.cs | 43 +++++++------ Assets/Scenes/Boids/New Cluster.asset | 60 ----------------- Assets/Scenes/Boids/New Cluster.asset.meta | 8 --- Assets/Scenes/Boids/NewSwarm.asset | 64 ++++--------------- Assets/Scenes/Boids/NewSwarm.asset.meta | 2 +- Assets/Scenes/Boids/Prefabs/Boid.prefab | 2 +- 15 files changed, 148 insertions(+), 171 deletions(-) rename Assets/NanoBrain/{Nucleus.cs => Neuron.cs} (70%) rename Assets/NanoBrain/{Nucleus.cs.meta => Neuron.cs.meta} (100%) delete mode 100644 Assets/Scenes/Boids/New Cluster.asset delete mode 100644 Assets/Scenes/Boids/New Cluster.asset.meta diff --git a/Assembly-CSharp-Editor.csproj b/Assembly-CSharp-Editor.csproj index 94f9e7e..36cc367 100644 --- a/Assembly-CSharp-Editor.csproj +++ b/Assembly-CSharp-Editor.csproj @@ -43,7 +43,7 @@ 6000.3.2f1 - + diff --git a/Assembly-CSharp.csproj b/Assembly-CSharp.csproj index 891c3d0..771999b 100644 --- a/Assembly-CSharp.csproj +++ b/Assembly-CSharp.csproj @@ -43,7 +43,7 @@ 6000.3.2f1 - + @@ -67,6 +67,7 @@ + @@ -84,7 +85,6 @@ - diff --git a/Assets/NanoBrain/Cluster.cs b/Assets/NanoBrain/Cluster.cs index 0cf80dc..079279c 100644 --- a/Assets/NanoBrain/Cluster.cs +++ b/Assets/NanoBrain/Cluster.cs @@ -5,18 +5,26 @@ using static Unity.Mathematics.math; [CreateAssetMenu(menuName = "Passer/Cluster")] public class Cluster : ScriptableObject, INucleus { - + public Cluster cluster => this; [SerializeReference] - public List nuclei = new(); + public List nuclei = new(); - public INucleus output => this.nuclei[0]; + public INucleus output => this.nuclei[0] as INucleus; - //private readonly List _inputs = new(); - public List inputs { // = compare receptors in NanoBrain - // for now all nuclei are inputs - get { return this.nuclei; } + public List _inputs = null; + public List inputs { + get { + if (this._inputs == null) { + this._inputs = new(); + foreach (IReceptor receptor in this.nuclei) { + if (receptor is INucleus nucleus) + this._inputs.Add(nucleus); + } + } + return this._inputs; + } } // The synapses of all inputs @@ -35,9 +43,9 @@ public class Cluster : ScriptableObject, INucleus { // This is an invariant and should be ensured before the nucleus is used // because output requires it. public void EnsureInitialization() { - nuclei ??= new List(); + nuclei ??= new List(); if (nuclei.Count == 0) - new Neuroid(this, "Output"); // Every cluster should have at least 1 neuroid + new Neuron(this, "Output"); // Every cluster should have at least 1 neuron } public void AddReceiver(INucleus receiver) { @@ -61,7 +69,7 @@ public class Cluster : ScriptableObject, INucleus { HashSet visitedNuclei = new(); MarkNuclei(visitedNuclei, this.output); //Debug.Log($"Garbage collection found {visitedNuclei.Count} Nuclei"); - this.nuclei.RemoveAll(nucleus => visitedNuclei.Contains(nucleus) == false); + this.nuclei.RemoveAll(nucleus => nucleus is INucleus n && visitedNuclei.Contains(n) == false); //this.perceptei.RemoveAll(perceptoid => visitedNuclei.Contains(perceptoid) == false); } @@ -105,7 +113,7 @@ public class Cluster : ScriptableObject, INucleus { } public void UpdateNuclei() { - foreach (INucleus nucleus in nuclei) + foreach (IReceptor nucleus in this.nuclei) nucleus.UpdateNuclei(); } diff --git a/Assets/NanoBrain/INucleus.cs b/Assets/NanoBrain/INucleus.cs index c565c3e..3ff6947 100644 --- a/Assets/NanoBrain/INucleus.cs +++ b/Assets/NanoBrain/INucleus.cs @@ -23,7 +23,6 @@ public interface INucleus : IReceptor { public void UpdateState(); - public void UpdateNuclei(); #endregion dynamic state @@ -47,6 +46,7 @@ public interface IReceptor { // float3 to prepare for SIMD public float3 outputValue { get; } + public void UpdateNuclei(); public bool isSleeping { get; } #endregion dynamic diff --git a/Assets/NanoBrain/Neuroid.cs b/Assets/NanoBrain/Neuroid.cs index a28583a..d4a64f2 100644 --- a/Assets/NanoBrain/Neuroid.cs +++ b/Assets/NanoBrain/Neuroid.cs @@ -1,9 +1,10 @@ +/* using UnityEngine; using Unity.Mathematics; using static Unity.Mathematics.math; [System.Serializable] -public class Neuroid : Nucleus { +public class Neuroid : Neuron { public bool average = false; @@ -78,3 +79,4 @@ public class Neuroid : Nucleus { } +*/ \ No newline at end of file diff --git a/Assets/NanoBrain/Nucleus.cs b/Assets/NanoBrain/Neuron.cs similarity index 70% rename from Assets/NanoBrain/Nucleus.cs rename to Assets/NanoBrain/Neuron.cs index 65a30c7..67fed76 100644 --- a/Assets/NanoBrain/Nucleus.cs +++ b/Assets/NanoBrain/Neuron.cs @@ -5,7 +5,7 @@ using Unity.Mathematics; using static Unity.Mathematics.math; [Serializable] -public class Nucleus : INucleus { +public class Neuron : INucleus { [SerializeField] protected string _name; @@ -49,6 +49,7 @@ public class Nucleus : INucleus { } public AnimationCurve curve; public float curveMax = 1.0f; + public bool average = false; public AnimationCurve GenerateCurve() { switch (this.curvePreset) { @@ -70,7 +71,7 @@ public class Nucleus : INucleus { } } - public virtual void Deserialize(Nucleus nucleus) { } + public virtual void Deserialize(Neuron nucleus) { } #endregion Serialization @@ -103,17 +104,27 @@ public class Nucleus : INucleus { #endregion Runtime state - public Nucleus(string name) { + public Neuron(Cluster brain, string name) : this(name) { + this.cluster = brain; + if (this.cluster != null) { + this.cluster.nuclei.Add(this); + } + else + Debug.LogError("No neuroid network"); + } + + public Neuron(string name) { this._name = name; } public virtual INucleus Clone() { - Nucleus clone = new(this.name) { + Neuron clone = new(this.name) { cluster = this.cluster, array = this.array, curve = this.curve, curvePreset = this.curvePreset, - curveMax = this.curveMax + curveMax = this.curveMax, + average = this.average }; if (clone.cluster != null) clone.cluster.nuclei.Add(clone); @@ -140,14 +151,14 @@ public class Nucleus : INucleus { public static void Delete(INucleus nucleus) { foreach (Synapse synapse in nucleus.synapses) { - if (synapse.nucleus is Nucleus synapse_nucleus) { + if (synapse.nucleus is Neuron synapse_nucleus) { if (synapse_nucleus.receivers.Count > 1) { // there is another nucleus feeding into this input nucleus synapse_nucleus.receivers.RemoveAll(r => r == nucleus); } else { // No other links, delete it. - Nucleus.Delete(synapse_nucleus); + Neuron.Delete(synapse_nucleus); } } } @@ -168,8 +179,41 @@ public class Nucleus : INucleus { return synapse; } - public virtual void UpdateState() { } + public virtual void UpdateState() { + float3 sum = new(0, 0, 0); + int n = 0; + //Applying the weight factgors + foreach (Synapse synapse in this.synapses) { + sum = sum + (synapse.weight * synapse.nucleus.outputValue); + if (lengthsq(synapse.nucleus.outputValue) != 0) + n++; + } + if (average) + sum /= n; + + // Activation function + Vector3 result; + switch (this.curvePreset) { + case CurvePresets.Linear: + result = sum; + break; + case CurvePresets.Sqrt: + result = normalize(sum) * System.MathF.Sqrt(length(sum)); + break; + case CurvePresets.Power: + result = normalize(sum) * System.MathF.Pow(length(sum), 2); + break; + case CurvePresets.Reciprocal: + result = normalize(sum) * (1 / length(sum)); + break; + default: + float activatedValue = this.curve.Evaluate(length(sum)); + result = normalize(sum) * activatedValue; + break; + } + UpdateResult(result); + } public void UpdateResult(Vector3 result) { // float d = Vector3.Distance(result, this.outputValue); // if (d < 0.5f) { diff --git a/Assets/NanoBrain/Nucleus.cs.meta b/Assets/NanoBrain/Neuron.cs.meta similarity index 100% rename from Assets/NanoBrain/Nucleus.cs.meta rename to Assets/NanoBrain/Neuron.cs.meta diff --git a/Assets/NanoBrain/NucleusArray.cs b/Assets/NanoBrain/NucleusArray.cs index 3e133f1..27f8194 100644 --- a/Assets/NanoBrain/NucleusArray.cs +++ b/Assets/NanoBrain/NucleusArray.cs @@ -38,7 +38,7 @@ public class NucleusArray { for (int i = 0; i < newLength; i++) newPerceptei[i] = this.nuclei[i]; // Delete the last perception - Nucleus.Delete(this.nuclei[newLength]); + Neuron.Delete(this.nuclei[newLength]); this.nuclei = newPerceptei; } diff --git a/Assets/NanoBrain/Receptor.cs b/Assets/NanoBrain/Receptor.cs index fe47dfb..69d3d46 100644 --- a/Assets/NanoBrain/Receptor.cs +++ b/Assets/NanoBrain/Receptor.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using UnityEngine; using Unity.Mathematics; @@ -24,15 +25,37 @@ public class Receptor : IReceptor { receiverNucleus.synapses.RemoveAll(synapse => synapse.nucleus == this); } - public bool isSleeping => false; + //public bool isSleeping => false; + private int stale = 1000; - public Vector3 localPosition; + private bool _isSleeping = false; + public bool isSleeping => _isSleeping; + + public Vector3 localPosition { + set { + this.stale = 0; + this._isSleeping = false; + this._outputValue = value; + + } + } public float distanceResolution = 0.1f; public float directionResolution = 5; - public float3 outputValue => this.localPosition; + //public float3 outputValue => this.localPosition; + private float3 _outputValue; + public float3 outputValue { + get { return this._outputValue; } + set { + this.stale = 0; + this._isSleeping = false; + this._outputValue = value; + } + } public Receptor(Cluster cluster, INucleus nucleus) { + if (cluster != null) + cluster.nuclei.Add(this); this.AddReceiver(nucleus); } @@ -100,4 +123,11 @@ public class Receptor : IReceptor { // selectedPerceptoid.name = selectedPerceptoid.baseName + " " + thingName; // selectedPerceptoid.UpdateState(); } + + public void UpdateNuclei() { + this.stale++; + this._isSleeping = this.stale > 2; + if (isSleeping) + this._outputValue = Vector3.zero; + } } \ No newline at end of file diff --git a/Assets/NanoBrain/VisualEditor/Editor/ClusterInspector.cs b/Assets/NanoBrain/VisualEditor/Editor/ClusterInspector.cs index c3744e6..f1109b9 100644 --- a/Assets/NanoBrain/VisualEditor/Editor/ClusterInspector.cs +++ b/Assets/NanoBrain/VisualEditor/Editor/ClusterInspector.cs @@ -228,7 +228,7 @@ public class ClusterInspector : Editor { // This is used to 'scale' the output value colors of the nuclei float maxValue = 0; foreach (INucleus receiver in nucleus.receivers) { - if (receiver is Neuroid neuroid) { + if (receiver is Neuron neuroid) { float value = length(neuroid.outputValue); if (value > maxValue) maxValue = value; @@ -263,29 +263,29 @@ public class ClusterInspector : Editor { int neuronCount = 0; List drawnArrays = new(); foreach (Synapse synapse in nucleus.synapses) { - if (synapse.nucleus is Neuroid neuroid) { + if (synapse.nucleus is Neuron neuroid) { if (drawnArrays.Contains(neuroid.array)) continue; drawnArrays.Add(neuroid.array); - neuronCount++; - float value = length(neuroid.outputValue); - if (value > maxValue) - maxValue = value; } + float value = length(synapse.nucleus.outputValue); + if (value > maxValue) + maxValue = value; + neuronCount++; } // Determine the spacing of the nuclei in the layer - float spacing = 400f / neuronCount; //nodeCount; + float spacing = 400f / neuronCount; float margin = 10 + spacing / 2; int row = 0; drawnArrays = new(); foreach (Synapse synapse in nucleus.synapses) { - if (synapse.nucleus is Neuroid neuroid) { - if (drawnArrays.Contains(neuroid.array)) + if (synapse.nucleus is Neuron neuron) { + if (drawnArrays.Contains(neuron.array)) continue; - drawnArrays.Add(neuroid.array); + drawnArrays.Add(neuron.array); } Vector3 pos = new(250, margin + row * spacing, 0.0f); Handles.color = Color.white; @@ -318,12 +318,12 @@ public class ClusterInspector : Editor { normal = { textColor = Color.white }, fontStyle = FontStyle.Bold, }; - if (nucleus is Nucleus perceptoid) { - if (perceptoid.array == null || perceptoid.array.nuclei == null || perceptoid.array.nuclei.Length == 0) - perceptoid.array = new NucleusArray(perceptoid); + if (nucleus is Neuron neuron) { + if (neuron.array == null || neuron.array.nuclei == null || neuron.array.nuclei.Length == 0) + neuron.array = new NucleusArray(neuron); - if (perceptoid.array.nuclei.Length > 1) { - Handles.Label(labelPosition, perceptoid.array.nuclei.Length.ToString(), style); + if (neuron.array.nuclei.Length > 1) { + Handles.Label(labelPosition, neuron.array.nuclei.Length.ToString(), style); } } @@ -396,14 +396,14 @@ public class ClusterInspector : Editor { return; this.currentNucleus.name = EditorGUILayout.TextField(this.currentNucleus.name); - if (this.currentNucleus is Nucleus neuroid) { + if (this.currentNucleus is Neuron neuroid) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Activation Curve", GUILayout.Width(150)); if (neuroid.curveMax > 0) EditorGUILayout.CurveField(neuroid.curve, Color.cyan, new Rect(0, 0, 1, neuroid.curveMax)); else EditorGUILayout.CurveField(neuroid.curve, Color.cyan, new Rect(0, neuroid.curveMax, 1, -neuroid.curveMax)); - neuroid.curvePreset = (Neuroid.CurvePresets)EditorGUILayout.EnumPopup(neuroid.curvePreset, GUILayout.Width(100)); + neuroid.curvePreset = (Neuron.CurvePresets)EditorGUILayout.EnumPopup(neuroid.curvePreset, GUILayout.Width(100)); EditorGUILayout.EndHorizontal(); if (neuroid.array == null || neuroid.array.nuclei == null || neuroid.array.nuclei.Length == 0) @@ -423,6 +423,7 @@ public class ClusterInspector : Editor { EditorGUILayout.LabelField(" "); if (this.currentNucleus.synapses.Count > 0) { + EditorGUILayout.LabelField("Synapses"); Synapse[] synapses = this.currentNucleus.synapses.ToArray(); foreach (Synapse synapse in synapses) { if (synapse.nucleus != null) { @@ -471,7 +472,7 @@ public class ClusterInspector : Editor { } protected virtual void AddInputNeuron(INucleus nucleus) { - Neuroid newNeuroid = new(this.cluster.cluster, "New neuron"); + Neuron newNeuroid = new(this.cluster.cluster, "New neuron"); newNeuroid.AddReceiver(nucleus); this.currentNucleus = newNeuroid; BuildLayers(); @@ -488,7 +489,7 @@ public class ClusterInspector : Editor { break; } } - Nucleus.Delete(nucleus); + Neuron.Delete(nucleus); BuildLayers(); } @@ -522,12 +523,12 @@ public class ClusterInspector : Editor { // Nucleus n = this.currentNucleus.brain.nuclei[selectedIndex - perceptei.Count()]; // n.AddReceiver(this.currentNucleus); // } - INucleus n = cluster.nuclei[selectedIndex]; + IReceptor n = cluster.nuclei[selectedIndex]; n.AddReceiver(this.currentNucleus); } } - protected virtual void DisconnectNucleus(Nucleus nucleus) { + protected virtual void DisconnectNucleus(Neuron nucleus) { if (this.currentNucleus.cluster == null) return; string[] names = this.currentNucleus.synapses.Select(synapse => synapse.nucleus.name).ToArray(); diff --git a/Assets/Scenes/Boids/New Cluster.asset b/Assets/Scenes/Boids/New Cluster.asset deleted file mode 100644 index 57dc073..0000000 --- a/Assets/Scenes/Boids/New Cluster.asset +++ /dev/null @@ -1,60 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!114 &11400000 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 60a957541c24c57e78018c202ebb1d9b, type: 3} - m_Name: New Cluster - m_EditorClassIdentifier: Assembly-CSharp::Cluster - nuclei: - - rid: 2243601034565648587 - references: - version: 2 - RefIds: - - rid: 2243601034565648587 - type: {class: Neuroid, ns: , asm: Assembly-CSharp} - data: - _name: Output - _synapses: [] - _receivers: [] - _array: - rid: 2243601034565648588 - _curvePreset: 0 - curve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 0 - inSlope: 0 - outSlope: 1 - tangentMode: 0 - weightedMode: 0 - inWeight: 0 - outWeight: 0 - - serializedVersion: 3 - time: 1000 - value: 1000 - inSlope: 1 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0 - outWeight: 0 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 - curveMax: 1 - average: 0 - - rid: 2243601034565648588 - type: {class: NucleusArray, ns: , asm: Assembly-CSharp} - data: - nuclei: - - rid: 2243601034565648587 - name: Output diff --git a/Assets/Scenes/Boids/New Cluster.asset.meta b/Assets/Scenes/Boids/New Cluster.asset.meta deleted file mode 100644 index db2e437..0000000 --- a/Assets/Scenes/Boids/New Cluster.asset.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 83e4ef8976534236989bcb1a9342dbf8 -NativeFormatImporter: - externalObjects: {} - mainObjectFileID: 11400000 - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scenes/Boids/NewSwarm.asset b/Assets/Scenes/Boids/NewSwarm.asset index d64c329..3f97f61 100644 --- a/Assets/Scenes/Boids/NewSwarm.asset +++ b/Assets/Scenes/Boids/NewSwarm.asset @@ -13,62 +13,18 @@ MonoBehaviour: m_Name: NewSwarm m_EditorClassIdentifier: Assembly-CSharp::Cluster nuclei: - - rid: 2243601034565648442 - - rid: 2243601034565648443 + - rid: 2243601062909444155 references: version: 2 RefIds: - - rid: 2243601034565648442 - type: {class: Neuroid, ns: , asm: Assembly-CSharp} + - rid: 2243601062909444155 + type: {class: Neuron, ns: , asm: Assembly-CSharp} data: - id: 322343360 _name: Output - _synapses: - - nucleus: - rid: 2243601034565648443 - cluster: {fileID: 0} - weight: 1 - curveMax: 1 - _receivers: [] - nucleusType: - _curvePreset: 0 - curve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 0 - inSlope: 0 - outSlope: 1 - tangentMode: 0 - weightedMode: 0 - inWeight: 0 - outWeight: 0 - - serializedVersion: 3 - time: 1000 - value: 1000 - inSlope: 1 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0 - outWeight: 0 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 - curveMax: 1 - average: 0 - inverse: 0 - exponent: 1 - - rid: 2243601034565648443 - type: {class: Neuroid, ns: , asm: Assembly-CSharp} - data: - id: -1924138416 - _name: Avoidance _synapses: [] - _receivers: - - rid: 2243601034565648442 - nucleusType: + _receivers: [] + _array: + rid: 2243601062909444156 _curvePreset: 0 curve: serializedVersion: 2 @@ -96,5 +52,9 @@ MonoBehaviour: m_RotationOrder: 4 curveMax: 1 average: 0 - inverse: 0 - exponent: 1 + - rid: 2243601062909444156 + type: {class: NucleusArray, ns: , asm: Assembly-CSharp} + data: + nuclei: + - rid: 2243601062909444155 + name: Output diff --git a/Assets/Scenes/Boids/NewSwarm.asset.meta b/Assets/Scenes/Boids/NewSwarm.asset.meta index 575238a..db2e437 100644 --- a/Assets/Scenes/Boids/NewSwarm.asset.meta +++ b/Assets/Scenes/Boids/NewSwarm.asset.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: eddc759ede59e66cd936ad6ae2c55c46 +guid: 83e4ef8976534236989bcb1a9342dbf8 NativeFormatImporter: externalObjects: {} mainObjectFileID: 11400000 diff --git a/Assets/Scenes/Boids/Prefabs/Boid.prefab b/Assets/Scenes/Boids/Prefabs/Boid.prefab index f4f2d9b..f364d47 100644 --- a/Assets/Scenes/Boids/Prefabs/Boid.prefab +++ b/Assets/Scenes/Boids/Prefabs/Boid.prefab @@ -179,4 +179,4 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 92f34a5e4027a1dc39efd8ce63cf6aba, type: 3} m_Name: m_EditorClassIdentifier: Assembly-CSharp::NanoBrainComponent - defaultBrain: {fileID: 11400000, guid: eddc759ede59e66cd936ad6ae2c55c46, type: 2} + defaultBrain: {fileID: 11400000, guid: 83e4ef8976534236989bcb1a9342dbf8, type: 2}