Simplified synapses, NanoBrain component
This commit is contained in:
parent
9a6ae0e071
commit
145e033d4c
@ -48,6 +48,7 @@
|
|||||||
<Analyzer Include="/home/pascal/Unity/Hub/Editor/6000.2.13f1/Editor/Data/Tools/Unity.SourceGenerators/Unity.UIToolkit.SourceGenerator.dll" />
|
<Analyzer Include="/home/pascal/Unity/Hub/Editor/6000.2.13f1/Editor/Data/Tools/Unity.SourceGenerators/Unity.UIToolkit.SourceGenerator.dll" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="Assets/NanoBrain/Editor/NanoBrain_Editor.cs" />
|
||||||
<Compile Include="Assets/NanoBrain/Editor/NeuroidWindow.cs" />
|
<Compile Include="Assets/NanoBrain/Editor/NeuroidWindow.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -50,6 +50,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Assets/Scenes/Boids/Scripts/SwarmSpawner.cs" />
|
<Compile Include="Assets/Scenes/Boids/Scripts/SwarmSpawner.cs" />
|
||||||
<Compile Include="Assets/NanoBrain/NeuroidBehaviour.cs" />
|
<Compile Include="Assets/NanoBrain/NeuroidBehaviour.cs" />
|
||||||
|
<Compile Include="Assets/NanoBrain/NanoBrain.cs" />
|
||||||
<Compile Include="Assets/NanoBrain/SensoryNeuroid.cs" />
|
<Compile Include="Assets/NanoBrain/SensoryNeuroid.cs" />
|
||||||
<Compile Include="Assets/Scenes/Boids/Scripts/SwarmControl.cs" />
|
<Compile Include="Assets/Scenes/Boids/Scripts/SwarmControl.cs" />
|
||||||
<Compile Include="Assets/Scenes/Boids/Scripts/RoamingNucleus.cs" />
|
<Compile Include="Assets/Scenes/Boids/Scripts/RoamingNucleus.cs" />
|
||||||
|
|||||||
203
Assets/NanoBrain/Editor/NanoBrain_Editor.cs
Normal file
203
Assets/NanoBrain/Editor/NanoBrain_Editor.cs
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
[CustomEditor(typeof(NanoBrain))]
|
||||||
|
public class NanoBrain_Editor : Editor {
|
||||||
|
private Nucleus currentNucleus;
|
||||||
|
private List<NeuroidLayer> layers = new();
|
||||||
|
private Dictionary<Nucleus, Vector2Int> neuroidPositions = new();
|
||||||
|
|
||||||
|
#region Start
|
||||||
|
|
||||||
|
private void OnEnable() {
|
||||||
|
SelectNeuron();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SelectNeuron() {
|
||||||
|
GameObject selectedObject = ((NanoBrain)target).gameObject;
|
||||||
|
if (!selectedObject.TryGetComponent(out Boid boid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Neuroid neuroid = boid.totalForce;
|
||||||
|
this.currentNucleus = neuroid;
|
||||||
|
|
||||||
|
BuildLayers();
|
||||||
|
Debug.Log($"Layercount = {this.layers.Count}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Start
|
||||||
|
|
||||||
|
#region Update
|
||||||
|
|
||||||
|
public override void OnInspectorGUI() {
|
||||||
|
DrawGraph();
|
||||||
|
|
||||||
|
DrawDefaultInspector();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BuildLayers() {
|
||||||
|
// A temporary list to track what's been added to layers
|
||||||
|
this.layers = new();
|
||||||
|
int layerIx = 0;
|
||||||
|
|
||||||
|
Nucleus selectedNucleus = this.currentNucleus;
|
||||||
|
if (selectedNucleus == null)
|
||||||
|
return;
|
||||||
|
NeuroidLayer currentLayer = new() { ix = layerIx };
|
||||||
|
|
||||||
|
foreach (Neuroid outputNeuroid in selectedNucleus.outputNeuroids) {
|
||||||
|
if (outputNeuroid != null) {
|
||||||
|
AddToLayer(currentLayer, outputNeuroid);
|
||||||
|
Debug.Log($"layer {layerIx} nucleus {outputNeuroid.name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentLayer.neuroids.Count > 0) {
|
||||||
|
this.layers.Add(currentLayer);
|
||||||
|
layerIx++;
|
||||||
|
currentLayer = new() { ix = layerIx };
|
||||||
|
}
|
||||||
|
|
||||||
|
AddToLayer(currentLayer, selectedNucleus);
|
||||||
|
this.layers.Add(currentLayer);
|
||||||
|
Debug.Log($"layer {layerIx} nucleus {selectedNucleus.name}");
|
||||||
|
|
||||||
|
layerIx++;
|
||||||
|
currentLayer = new() { ix = layerIx };
|
||||||
|
|
||||||
|
foreach (Nucleus input in selectedNucleus.synapses.Keys) {
|
||||||
|
AddToLayer(currentLayer, input);
|
||||||
|
Debug.Log($"layer {layerIx} nucleus {input.name}");
|
||||||
|
}
|
||||||
|
if (currentLayer.neuroids.Count > 0) {
|
||||||
|
this.layers.Add(currentLayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddToLayer(NeuroidLayer layer, Nucleus nucleus) {
|
||||||
|
layer.neuroids.Add(nucleus);
|
||||||
|
nucleus.layerIx = layer.ix;
|
||||||
|
// Store its position
|
||||||
|
Vector2Int neuroidPosition = new(layer.ix, layer.neuroids.Count - 1);
|
||||||
|
neuroidPositions[nucleus] = neuroidPosition;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawGraph() {
|
||||||
|
if (currentNucleus == null)
|
||||||
|
return;
|
||||||
|
Rect outer = EditorGUILayout.GetControlRect(false, 400);
|
||||||
|
GUI.BeginGroup(outer);
|
||||||
|
foreach (NeuroidLayer layer in layers)
|
||||||
|
DrawLayer(layer);
|
||||||
|
GUI.EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawLayer(NeuroidLayer layer) {
|
||||||
|
int nodeCount = layer.neuroids.Count;
|
||||||
|
float maxValue = 0;
|
||||||
|
foreach (Nucleus nucleus in layer.neuroids) {
|
||||||
|
if (nucleus is Neuroid neuroid) {
|
||||||
|
float value = neuroid.outputValue.magnitude;
|
||||||
|
if (value > maxValue)
|
||||||
|
maxValue = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
float spacing = 400f / nodeCount;
|
||||||
|
float margin = 10 + spacing / 2;
|
||||||
|
foreach (Nucleus layerNucleus in layer.neuroids) {
|
||||||
|
if (layerNucleus is Neuroid layerNeuroid) {
|
||||||
|
Vector2Int layerNeuroidPos = this.neuroidPositions[layerNeuroid];
|
||||||
|
Vector3 parentPos = new(100 + layerNeuroidPos.x * 100, margin + layerNeuroidPos.y * spacing, 0.1f);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
float inputSpacing = 400f / layerNeuroid.synapses.Count;
|
||||||
|
float inputMargin = 10 + inputSpacing / 2;
|
||||||
|
// foreach (Synapse synapse in layerNeuroid.synapses.Values) {
|
||||||
|
// if (synapse.neuroid != null) {
|
||||||
|
// if (this.neuroidPositions.ContainsKey(synapse.neuroid)) {
|
||||||
|
|
||||||
|
// Vector2Int inputNeuroidPos = this.neuroidPositions[synapse.neuroid];
|
||||||
|
foreach ((Nucleus neuroid, Synapse synapse) in layerNeuroid.synapses) {
|
||||||
|
if (neuroid != null) {
|
||||||
|
if (this.neuroidPositions.ContainsKey(neuroid)) {
|
||||||
|
Vector2Int inputNeuroidPos = this.neuroidPositions[neuroid];
|
||||||
|
if (inputNeuroidPos.x == layerNeuroidPos.x + 1) {
|
||||||
|
Vector3 pos = new(100 + inputNeuroidPos.x * 100, inputMargin + inputNeuroidPos.y * inputSpacing, 0.0f);
|
||||||
|
|
||||||
|
float brightness = synapse.weight / 10.0f;
|
||||||
|
Handles.color = new Color(brightness, brightness, brightness);
|
||||||
|
Handles.DrawLine(parentPos, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float size = 20;
|
||||||
|
if (layerNeuroid.IsStale())
|
||||||
|
Handles.color = Color.black;
|
||||||
|
else {
|
||||||
|
float brightness = layerNeuroid.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, layerNeuroid.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)) {
|
||||||
|
HandleMouseHover(layerNeuroid, neuronRect);
|
||||||
|
// Process click
|
||||||
|
Debug.Log($"{et}");
|
||||||
|
if (et == EventType.MouseDown && e.button == 0) {
|
||||||
|
// Consume the event so the scene doesn't also handle it
|
||||||
|
e.Use();
|
||||||
|
HandleDiscClicked(layerNeuroid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleMouseHover(Neuroid neuroid, Rect rect) {
|
||||||
|
GUIContent tooltip;
|
||||||
|
if (neuroid is SensoryNeuroid sensoryNeuroid) {
|
||||||
|
tooltip = new(
|
||||||
|
$"{sensoryNeuroid.name}" +
|
||||||
|
$"\nThing {sensoryNeuroid.receptor.thingId}" +
|
||||||
|
$"\nValue: {neuroid.outputValue}" +
|
||||||
|
$"\nStale: {neuroid.stale}");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tooltip = new(
|
||||||
|
$"{neuroid.name}" +
|
||||||
|
$"\nsynapse count {neuroid.synapses.Count}" +
|
||||||
|
$"\nValue: {neuroid.outputValue}" +
|
||||||
|
$"\nStale: {neuroid.stale}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2 mousePosition = Event.current.mousePosition;
|
||||||
|
|
||||||
|
// Display tooltip with some offset
|
||||||
|
Vector2 tooltipSize = GUI.skin.box.CalcSize(tooltip);
|
||||||
|
Rect tooltipRect = new Rect(mousePosition.x + 10, mousePosition.y + 10, tooltipSize.x, tooltipSize.y);
|
||||||
|
|
||||||
|
GUI.Box(tooltipRect, tooltip);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleDiscClicked(Nucleus nucleus) {
|
||||||
|
this.currentNucleus = nucleus;
|
||||||
|
BuildLayers();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Update
|
||||||
|
}
|
||||||
2
Assets/NanoBrain/Editor/NanoBrain_Editor.cs.meta
Normal file
2
Assets/NanoBrain/Editor/NanoBrain_Editor.cs.meta
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2299b68d073cc5c31915f591deb79ddc
|
||||||
@ -36,6 +36,8 @@ public class GraphEditorWindow : EditorWindow {
|
|||||||
int layerIx = 0;
|
int layerIx = 0;
|
||||||
|
|
||||||
Nucleus selectedNucleus = this.currentNucleus;
|
Nucleus selectedNucleus = this.currentNucleus;
|
||||||
|
if (selectedNucleus == null)
|
||||||
|
return;
|
||||||
NeuroidLayer currentLayer = new() { ix = layerIx };
|
NeuroidLayer currentLayer = new() { ix = layerIx };
|
||||||
|
|
||||||
foreach (Neuroid outputNeuroid in selectedNucleus.outputNeuroids) {
|
foreach (Neuroid outputNeuroid in selectedNucleus.outputNeuroids) {
|
||||||
@ -58,9 +60,10 @@ public class GraphEditorWindow : EditorWindow {
|
|||||||
currentLayer = new() { ix = layerIx };
|
currentLayer = new() { ix = layerIx };
|
||||||
|
|
||||||
int six = 0;
|
int six = 0;
|
||||||
foreach (Synapse synapse in selectedNucleus.synapses.Values) {
|
// foreach (Synapse synapse in selectedNucleus.synapses.Values) {
|
||||||
Debug.Log($"Synapse {six}");
|
// Debug.Log($"Synapse {six}");
|
||||||
Nucleus input = synapse.neuroid;
|
// Nucleus input = synapse.neuroid;
|
||||||
|
foreach ((Nucleus input, Synapse synapse) in selectedNucleus.synapses) {
|
||||||
if (input != null) {
|
if (input != null) {
|
||||||
AddToLayer(currentLayer, input);
|
AddToLayer(currentLayer, input);
|
||||||
Debug.Log($"layer {layerIx} nucleus {input.name}");
|
Debug.Log($"layer {layerIx} nucleus {input.name}");
|
||||||
@ -172,11 +175,15 @@ public class GraphEditorWindow : EditorWindow {
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
float inputSpacing = 400f / layerNeuroid.synapses.Count;
|
float inputSpacing = 400f / layerNeuroid.synapses.Count;
|
||||||
float inputMargin = 100 + inputSpacing / 2;
|
float inputMargin = 100 + inputSpacing / 2;
|
||||||
foreach (Synapse synapse in layerNeuroid.synapses.Values) {
|
// foreach (Synapse synapse in layerNeuroid.synapses.Values) {
|
||||||
if (synapse.neuroid != null) {
|
// if (synapse.neuroid != null) {
|
||||||
if (this.neuroidPositions.ContainsKey(synapse.neuroid)) {
|
// if (this.neuroidPositions.ContainsKey(synapse.neuroid)) {
|
||||||
|
|
||||||
Vector2Int inputNeuroidPos = this.neuroidPositions[synapse.neuroid];
|
// Vector2Int inputNeuroidPos = this.neuroidPositions[synapse.neuroid];
|
||||||
|
foreach ((Nucleus neuroid, Synapse synapse) in layerNeuroid.synapses) {
|
||||||
|
if (neuroid != null) {
|
||||||
|
if (this.neuroidPositions.ContainsKey(neuroid)) {
|
||||||
|
Vector2Int inputNeuroidPos = this.neuroidPositions[neuroid];
|
||||||
if (inputNeuroidPos.x == layerNeuroidPos.x + 1) {
|
if (inputNeuroidPos.x == layerNeuroidPos.x + 1) {
|
||||||
Vector3 pos = new(100 + inputNeuroidPos.x * 100, inputMargin + inputNeuroidPos.y * inputSpacing, 0.0f);
|
Vector3 pos = new(100 + inputNeuroidPos.x * 100, inputMargin + inputNeuroidPos.y * inputSpacing, 0.0f);
|
||||||
|
|
||||||
|
|||||||
19
Assets/NanoBrain/NanoBrain.cs
Normal file
19
Assets/NanoBrain/NanoBrain.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class NanoBrain : MonoBehaviour {
|
||||||
|
public List<Neuroid> neuroids = new();
|
||||||
|
|
||||||
|
public Neuroid AddNeuron(string name) {
|
||||||
|
Neuroid neuroid = new(this, name);
|
||||||
|
return neuroid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update() {
|
||||||
|
foreach (Neuroid neuroid in neuroids) {
|
||||||
|
neuroid.stale++;
|
||||||
|
if (neuroid.IsStale())
|
||||||
|
neuroid.outputValue = Vector3.zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/NanoBrain/NanoBrain.cs.meta
Normal file
2
Assets/NanoBrain/NanoBrain.cs.meta
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 74e1478743ac3bc078cbe8501c287e98
|
||||||
@ -3,32 +3,30 @@ using UnityEngine;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
public class Synapse {
|
public class Synapse {
|
||||||
public Synapse(Nucleus neuroid, Vector3 value, float weight) {
|
public Synapse(Nucleus neuroid, float weight = 1.0f) {
|
||||||
this.neuroid = neuroid;
|
//this.neuroid = neuroid;
|
||||||
this.value = value;
|
|
||||||
this.weight = weight;
|
this.weight = weight;
|
||||||
}
|
}
|
||||||
public Nucleus neuroid;
|
//public Nucleus neuroid;
|
||||||
public Vector3 value;
|
|
||||||
public float weight;
|
public float weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class NeuroidNetwork {
|
// public class NeuroidNetwork {
|
||||||
public List<Neuroid> neuroids = new();
|
// public List<Neuroid> neuroids = new();
|
||||||
|
|
||||||
public Neuroid AddNeuron(string name) {
|
// public Neuroid AddNeuron(string name) {
|
||||||
Neuroid neuroid = new(this, name);
|
// Neuroid neuroid = new(this, name);
|
||||||
return neuroid;
|
// return neuroid;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public void Update() {
|
// public void Update() {
|
||||||
foreach (Neuroid neuroid in neuroids) {
|
// foreach (Neuroid neuroid in neuroids) {
|
||||||
neuroid.stale++;
|
// neuroid.stale++;
|
||||||
if (neuroid.IsStale())
|
// if (neuroid.IsStale())
|
||||||
neuroid.outputValue = Vector3.zero;
|
// neuroid.outputValue = Vector3.zero;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
public class Neuroid : Nucleus {
|
public class Neuroid : Nucleus {
|
||||||
public int stale = 0;
|
public int stale = 0;
|
||||||
@ -40,9 +38,11 @@ public class Neuroid : Nucleus {
|
|||||||
public bool inverse = false;
|
public bool inverse = false;
|
||||||
public float exponent = 1.0f;
|
public float exponent = 1.0f;
|
||||||
|
|
||||||
public NeuroidNetwork net;
|
//public NeuroidNetwork net;
|
||||||
|
public NanoBrain net;
|
||||||
|
|
||||||
public Neuroid(NeuroidNetwork net, string name) : base(name) {
|
// public Neuroid(NeuroidNetwork net, string name) : base(name) {
|
||||||
|
public Neuroid(NanoBrain net, string name) : base(name) {
|
||||||
this.net = net;
|
this.net = net;
|
||||||
if (this.net != null)
|
if (this.net != null)
|
||||||
this.net.neuroids.Add(this);
|
this.net.neuroids.Add(this);
|
||||||
@ -52,7 +52,7 @@ public class Neuroid : Nucleus {
|
|||||||
|
|
||||||
public void AddSynapse(Neuroid input) {
|
public void AddSynapse(Neuroid input) {
|
||||||
input.AddReceiver(this);
|
input.AddReceiver(this);
|
||||||
this.synapses[input] = new(input, Vector3.zero, 1.0f);
|
this.synapses[input] = new(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
// public void AddReceiver(Neuroid receiver) {
|
// public void AddReceiver(Neuroid receiver) {
|
||||||
@ -69,33 +69,28 @@ public class Neuroid : Nucleus {
|
|||||||
this.synapses[input].weight = weight;
|
this.synapses[input].weight = weight;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.synapses[input] = new(input, Vector3.zero, weight);
|
this.synapses[input] = new(input, weight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetInputFrom(Neuroid input, float weight = 1.0f) {
|
public void GetInputFrom(Neuroid input, float weight = 1.0f) {
|
||||||
input.AddReceiver(this);
|
input.AddReceiver(this);
|
||||||
this.synapses[input] = new(input, Vector3.zero, weight);
|
this.synapses[input] = new(input, weight);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetInput(Neuroid input, Vector3 value) {
|
public void SetInput(Neuroid input) {
|
||||||
if (this.synapses.ContainsKey(input)) {
|
if (this.synapses.ContainsKey(input) == false)
|
||||||
Synapse synapse = this.synapses[input];
|
this.synapses[input] = new(input);
|
||||||
synapse.value = value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
this.synapses[input] = new(null, value, 1.0f);
|
|
||||||
UpdateState();
|
UpdateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetInput(Neuroid input, Vector3 value, float weight) {
|
public void SetInput(Neuroid input, float weight) {
|
||||||
if (this.synapses.ContainsKey(input)) {
|
if (this.synapses.ContainsKey(input)) {
|
||||||
Synapse synapse = this.synapses[input];
|
Synapse synapse = this.synapses[input];
|
||||||
synapse.value = value;
|
|
||||||
synapse.weight = weight;
|
synapse.weight = weight;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.synapses[input] = new(null, value, weight);
|
this.synapses[input] = new(input, weight);
|
||||||
UpdateState();
|
UpdateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,38 +100,28 @@ public class Neuroid : Nucleus {
|
|||||||
// In case this was the last synapse, we reset the output because in this case no updates from synapses will follow.
|
// In case this was the last synapse, we reset the output because in this case no updates from synapses will follow.
|
||||||
this.outputValue = Vector3.zero;
|
this.outputValue = Vector3.zero;
|
||||||
foreach (Neuroid neuroid in this.outputNeuroids)
|
foreach (Neuroid neuroid in this.outputNeuroids)
|
||||||
neuroid.SetInput(this, this.outputValue);
|
neuroid.SetInput(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// public readonly Dictionary<int, Neuroid> fakeNeuroids = new();
|
public virtual void UpdateState() {
|
||||||
// public void SetInput(int thingId, Vector3 value, float weight, NeuroidNetwork net) {
|
|
||||||
// if (fakeNeuroids.ContainsKey(thingId)) {
|
|
||||||
// Neuroid fakeInput = fakeNeuroids[thingId];
|
|
||||||
// Synapse synapse = this.synapses[fakeInput];
|
|
||||||
// synapse.value = value;
|
|
||||||
// synapse.weight = weight;
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// fakeNeuroids[thingId] = new(net);
|
|
||||||
// this.synapses[fakeNeuroids[thingId]] = new(null, value, weight);
|
|
||||||
// }
|
|
||||||
// UpdateState();
|
|
||||||
// }
|
|
||||||
|
|
||||||
protected virtual void UpdateState() {
|
|
||||||
Vector3 result = Vector3.zero;
|
Vector3 result = Vector3.zero;
|
||||||
foreach (Synapse synapse in this.synapses.Values) {
|
foreach ((Nucleus nucleus, Synapse synapse) in this.synapses) {
|
||||||
|
// foreach (Synapse synapse in this.synapses.Values) {
|
||||||
// if (synapse.neuroid == null)
|
// if (synapse.neuroid == null)
|
||||||
// continue;
|
// Debug.LogWarning(" disconnected synapse");
|
||||||
Vector3 direction = synapse.value.normalized;
|
// if (synapse.value != synapse.neuroid.outputValue)
|
||||||
float magnitude = synapse.value.magnitude;
|
// Debug.LogWarning("synapse value error");
|
||||||
|
// Vector3 direction = synapse.value.normalized;
|
||||||
|
// float magnitude = synapse.value.magnitude;
|
||||||
|
|
||||||
// Vector3 direction = synapse.neuroid.outputValue.normalized;
|
// Vector3 direction = synapse.neuroid.outputValue.normalized;
|
||||||
// float magnitude = synapse.neuroid.outputValue.magnitude;
|
// float magnitude = synapse.neuroid.outputValue.magnitude;
|
||||||
|
Vector3 direction = nucleus.outputValue.normalized;
|
||||||
|
float magnitude = nucleus.outputValue.magnitude;
|
||||||
|
|
||||||
magnitude = synapse.weight * Mathf.Pow(magnitude, exponent);
|
magnitude = synapse.weight * Mathf.Pow(magnitude, exponent);
|
||||||
if (inverse)
|
if (inverse && magnitude > 0)
|
||||||
magnitude = 1 / magnitude;
|
magnitude = 1 / magnitude;
|
||||||
result += direction * magnitude;
|
result += direction * magnitude;
|
||||||
}
|
}
|
||||||
@ -145,7 +130,7 @@ public class Neuroid : Nucleus {
|
|||||||
|
|
||||||
this.outputValue = result;
|
this.outputValue = result;
|
||||||
foreach (Neuroid neuroid in this.outputNeuroids)
|
foreach (Neuroid neuroid in this.outputNeuroids)
|
||||||
neuroid.SetInput(this, this.outputValue);
|
neuroid.SetInput(this);
|
||||||
this.stale = 0;
|
this.stale = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,6 @@ public class Nucleus {
|
|||||||
|
|
||||||
public virtual void AddReceiver(Neuroid receiver) {
|
public virtual void AddReceiver(Neuroid receiver) {
|
||||||
this.outputNeuroids.Add(receiver);
|
this.outputNeuroids.Add(receiver);
|
||||||
receiver.synapses[this] = new(this, Vector3.zero, 1.0f);
|
receiver.synapses[this] = new(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5,7 +5,8 @@ using UnityEngine;
|
|||||||
public class Perception : Nucleus {
|
public class Perception : Nucleus {
|
||||||
public SensoryNeuroid[] sensoryNeuroids = new SensoryNeuroid[7];
|
public SensoryNeuroid[] sensoryNeuroids = new SensoryNeuroid[7];
|
||||||
|
|
||||||
public NeuroidNetwork neuroidNet { get; protected set; }
|
// public NeuroidNetwork neuroidNet { get; protected set; }
|
||||||
|
public NanoBrain neuroidNet { get; protected set; }
|
||||||
|
|
||||||
public class Receiver {
|
public class Receiver {
|
||||||
public int thingType = 0;
|
public int thingType = 0;
|
||||||
@ -15,7 +16,8 @@ public class Perception : Nucleus {
|
|||||||
public HashSet<Receiver> positionReceivers { get; protected set; }
|
public HashSet<Receiver> positionReceivers { get; protected set; }
|
||||||
public HashSet<Receiver> velocityReceivers { get; protected set; }
|
public HashSet<Receiver> velocityReceivers { get; protected set; }
|
||||||
|
|
||||||
public Perception(NeuroidNetwork neuroidNet) : base("Perception") {
|
// public Perception(NeuroidNetwork neuroidNet) : base("Perception") {
|
||||||
|
public Perception(NanoBrain neuroidNet) : base("Perception") {
|
||||||
this.neuroidNet = neuroidNet;
|
this.neuroidNet = neuroidNet;
|
||||||
this.positionReceivers = new();
|
this.positionReceivers = new();
|
||||||
this.velocityReceivers = new();
|
this.velocityReceivers = new();
|
||||||
@ -30,7 +32,7 @@ public class Perception : Nucleus {
|
|||||||
foreach (SensoryNeuroid neuroid in sensoryNeuroids) {
|
foreach (SensoryNeuroid neuroid in sensoryNeuroids) {
|
||||||
if (neuroid != null) {
|
if (neuroid != null) {
|
||||||
neuroid.AddReceiver(receivingNeuroid);
|
neuroid.AddReceiver(receivingNeuroid);
|
||||||
receivingNeuroid.synapses[neuroid] = new(neuroid, Vector3.zero, weight);
|
receivingNeuroid.synapses[neuroid] = new(neuroid, weight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -43,7 +45,7 @@ public class Perception : Nucleus {
|
|||||||
foreach (SensoryNeuroid neuroid in sensoryNeuroids) {
|
foreach (SensoryNeuroid neuroid in sensoryNeuroids) {
|
||||||
if (neuroid != null && neuroid.velocityNeuroid != null) {
|
if (neuroid != null && neuroid.velocityNeuroid != null) {
|
||||||
neuroid.velocityNeuroid.AddReceiver(receivingNeuroid);
|
neuroid.velocityNeuroid.AddReceiver(receivingNeuroid);
|
||||||
receivingNeuroid.synapses[neuroid] = new(neuroid, Vector3.zero, 1.0f);
|
receivingNeuroid.synapses[neuroid] = new(neuroid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,36 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class Receptor {
|
||||||
|
|
||||||
|
public SensoryNeuroid neuroid;
|
||||||
|
|
||||||
|
public int thingId;
|
||||||
|
public Vector3 value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Local position of the thing
|
||||||
|
/// </summary>
|
||||||
|
public virtual Vector3 position {
|
||||||
|
get {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
this.value = value;
|
||||||
|
neuroid.UpdateState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class SensoryNeuroid : Neuroid {
|
public class SensoryNeuroid : Neuroid {
|
||||||
// A neuroid which has no neurons as input
|
// A neuroid which has no neurons as input
|
||||||
// But receives value from a receptor
|
// But receives value from a receptor
|
||||||
public Receptor receptor;
|
public Receptor receptor;
|
||||||
public VelocityNeuroid velocityNeuroid;
|
public VelocityNeuroid velocityNeuroid;
|
||||||
|
|
||||||
public SensoryNeuroid(NeuroidNetwork net, int thingId) : base(net, "sensory neuroid") {
|
// public SensoryNeuroid(NeuroidNetwork net, int thingId) : base(net, "sensory neuroid") {
|
||||||
|
public SensoryNeuroid(NanoBrain net, int thingId) : base(net, "sensory neuroid") {
|
||||||
this.receptor = new Receptor {
|
this.receptor = new Receptor {
|
||||||
neuroid = this,
|
neuroid = this,
|
||||||
thingId = thingId
|
thingId = thingId
|
||||||
@ -17,27 +40,34 @@ public class SensoryNeuroid : Neuroid {
|
|||||||
this.AddReceiver(velocityNeuroid);
|
this.AddReceiver(velocityNeuroid);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
public override void UpdateState() {
|
||||||
|
Vector3 result = receptor.value;
|
||||||
|
// SensoryNeuroid normally do not have synapses...
|
||||||
|
// foreach (Synapse synapse in this.synapses.Values) {
|
||||||
|
// if (synapse.neuroid == null)
|
||||||
|
// Debug.LogWarning(" disconnected synapse");
|
||||||
|
// // if (synapse.value != synapse.neuroid.outputValue)
|
||||||
|
// // Debug.LogWarning("synapse value error");
|
||||||
|
// // Vector3 direction = synapse.value.normalized;
|
||||||
|
// // float magnitude = synapse.value.magnitude;
|
||||||
|
|
||||||
public class Receptor {
|
// Vector3 direction = synapse.neuroid.outputValue.normalized;
|
||||||
|
// float magnitude = synapse.neuroid.outputValue.magnitude;
|
||||||
public SensoryNeuroid neuroid;
|
foreach ((Nucleus nucleus, Synapse synapse) in this.synapses) {
|
||||||
|
Vector3 direction = nucleus.outputValue.normalized;
|
||||||
public int thingId;
|
float magnitude = nucleus.outputValue.magnitude;
|
||||||
/// <summary>
|
magnitude = synapse.weight * Mathf.Pow(magnitude, exponent);
|
||||||
/// Local position of the thing
|
if (inverse)
|
||||||
/// </summary>
|
magnitude = 1 / magnitude;
|
||||||
public virtual Vector3 position {
|
result += direction * magnitude;
|
||||||
get {
|
|
||||||
if (neuroid != null)
|
|
||||||
return neuroid.synapses[neuroid].value;
|
|
||||||
else
|
|
||||||
return Vector3.zero;
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
if (neuroid != null)
|
|
||||||
neuroid.SetInput(neuroid, value);
|
|
||||||
}
|
}
|
||||||
|
if (average && this.synapses.Count > 0)
|
||||||
|
result /= this.synapses.Count + 1;
|
||||||
|
|
||||||
|
this.outputValue = result;
|
||||||
|
foreach (Neuroid neuroid in this.outputNeuroids)
|
||||||
|
neuroid.SetInput(this);
|
||||||
|
this.stale = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,12 +76,14 @@ public class VelocityNeuroid : Neuroid {
|
|||||||
private Vector3 lastPosition = Vector3.zero;
|
private Vector3 lastPosition = Vector3.zero;
|
||||||
private float lastValueTime = 0;
|
private float lastValueTime = 0;
|
||||||
|
|
||||||
public VelocityNeuroid(NeuroidNetwork net) : base(net, "Velocity") {
|
// public VelocityNeuroid(NeuroidNetwork net) : base(net, "Velocity") {
|
||||||
|
public VelocityNeuroid(NanoBrain net) : base(net, "Velocity") {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateState() {
|
public override void UpdateState() {
|
||||||
// Assuming only one synapse for now....
|
// Assuming only one synapse for now....
|
||||||
Vector3 currentPosition = this.synapses.First().Value.value;
|
//Vector3 currentPosition = this.synapses.First().Value.neuroid.outputValue;
|
||||||
|
Vector3 currentPosition = this.synapses.First().Key.outputValue;
|
||||||
float currentValueTime = Time.time;
|
float currentValueTime = Time.time;
|
||||||
|
|
||||||
float deltaTime = currentValueTime - lastValueTime;
|
float deltaTime = currentValueTime - lastValueTime;
|
||||||
@ -61,7 +93,7 @@ public class VelocityNeuroid : Neuroid {
|
|||||||
// No activation function...
|
// No activation function...
|
||||||
this.outputValue = velocity;
|
this.outputValue = velocity;
|
||||||
foreach (Neuroid receiver in outputNeuroids)
|
foreach (Neuroid receiver in outputNeuroids)
|
||||||
receiver?.SetInput(this, this.outputValue);
|
receiver?.SetInput(this);
|
||||||
this.stale = 0;
|
this.stale = 0;
|
||||||
|
|
||||||
this.lastValueTime = Time.time;
|
this.lastValueTime = Time.time;
|
||||||
|
|||||||
@ -371,7 +371,7 @@ MonoBehaviour:
|
|||||||
m_Script: {fileID: 11500000, guid: 0464906885ae3494f8fd0314719fb2db, type: 3}
|
m_Script: {fileID: 11500000, guid: 0464906885ae3494f8fd0314719fb2db, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::SwarmControl
|
m_EditorClassIdentifier: Assembly-CSharp::SwarmControl
|
||||||
speed: 1
|
speed: 2
|
||||||
inertia: 0.1
|
inertia: 0.1
|
||||||
alignmentForce: 5
|
alignmentForce: 5
|
||||||
cohesionForce: 5
|
cohesionForce: 5
|
||||||
|
|||||||
@ -1,16 +1,7 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
|
[RequireComponent(typeof(NanoBrain))]
|
||||||
public class Boid : MonoBehaviour {
|
public class Boid : MonoBehaviour {
|
||||||
// public float speed = 0.2f;
|
|
||||||
// public int neighbourCount = 0;
|
|
||||||
// public float inertia = 0.2f;
|
|
||||||
// public float alignmentForce = 1.0f;
|
|
||||||
// public float cohesionForce = 1.0f;
|
|
||||||
// public float separationForce = 1.0f;
|
|
||||||
// public float separationDistance = 0.5f;
|
|
||||||
// public float bodyForce = 1;
|
|
||||||
|
|
||||||
public const int BoundaryType = 1;
|
public const int BoundaryType = 1;
|
||||||
public const int BoidType = 2;
|
public const int BoidType = 2;
|
||||||
|
|
||||||
@ -21,7 +12,8 @@ public class Boid : MonoBehaviour {
|
|||||||
private Bounds innerBounds;
|
private Bounds innerBounds;
|
||||||
private Bounds outerBounds;
|
private Bounds outerBounds;
|
||||||
|
|
||||||
public NeuroidNetwork neuroidNet = new();
|
//public NeuroidNetwork neuroidNet = new();
|
||||||
|
public NanoBrain neuroidNet;
|
||||||
public Perception perception;
|
public Perception perception;
|
||||||
|
|
||||||
public Nucleus behaviour;
|
public Nucleus behaviour;
|
||||||
@ -31,6 +23,8 @@ public class Boid : MonoBehaviour {
|
|||||||
public int id;
|
public int id;
|
||||||
|
|
||||||
void Awake() {
|
void Awake() {
|
||||||
|
neuroidNet = GetComponent<NanoBrain>();
|
||||||
|
|
||||||
this.id = this.GetInstanceID();
|
this.id = this.GetInstanceID();
|
||||||
|
|
||||||
sc = FindFirstObjectByType<SwarmControl>();
|
sc = FindFirstObjectByType<SwarmControl>();
|
||||||
@ -103,7 +97,9 @@ public class Boid : MonoBehaviour {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OnDrawGizmosSelected() {
|
void OnDrawGizmosSelected() {
|
||||||
Gizmos.DrawWireSphere(transform.position, sc.perceptionDistance);
|
if (sc == null)
|
||||||
|
return;
|
||||||
|
Gizmos.DrawWireSphere(this.transform.position, sc.perceptionDistance);
|
||||||
Gizmos.color = Color.yellow;
|
Gizmos.color = Color.yellow;
|
||||||
Vector3 worldForce = this.transform.TransformDirection(totalForce.outputValue);
|
Vector3 worldForce = this.transform.TransformDirection(totalForce.outputValue);
|
||||||
Gizmos.DrawRay(transform.position, worldForce * 10);
|
Gizmos.DrawRay(transform.position, worldForce * 10);
|
||||||
|
|||||||
@ -3,7 +3,8 @@ public class Roaming : Nucleus {
|
|||||||
|
|
||||||
public Neuroid output;
|
public Neuroid output;
|
||||||
|
|
||||||
public Roaming(NeuroidNetwork neuroidNet, Perception perception, SwarmControl sc) : base("Roaming nucleus") {
|
// public Roaming(NeuroidNetwork neuroidNet, Perception perception, SwarmControl sc) : base("Roaming nucleus") {
|
||||||
|
public Roaming(NanoBrain neuroidNet, Perception perception, SwarmControl sc) : base("Roaming nucleus") {
|
||||||
avoidance = new(neuroidNet, "Avoidance") { inverse = true };
|
avoidance = new(neuroidNet, "Avoidance") { inverse = true };
|
||||||
perception.SendPositions(avoidance, 1.0f, 1);
|
perception.SendPositions(avoidance, 1.0f, 1);
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,8 @@ public class Swarming : Nucleus {
|
|||||||
public const int BoundaryType = 1;
|
public const int BoundaryType = 1;
|
||||||
public const int BoidType = 2;
|
public const int BoidType = 2;
|
||||||
|
|
||||||
public Swarming(NeuroidNetwork neuroidNet, Perception perception, SwarmControl sc) : base("Swarming Nucleus") {
|
// public Swarming(NeuroidNetwork neuroidNet, Perception perception, SwarmControl sc) : base("Swarming Nucleus") {
|
||||||
|
public Swarming(NanoBrain neuroidNet, Perception perception, SwarmControl sc) : base("Swarming Nucleus") {
|
||||||
this.cohesion = new(neuroidNet, "Cohesion");
|
this.cohesion = new(neuroidNet, "Cohesion");
|
||||||
perception.SendPositions(this.cohesion, 1.0f, BoidType);
|
perception.SendPositions(this.cohesion, 1.0f, BoidType);
|
||||||
|
|
||||||
@ -23,7 +24,7 @@ public class Swarming : Nucleus {
|
|||||||
perception.SendPositions(this.avoidance);
|
perception.SendPositions(this.avoidance);
|
||||||
|
|
||||||
this.output = new(neuroidNet, "Swarming");
|
this.output = new(neuroidNet, "Swarming");
|
||||||
//this.output.GetInputFrom(alignment, sc.alignmentForce);
|
this.output.GetInputFrom(alignment, sc.alignmentForce);
|
||||||
this.output.GetInputFrom(cohesion, sc.cohesionForce);
|
this.output.GetInputFrom(cohesion, sc.cohesionForce);
|
||||||
this.output.GetInputFrom(avoidance, -sc.avoidanceForce);
|
this.output.GetInputFrom(avoidance, -sc.avoidanceForce);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user