diff --git a/Assets/NanoBrain/Perceptoid.cs b/Assets/NanoBrain/Perceptoid.cs index 3808936..2802128 100644 --- a/Assets/NanoBrain/Perceptoid.cs +++ b/Assets/NanoBrain/Perceptoid.cs @@ -11,12 +11,16 @@ public class Perceptoid : Neuroid { [SerializeField] public int thingType; + public int thingId; public override void Rebuild(NanoBrainObj brain) { base.Rebuild(brain); - this.receptor = new Receptor() { - neuroid = this - }; + //this.receptor = new Receptor(this); + this.receptor = Perceptoid.GetReceptor(brain, thingType); + if (this.receptor == null) + this.receptor = new Receptor(this); + else + this.receptor.perceptei.Add(this); } public override void Deserialize(Nucleus nucleus) { @@ -57,9 +61,7 @@ public class Perceptoid : Neuroid { this.nucleusType = nameof(Perceptoid); this.name = name; - this.receptor = new Receptor { - neuroid = this - }; + this.receptor = new Receptor(this); } public Perceptoid(NanoBrainObj brain, int thingType, string name = "sensor") : base(name) { @@ -73,8 +75,7 @@ public class Perceptoid : Neuroid { this.nucleusType = nameof(Perceptoid); this.name = name; this.thingType = thingType; - this.receptor = new Receptor { - neuroid = this, + this.receptor = new Receptor(this) { thingType = thingType }; this.velocityNeuroid = new(brain, name + ": velocity"); @@ -119,6 +120,31 @@ public class Perceptoid : Neuroid { this.stale = 0; } + public void UpdateState(int thingId, Vector3 receptorValue) { + this.thingId = thingId; + Vector3 result = receptorValue; + foreach (Synapse synapse in this.synapses) { + Nucleus nucleus = synapse.nucleus; + float weight = synapse.weight; + Vector3 direction = nucleus.outputValue.normalized; + float magnitude = nucleus.outputValue.magnitude; + + magnitude = weight * Mathf.Pow(magnitude, exponent); + if (inverse) + magnitude = 1 / magnitude; + result += direction * magnitude; + } + if (average && this.synapses.Count > 0) + result /= this.synapses.Count + 1; + + this.outputValue = result; + foreach (Receiver receiver in this.receivers) + if (receiver.nucleus is Neuroid neuroid) + neuroid.SetInput(this); + this.stale = 0; + } + + public static Perceptoid GetPerception(NanoBrainObj brain, int thingType = 0) { foreach (Nucleus nucleus in brain.nuclei) { if (nucleus is Perceptoid perceptoid && (thingType == 0 || perceptoid.thingType == thingType)) diff --git a/Assets/NanoBrain/Receptor.cs b/Assets/NanoBrain/Receptor.cs index 7c9ccc7..92b7d67 100644 --- a/Assets/NanoBrain/Receptor.cs +++ b/Assets/NanoBrain/Receptor.cs @@ -4,13 +4,18 @@ using UnityEngine; public class Receptor { - public Neuroid neuroid; - public List perceptoids; + private Neuroid neuroid; + public List perceptei = new(); public int thingId; public int thingType; public Vector3 localPosition; + public Receptor(Perceptoid perceptoid) { + this.neuroid = perceptoid; + this.perceptei.Add(perceptoid); + } + /// /// Local position of the thing /// @@ -27,7 +32,29 @@ public class Receptor { public virtual void ProcessStimulus(int thingId, Vector3 localPosition) { this.thingId = thingId; this.localPosition = localPosition; - neuroid.UpdateState(); - //perceptoids[0].UpdateState(); + ///neuroid.UpdateState(); + + Perceptoid selectedPerceptoid = null; + foreach (Perceptoid perceptoid in this.perceptei) { + if (perceptoid.thingId == this.thingId) { + selectedPerceptoid = perceptoid; + break; + } + else if (perceptoid.isSleeping) + selectedPerceptoid = perceptoid; + else if (selectedPerceptoid == null) { + selectedPerceptoid = perceptoid; + } + else if (selectedPerceptoid.isSleeping == false) { + if (perceptoid.receptor.position.magnitude < selectedPerceptoid.receptor.position.magnitude) + selectedPerceptoid = perceptoid; + } + } + if (selectedPerceptoid == null) { + Debug.Log("No perceptoid selected, stimulus is ignored"); + return; + } + Debug.Log($"Stimulus {thingId} {selectedPerceptoid.thingId}"); + selectedPerceptoid.UpdateState(this.thingId, this.localPosition); } } \ No newline at end of file diff --git a/Assets/NanoBrain/SensoryNeuroid.cs b/Assets/NanoBrain/SensoryNeuroid.cs index 6bfb5df..c716cca 100644 --- a/Assets/NanoBrain/SensoryNeuroid.cs +++ b/Assets/NanoBrain/SensoryNeuroid.cs @@ -40,11 +40,9 @@ public class SensoryNeuroid : Neuroid { public SensoryNeuroid(NanoBrainObj brain, int thingId, string name = "sensor") : base(brain, name) { this.name = name + ": position"; - this.receptor = new Receptor { - neuroid = this, - //perceptoids[0] = this, - thingType = thingId - }; + // this.receptor = new Receptor(this) { + // thingType = thingId + // }; this.velocityNeuroid = new(brain, name + ": velocity"); // The velocity neuroid received position data from this this.AddReceiver(velocityNeuroid); diff --git a/Assets/NanoBrain/VisualEditor/Editor/NanoBrainInspector.cs b/Assets/NanoBrain/VisualEditor/Editor/NanoBrainInspector.cs index e944386..f86e0f3 100644 --- a/Assets/NanoBrain/VisualEditor/Editor/NanoBrainInspector.cs +++ b/Assets/NanoBrain/VisualEditor/Editor/NanoBrainInspector.cs @@ -237,38 +237,6 @@ public class NanoBrainInspector : Editor { DrawNucleus(this.currentNucleus, position, this.currentNucleus.outputValue.magnitude, 20); } - private void DrawNucleus(Nucleus nucleus, Vector3 position, float maxValue, float size) { - if (nucleus.isSleeping) - Handles.color = Color.darkRed; - else { - float brightness = nucleus.outputValue.magnitude / maxValue; - Handles.color = new Color(brightness, brightness, brightness); - } - 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) { - alignment = TextAnchor.UpperCenter, - normal = { textColor = Color.white }, - fontStyle = FontStyle.Bold - }; - Handles.Label(labelPos, nucleus.name, style); - - Rect neuronRect = new(position.x - size, position.y - size, size * 2, size * 2); - int id = GUIUtility.GetControlID(FocusType.Passive); - Event e = Event.current; - EventType et = e.GetTypeForControl(id); - if (e != null && neuronRect.Contains(e.mousePosition)) { - // Process Hover - HandleMouseHover(nucleus, neuronRect); - // Process click - if (e.type == EventType.MouseDown && e.button == 0) { - // Consume the event so the scene doesn't also handle it - e.Use(); - HandleClicked(nucleus); - } - } - } - private void DrawReceivers(Nucleus nucleus, Vector3 parentPos, float size) { int nodeCount = nucleus.receivers.Count; @@ -290,6 +258,8 @@ public class NanoBrainInspector : Editor { int row = 0; foreach (Receiver receiver in nucleus.receivers) { Nucleus receiverNucleus = receiver.nucleus; + if (receiverNucleus == null) + continue; Vector3 pos = new(100, margin + row * spacing, 0.0f); Handles.color = Color.white; @@ -331,104 +301,68 @@ public class NanoBrainInspector : Editor { } } - /* - private void DrawLayer(NeuroidLayer layer) { - int nodeCount = layer.neuroids.Count; - - // Determine the maximum value in this layer - // This is used to 'scale' the output value colors of the nuclei - float maxValue = 0; - foreach (Nucleus nucleus in layer.neuroids) { - if (nucleus is Neuroid neuroid) { - float value = neuroid.outputValue.magnitude; - if (value > maxValue) - maxValue = value; - } + private void DrawNucleus(Nucleus nucleus, Vector3 position, float maxValue, float size) { + if (nucleus.isSleeping) + Handles.color = Color.darkRed; + else { + float brightness = nucleus.outputValue.magnitude / maxValue; + Handles.color = new Color(brightness, brightness, brightness); } + 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) { + alignment = TextAnchor.UpperCenter, + normal = { textColor = Color.white }, + fontStyle = FontStyle.Bold + }; + Handles.Label(labelPos, nucleus.name, style); - // Determine the spacing of the nuclei in the layer - float spacing = 400f / nodeCount; - float margin = 10 + spacing / 2; - - foreach (Nucleus layerNucleus in layer.neuroids) { - Vector2Int layerNeuroidPos = this.neuroidPositions[layerNucleus]; - Vector3 parentPos = new(100 + layerNeuroidPos.x * 100, margin + layerNeuroidPos.y * spacing, 0.1f); - - float inputSpacing = 400f / layerNucleus.synapses.Count; - float inputMargin = 10 + inputSpacing / 2; - int minStale = 10000; - foreach (Synapse synapse in layerNucleus.synapses) { - Nucleus nucleus = synapse.nucleus; - if (nucleus != null) { - if (this.neuroidPositions.ContainsKey(nucleus)) { - Vector2Int inputNeuroidPos = this.neuroidPositions[nucleus]; - if (inputNeuroidPos.x == layerNeuroidPos.x + 1) { - Vector3 pos = new(100 + inputNeuroidPos.x * 100, inputMargin + inputNeuroidPos.y * inputSpacing, 0.0f); - - Handles.color = Color.white; - Handles.DrawLine(parentPos, pos); - } - } - if (nucleus is Neuroid neuroid && neuroid.stale < minStale) - minStale = neuroid.stale; - } - } - - - float size = 20; - if (layerNucleus == this.currentNucleus) { - Handles.color = Color.white; - Handles.DrawSolidDisc(parentPos, Vector3.forward, size + 2); - } - DrawNucleus(layerNucleus, parentPos, maxValue, size); - // if (layerNucleus.isSleeping) - // Handles.color = Color.darkRed; - // else { - // float brightness = layerNucleus.outputValue.magnitude / maxValue; - // Handles.color = new Color(brightness, brightness, brightness); - // } - // Handles.DrawSolidDisc(parentPos, Vector3.forward, size); - // Vector3 labelPos = parentPos - Vector3.down * (size + 0.2f); // below disc along up axis - // GUIStyle style = new GUIStyle(EditorStyles.label) { - // alignment = TextAnchor.UpperCenter, - // normal = { textColor = Color.white }, - // fontStyle = FontStyle.Bold - // }; - // Handles.Label(labelPos, layerNucleus.name, style); - - Rect neuronRect = new(parentPos.x - size, parentPos.y - size, size * 2, size * 2); - int id = GUIUtility.GetControlID(FocusType.Passive); - Event e = Event.current; - EventType et = e.GetTypeForControl(id); - if (e != null && neuronRect.Contains(e.mousePosition)) { - // Process Hover - HandleMouseHover(layerNucleus, neuronRect); - // Process click - if (e.type == EventType.MouseDown && e.button == 0) { - // Consume the event so the scene doesn't also handle it - e.Use(); - HandleClicked(layerNucleus); - } + Rect neuronRect = new(position.x - size, position.y - size, size * 2, size * 2); + int id = GUIUtility.GetControlID(FocusType.Passive); + Event e = Event.current; + EventType et = e.GetTypeForControl(id); + if (e != null && neuronRect.Contains(e.mousePosition)) { + // Process Hover + HandleMouseHover(nucleus, neuronRect); + // Process click + if (e.type == EventType.MouseDown && e.button == 0) { + // Consume the event so the scene doesn't also handle it + e.Use(); + HandleClicked(nucleus); } } } - */ - private void HandleMouseHover(Nucleus neuroid, Rect rect) { + private void HandleMouseHover(Nucleus nucleus, Rect rect) { GUIContent tooltip; - if (neuroid is SensoryNeuroid sensoryNeuroid) { + if (nucleus is SensoryNeuroid sensoryNeuroid) { tooltip = new( $"{sensoryNeuroid.name}" + $"\nThing {sensoryNeuroid.receptor.thingType}" + - $"\nValue: {neuroid.outputValue}" + - $"\nStale: {neuroid.stale}"); + $"\nValue: {nucleus.outputValue}" + + $"\nStale: {nucleus.stale}"); + } + else if (nucleus is Perceptoid perceptoid) { + if (perceptoid.receptor != null) { + tooltip = new( + $"{perceptoid.name}" + + $"\nType {perceptoid.receptor.thingType}" + + $" Thing {perceptoid.thingId}" + + $"\nValue: {nucleus.outputValue}"); + } + else { + tooltip = new( + $"{perceptoid.name}" + + $"\nThing {perceptoid.thingId}" + + $"\nValue: {nucleus.outputValue}"); + } } else { tooltip = new( - $"{neuroid.name}" + - $"\nsynapse count {neuroid.synapses.Count}" + - $"\nValue: {neuroid.outputValue}" + - $"\nStale: {neuroid.stale}"); + $"{nucleus.name}" + + $"\nsynapse count {nucleus.synapses.Count}" + + $"\nValue: {nucleus.outputValue}" + + $"\nStale: {nucleus.stale}"); } Vector2 mousePosition = Event.current.mousePosition; @@ -445,7 +379,6 @@ public class NanoBrainInspector : Editor { BuildLayers(); } - void DrawInspector(VisualElement inspectorContainer) { if (inspectorContainer == null) return; @@ -518,9 +451,14 @@ public class NanoBrainInspector : Editor { } protected virtual void DeleteNeuron(Nucleus nucleus) { - this.currentNucleus = nucleus.receivers[0].nucleus; + this.currentNucleus = nucleus.brain.root; + foreach (Receiver receiver in nucleus.receivers) { + if (receiver.nucleus != null) { + this.currentNucleus = receiver.nucleus; + break; + } + } Nucleus.Delete(nucleus); - //Rebuild(inspectorContainer); BuildLayers(); } diff --git a/Assets/Scenes/Boids/Boids.unity b/Assets/Scenes/Boids/Boids.unity index 5515d90..f0e9d7c 100644 --- a/Assets/Scenes/Boids/Boids.unity +++ b/Assets/Scenes/Boids/Boids.unity @@ -393,7 +393,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: ec888ca5333d45a438f9f417fa5ce135, type: 3} m_Name: m_EditorClassIdentifier: Assembly-CSharp::SwarmSpawn - count: 3 + count: 30 boidPrefab: {fileID: 8702527964058765413, guid: f9c706268554ce449a8773675b2864b8, type: 3} spawnAreaSize: {x: 0.5, y: 0.5, z: 0.5} minDelay: 0.05 diff --git a/Assets/Scenes/Boids/RoamingBrain.asset b/Assets/Scenes/Boids/RoamingBrain.asset index 0d13ed5..02830cf 100644 --- a/Assets/Scenes/Boids/RoamingBrain.asset +++ b/Assets/Scenes/Boids/RoamingBrain.asset @@ -49,4 +49,5 @@ MonoBehaviour: inverse: 0 exponent: 1 thingType: 1 + thingId: 0 rootId: -1707533328 diff --git a/Assets/Scenes/Boids/SwarmingBrain.asset b/Assets/Scenes/Boids/SwarmingBrain.asset index 4228d29..ab81e1e 100644 --- a/Assets/Scenes/Boids/SwarmingBrain.asset +++ b/Assets/Scenes/Boids/SwarmingBrain.asset @@ -23,7 +23,7 @@ MonoBehaviour: - nucleusId: -112538112 weight: 1 - nucleusId: 1938577052 - weight: 1 + weight: 10 receivers: [] nucleusType: average: 0 @@ -43,9 +43,9 @@ MonoBehaviour: - id: 1938577052 _name: Cohesion synapses: - - nucleusId: -1408496896 + - nucleusId: -1420275136 weight: 1 - - nucleusId: -133566816 + - nucleusId: -1266532688 weight: 1 receivers: - nucleusId: -1707533328 @@ -64,7 +64,8 @@ MonoBehaviour: inverse: 0 exponent: 1 thingType: 1 - - id: -1408496896 + thingId: 0 + - id: -1420275136 _name: Boid1 synapses: [] receivers: @@ -74,15 +75,16 @@ MonoBehaviour: inverse: 0 exponent: 1 thingType: 2 - - id: -133566816 + thingId: 0 + - id: -1266532688 _name: Boid2 synapses: [] receivers: - - nucleusId: 27651644 - nucleusId: 1938577052 nucleusType: Perceptoid average: 0 inverse: 0 exponent: 1 thingType: 2 + thingId: 0 rootId: -1707533328