Scriptable nanobrain
This commit is contained in:
parent
5a9aa7161b
commit
605bdfc629
@ -49,8 +49,10 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Assets/NanoBrain/VisualEditor/Editor/NanoBrainEditor.cs" />
|
<Compile Include="Assets/NanoBrain/VisualEditor/Editor/NanoBrainEditor.cs" />
|
||||||
|
<Compile Include="Assets/NanoBrain/VisualEditor/Editor/NanoBrainInspector.cs" />
|
||||||
<Compile Include="Assets/NanoBrain/Editor/NanoBrain_Editor.cs" />
|
<Compile Include="Assets/NanoBrain/Editor/NanoBrain_Editor.cs" />
|
||||||
<Compile Include="Assets/NanoBrain/Editor/NeuroidWindow.cs" />
|
<Compile Include="Assets/NanoBrain/Editor/NeuroidWindow.cs" />
|
||||||
|
<Compile Include="Assets/NanoBrain/VisualEditor/Editor/NanoBrainComponent_Editor.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="UnityEngine">
|
<Reference Include="UnityEngine">
|
||||||
|
|||||||
@ -53,6 +53,7 @@
|
|||||||
<Compile Include="Assets/NanoBrain/NeuroidBehaviour.cs" />
|
<Compile Include="Assets/NanoBrain/NeuroidBehaviour.cs" />
|
||||||
<Compile Include="Assets/NanoBrain/NanoBrain.cs" />
|
<Compile Include="Assets/NanoBrain/NanoBrain.cs" />
|
||||||
<Compile Include="Assets/NanoBrain/SensoryNeuroid.cs" />
|
<Compile Include="Assets/NanoBrain/SensoryNeuroid.cs" />
|
||||||
|
<Compile Include="Assets/NanoBrain/VisualEditor/NanoBrainComponent.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" />
|
||||||
<Compile Include="Assets/NanoBrain/Perception.cs" />
|
<Compile Include="Assets/NanoBrain/Perception.cs" />
|
||||||
|
|||||||
@ -26,15 +26,15 @@ public class NanoBrain_Editor : Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void SelectNeuron() {
|
private void SelectNeuron() {
|
||||||
GameObject selectedObject = ((NanoBrain)target).gameObject;
|
// GameObject selectedObject = ((NanoBrain)target).gameObject;
|
||||||
if (!selectedObject.TryGetComponent(out Boid boid))
|
// if (!selectedObject.TryGetComponent(out Boid boid))
|
||||||
return;
|
// return;
|
||||||
|
|
||||||
Nucleus neuroid = boid.totalForce;
|
// Nucleus neuroid = boid.totalForce;
|
||||||
this.currentNucleus = neuroid;
|
// this.currentNucleus = neuroid;
|
||||||
|
|
||||||
BuildLayers();
|
// BuildLayers();
|
||||||
Debug.Log($"Layercount = {this.layers.Count}");
|
// Debug.Log($"Layercount = {this.layers.Count}");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Start
|
#endregion Start
|
||||||
@ -57,7 +57,11 @@ public class NanoBrain_Editor : Editor {
|
|||||||
EditorGUILayout.Vector3Field("Output Value", currentNucleus.outputValue);
|
EditorGUILayout.Vector3Field("Output Value", currentNucleus.outputValue);
|
||||||
if (currentNucleus.synapses.Count > 0) {
|
if (currentNucleus.synapses.Count > 0) {
|
||||||
EditorGUI.indentLevel++;
|
EditorGUI.indentLevel++;
|
||||||
foreach ((Nucleus nucleus, float weight) in currentNucleus.synapses) {
|
//foreach ((Nucleus nucleus, float weight) in currentNucleus.synapses) {
|
||||||
|
foreach (Synapse synapse in currentNucleus.synapses) {
|
||||||
|
Nucleus nucleus = synapse.nucleus;
|
||||||
|
float weight = synapse.weight;
|
||||||
|
|
||||||
EditorGUI.BeginDisabledGroup(nucleus.isSleeping);
|
EditorGUI.BeginDisabledGroup(nucleus.isSleeping);
|
||||||
|
|
||||||
EditorGUILayout.BeginHorizontal();
|
EditorGUILayout.BeginHorizontal();
|
||||||
@ -83,7 +87,9 @@ public class NanoBrain_Editor : Editor {
|
|||||||
return;
|
return;
|
||||||
NeuroidLayer currentLayer = new() { ix = layerIx };
|
NeuroidLayer currentLayer = new() { ix = layerIx };
|
||||||
|
|
||||||
foreach (Neuroid outputNeuroid in selectedNucleus.receivers) {
|
//foreach (Neuroid outputNeuroid in selectedNucleus.receivers) {
|
||||||
|
foreach (Receiver receiver in selectedNucleus.receivers) {
|
||||||
|
Nucleus outputNeuroid = receiver.nucleus;
|
||||||
if (outputNeuroid != null) {
|
if (outputNeuroid != null) {
|
||||||
AddToLayer(currentLayer, outputNeuroid);
|
AddToLayer(currentLayer, outputNeuroid);
|
||||||
// Debug.Log($"layer {layerIx} nucleus {outputNeuroid.name}");
|
// Debug.Log($"layer {layerIx} nucleus {outputNeuroid.name}");
|
||||||
@ -102,7 +108,9 @@ public class NanoBrain_Editor : Editor {
|
|||||||
layerIx++;
|
layerIx++;
|
||||||
currentLayer = new() { ix = layerIx };
|
currentLayer = new() { ix = layerIx };
|
||||||
|
|
||||||
foreach (Nucleus input in selectedNucleus.synapses.Keys) {
|
//foreach (Nucleus input in selectedNucleus.synapses.Keys) {
|
||||||
|
foreach (Synapse synapse in selectedNucleus.synapses) {
|
||||||
|
Nucleus input = synapse.nucleus;
|
||||||
AddToLayer(currentLayer, input);
|
AddToLayer(currentLayer, input);
|
||||||
// Debug.Log($"layer {layerIx} nucleus {input.name}");
|
// Debug.Log($"layer {layerIx} nucleus {input.name}");
|
||||||
}
|
}
|
||||||
@ -151,7 +159,10 @@ public class NanoBrain_Editor : Editor {
|
|||||||
float inputSpacing = 400f / layerNeuroid.synapses.Count;
|
float inputSpacing = 400f / layerNeuroid.synapses.Count;
|
||||||
float inputMargin = 10 + inputSpacing / 2;
|
float inputMargin = 10 + inputSpacing / 2;
|
||||||
int minStale = 10000;
|
int minStale = 10000;
|
||||||
foreach ((Nucleus nucleus, float weight) in layerNeuroid.synapses) {
|
//foreach ((Nucleus nucleus, float weight) in layerNeuroid.synapses) {
|
||||||
|
foreach (Synapse synapse in layerNeuroid.synapses) {
|
||||||
|
Nucleus nucleus = synapse.nucleus;
|
||||||
|
float weight = synapse.weight;
|
||||||
if (this.neuroidPositions.ContainsKey(nucleus)) {
|
if (this.neuroidPositions.ContainsKey(nucleus)) {
|
||||||
Vector2Int inputNeuroidPos = this.neuroidPositions[nucleus];
|
Vector2Int inputNeuroidPos = this.neuroidPositions[nucleus];
|
||||||
if (inputNeuroidPos.x == layerNeuroidPos.x + 1) {
|
if (inputNeuroidPos.x == layerNeuroidPos.x + 1) {
|
||||||
|
|||||||
@ -40,7 +40,9 @@ public class GraphEditorWindow : EditorWindow {
|
|||||||
return;
|
return;
|
||||||
NeuroidLayer currentLayer = new() { ix = layerIx };
|
NeuroidLayer currentLayer = new() { ix = layerIx };
|
||||||
|
|
||||||
foreach (Nucleus outputNeuroid in selectedNucleus.receivers) {
|
//foreach (Nucleus outputNeuroid in selectedNucleus.receivers) {
|
||||||
|
foreach (Receiver receiver in selectedNucleus.receivers) {
|
||||||
|
Nucleus outputNeuroid = receiver.nucleus;
|
||||||
if (outputNeuroid != null) {
|
if (outputNeuroid != null) {
|
||||||
AddToLayer(currentLayer, outputNeuroid);
|
AddToLayer(currentLayer, outputNeuroid);
|
||||||
Debug.Log($"layer {layerIx} nucleus {outputNeuroid.name}");
|
Debug.Log($"layer {layerIx} nucleus {outputNeuroid.name}");
|
||||||
@ -64,7 +66,9 @@ public class GraphEditorWindow : EditorWindow {
|
|||||||
// Debug.Log($"Synapse {six}");
|
// Debug.Log($"Synapse {six}");
|
||||||
// Nucleus input = synapse.neuroid;
|
// Nucleus input = synapse.neuroid;
|
||||||
//foreach ((Nucleus input, Synapse synapse) in selectedNucleus.synapses) {
|
//foreach ((Nucleus input, Synapse synapse) in selectedNucleus.synapses) {
|
||||||
foreach ((Nucleus input, float weight) in selectedNucleus.synapses) {
|
//foreach ((Nucleus input, float weight) in selectedNucleus.synapses) {
|
||||||
|
foreach (Synapse synapse in selectedNucleus.synapses) {
|
||||||
|
Nucleus input = synapse.nucleus;
|
||||||
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}");
|
||||||
@ -104,8 +108,10 @@ public class GraphEditorWindow : EditorWindow {
|
|||||||
|
|
||||||
// If the output neuroid is visited
|
// If the output neuroid is visited
|
||||||
// Note: this does not yet work for multiple outputs yet (see the use of First())
|
// Note: this does not yet work for multiple outputs yet (see the use of First())
|
||||||
|
// if (neuroid.receivers.Count == 0 // make sure the root neuroids are processed directly
|
||||||
|
// || (neuronVisited.Contains(neuroid.receivers.First()) && neuroid.receivers.First().layerIx == layerIx - 1)) {
|
||||||
if (neuroid.receivers.Count == 0 // make sure the root neuroids are processed directly
|
if (neuroid.receivers.Count == 0 // make sure the root neuroids are processed directly
|
||||||
|| (neuronVisited.Contains(neuroid.receivers.First()) && neuroid.receivers.First().layerIx == layerIx - 1)) {
|
|| (neuronVisited.Contains(neuroid.receivers.First().nucleus) && neuroid.receivers.First().nucleus.layerIx == layerIx - 1)) {
|
||||||
// Add it to the next layer
|
// Add it to the next layer
|
||||||
currentLayer.neuroids.Add(neuroid);
|
currentLayer.neuroids.Add(neuroid);
|
||||||
neuroid.layerIx = layerIx;
|
neuroid.layerIx = layerIx;
|
||||||
@ -182,7 +188,10 @@ public class GraphEditorWindow : EditorWindow {
|
|||||||
|
|
||||||
// Vector2Int inputNeuroidPos = this.neuroidPositions[synapse.neuroid];
|
// Vector2Int inputNeuroidPos = this.neuroidPositions[synapse.neuroid];
|
||||||
//foreach ((Nucleus neuroid, Synapse synapse) in layerNeuroid.synapses) {
|
//foreach ((Nucleus neuroid, Synapse synapse) in layerNeuroid.synapses) {
|
||||||
foreach ((Nucleus neuroid, float weight) in layerNeuroid.synapses) {
|
//foreach ((Nucleus neuroid, float weight) in layerNeuroid.synapses) {
|
||||||
|
foreach (Synapse synapse in layerNeuroid.synapses) {
|
||||||
|
Nucleus neuroid = synapse.nucleus;
|
||||||
|
float weight = synapse.weight;
|
||||||
if (neuroid != null) {
|
if (neuroid != null) {
|
||||||
if (this.neuroidPositions.ContainsKey(neuroid)) {
|
if (this.neuroidPositions.ContainsKey(neuroid)) {
|
||||||
Vector2Int inputNeuroidPos = this.neuroidPositions[neuroid];
|
Vector2Int inputNeuroidPos = this.neuroidPositions[neuroid];
|
||||||
@ -272,17 +281,17 @@ public class GraphEditorWindow : EditorWindow {
|
|||||||
if (boid == null)
|
if (boid == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Nucleus neuroid = boid.behaviour;
|
// Nucleus neuroid = boid.behaviour;
|
||||||
this.currentNucleus = neuroid;
|
// this.currentNucleus = neuroid;
|
||||||
if (neuroid == null)
|
// if (neuroid == null)
|
||||||
this.allNeuroids = new();
|
// this.allNeuroids = new();
|
||||||
else
|
// else
|
||||||
this.allNeuroids = neuroid.brain.neuroids;
|
// this.allNeuroids = neuroid.brain.neuroids;
|
||||||
|
|
||||||
|
|
||||||
Debug.Log($"Neuroncount = {this.allNeuroids.Count}");
|
// Debug.Log($"Neuroncount = {this.allNeuroids.Count}");
|
||||||
BuildLayers();
|
// BuildLayers();
|
||||||
Debug.Log($"Layercount = {this.layers.Count}");
|
// Debug.Log($"Layercount = {this.layers.Count}");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ public class Neuroid : Nucleus {
|
|||||||
public bool inverse = false;
|
public bool inverse = false;
|
||||||
public float exponent = 1.0f;
|
public float exponent = 1.0f;
|
||||||
|
|
||||||
public Neuroid(NanoBrain brain, string name) : base(name) {
|
public Neuroid(NanoBrain brain, string name) : base(null, name) {
|
||||||
this.brain = brain;
|
this.brain = brain;
|
||||||
if (this.brain != null)
|
if (this.brain != null)
|
||||||
this.brain.neuroids.Add(this);
|
this.brain.neuroids.Add(this);
|
||||||
@ -13,43 +13,46 @@ public class Neuroid : Nucleus {
|
|||||||
Debug.LogError("No neuroid network");
|
Debug.LogError("No neuroid network");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Neuroid(NanoBrainObj brain, string name) : base(name) {
|
public Neuroid(NanoBrainObj brain, string name) : base(brain, name) {
|
||||||
this.newBrain = brain;
|
|
||||||
if (this.newBrain != null)
|
|
||||||
this.newBrain.neuroids.Add(this);
|
|
||||||
else
|
|
||||||
Debug.LogError("No neuroid network");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetWeight(Neuroid input, float weight) {
|
public void SetWeight(Neuroid input, float weight) {
|
||||||
this.synapses[input] = weight;
|
//this.synapses[input] = weight;
|
||||||
|
this.SetWeight((Nucleus)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] = weight;
|
// //this.synapses[input] = weight;
|
||||||
}
|
// this.SetWeight((Nucleus)input, weight);
|
||||||
|
// }
|
||||||
|
|
||||||
public void SetInput(Neuroid input) {
|
public void SetInput(Neuroid input) {
|
||||||
if (this.synapses.ContainsKey(input) == false)
|
// if (this.synapses.ContainsKey(input) == false)
|
||||||
this.synapses[input] = 1.0f;
|
// this.synapses[input] = 1.0f;
|
||||||
|
if (this.SynapseExists(input))
|
||||||
|
this.SetWeight(input, 1.0f);
|
||||||
UpdateState();
|
UpdateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetInput(Neuroid input, float weight) {
|
public void SetInput(Neuroid input, float weight) {
|
||||||
this.synapses[input] = weight;
|
//this.synapses[input] = weight;
|
||||||
|
this.SetWeight(input, weight);
|
||||||
UpdateState();
|
UpdateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void UpdateState() {
|
public virtual void UpdateState() {
|
||||||
Vector3 result = Vector3.zero;
|
Vector3 result = Vector3.zero;
|
||||||
foreach ((Nucleus nucleus, float weight) in this.synapses) {
|
//foreach ((Nucleus nucleus, float weight) in this.synapses) {
|
||||||
|
foreach (Synapse synapse in this.synapses) {
|
||||||
|
Nucleus nucleus = synapse.nucleus;
|
||||||
if (nucleus is Neuroid neuroid && neuroid.isSleeping)
|
if (nucleus is Neuroid neuroid && neuroid.isSleeping)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Vector3 direction = nucleus.outputValue.normalized;
|
Vector3 direction = nucleus.outputValue.normalized;
|
||||||
float magnitude = nucleus.outputValue.magnitude;
|
float magnitude = nucleus.outputValue.magnitude;
|
||||||
|
|
||||||
|
float weight = synapse.weight;
|
||||||
magnitude = weight * Mathf.Pow(magnitude, exponent);
|
magnitude = weight * Mathf.Pow(magnitude, exponent);
|
||||||
if (inverse && magnitude > 0)
|
if (inverse && magnitude > 0)
|
||||||
magnitude = 1 / magnitude;
|
magnitude = 1 / magnitude;
|
||||||
@ -62,8 +65,10 @@ public class Neuroid : Nucleus {
|
|||||||
this.outputValue = result;
|
this.outputValue = result;
|
||||||
this.stale = 0;
|
this.stale = 0;
|
||||||
|
|
||||||
foreach (Neuroid receiver in this.receivers)
|
foreach (Receiver receiver in this.receivers) {
|
||||||
receiver.SetInput(this);
|
if (receiver.nucleus is Neuroid neuroid)
|
||||||
|
neuroid.SetInput(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// public bool IsStale() {
|
// public bool IsStale() {
|
||||||
|
|||||||
@ -1,35 +1,169 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
|
[System.Serializable]
|
||||||
public class Nucleus {
|
public class Nucleus {
|
||||||
public int stale = 0;
|
|
||||||
|
public int id; // hash code
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
protected string _name;
|
||||||
|
public virtual string name {
|
||||||
|
get => _name;
|
||||||
|
set => _name = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
//public readonly Dictionary<Nucleus, float> synapses = new();
|
||||||
|
[SerializeField]
|
||||||
|
public List<Synapse> synapses = new();
|
||||||
|
//public HashSet<Nucleus> receivers = new();
|
||||||
|
[SerializeField]
|
||||||
|
public List<Receiver> receivers = new();
|
||||||
|
|
||||||
|
#region Serialization
|
||||||
|
|
||||||
|
public void Rebuild(NanoBrainObj brain) {
|
||||||
|
if (this.synapses != null) {
|
||||||
|
foreach (Synapse synapse in synapses)
|
||||||
|
synapse.Rebuild(brain);
|
||||||
|
}
|
||||||
|
if (this.receivers == null)
|
||||||
|
this.receivers = new();
|
||||||
|
else
|
||||||
|
foreach (Receiver receiver in receivers)
|
||||||
|
receiver.Rebuild(brain);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Runtime state (not serialized)
|
||||||
|
|
||||||
public NanoBrain brain { get; protected set; }
|
public NanoBrain brain { get; protected set; }
|
||||||
public NanoBrainObj newBrain { get; protected set; }
|
public NanoBrainObj newBrain { get; protected set; }
|
||||||
|
|
||||||
public virtual string name { get; set; }
|
|
||||||
|
|
||||||
public readonly Dictionary<Nucleus, float> synapses = new();
|
|
||||||
public HashSet<Nucleus> receivers = new();
|
|
||||||
public virtual Vector3 outputValue { get; set; }
|
public virtual Vector3 outputValue { get; set; }
|
||||||
|
|
||||||
|
[System.NonSerialized]
|
||||||
|
public int stale = 0;
|
||||||
|
[System.NonSerialized]
|
||||||
public int layerIx;
|
public int layerIx;
|
||||||
|
|
||||||
public Nucleus(string name) {
|
#endregion Runtime state
|
||||||
this.name = name;
|
|
||||||
|
public Nucleus(NanoBrainObj brain, string name) {
|
||||||
|
this.newBrain = brain;
|
||||||
|
if (this.newBrain != null)
|
||||||
|
this.newBrain.nuclei.Add(this);
|
||||||
|
else
|
||||||
|
Debug.LogError("No neuroid network");
|
||||||
|
|
||||||
|
this._name = name;
|
||||||
|
this.id = this.GetHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void AddReceiver(Nucleus receiver) {
|
public virtual void AddReceiver(Nucleus receiver) {
|
||||||
//Debug.Log($"add receiver to {this} for {receiver} {receiver.GetHashCode()} {this.receivers.Count} {receiver.synapses.Count}");
|
this.receivers.Add(new Receiver(receiver));
|
||||||
this.receivers.Add(receiver);
|
//receiver.synapses[this] = 1.0f; // new(this);
|
||||||
receiver.synapses[this] = 1.0f; // new(this);
|
receiver.SetWeight(this, 1.0f);
|
||||||
//Debug.Log($"receiver # {this.receivers.Count} synapse count {receiver.synapses.Count}");
|
//Debug.Log($"receiver # {this.receivers.Count} synapse count {receiver.synapses.Count}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Delete(Nucleus nucleus) {
|
||||||
|
foreach (Synapse synapse in nucleus.synapses) {
|
||||||
|
if (synapse.nucleus.receivers.Count > 1) {
|
||||||
|
// there is another nucleus feeding into this input nucleus
|
||||||
|
synapse.nucleus.receivers.RemoveAll(r => r.nucleus == nucleus);
|
||||||
|
} else {
|
||||||
|
// No other links, delete it.
|
||||||
|
Nucleus.Delete(synapse.nucleus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (Receiver receiver in nucleus.receivers)
|
||||||
|
receiver.nucleus.synapses.RemoveAll(s => s.nucleus == nucleus);
|
||||||
|
|
||||||
|
nucleus.newBrain.nuclei.RemoveAll(n => n == nucleus);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetInputFrom(Nucleus input, float weight = 1.0f) {
|
||||||
|
input.AddReceiver(this);
|
||||||
|
this.SetWeight(input, weight);
|
||||||
|
}
|
||||||
|
|
||||||
public bool isSleeping {
|
public bool isSleeping {
|
||||||
get {
|
get {
|
||||||
return this.stale > 2;
|
return this.stale > 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool SynapseExists(Nucleus nucleus) {
|
||||||
|
foreach (Synapse synapse in synapses) {
|
||||||
|
if (synapse.nucleus == nucleus)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetWeight(Nucleus nucleus, float weight) {
|
||||||
|
foreach (Synapse synapse in synapses) {
|
||||||
|
if (synapse.nucleus == nucleus) {
|
||||||
|
synapse.weight = weight;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Synapse newSynapse = new(nucleus, weight);
|
||||||
|
synapses.Add(newSynapse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.Serializable]
|
||||||
|
public class Synapse {
|
||||||
|
[System.NonSerialized]
|
||||||
|
public Nucleus nucleus;
|
||||||
|
public int nucleusId;
|
||||||
|
public float weight;
|
||||||
|
|
||||||
|
public Synapse(Nucleus nucleus, float weight) {
|
||||||
|
this.nucleus = nucleus;
|
||||||
|
this.nucleusId = nucleus.id;
|
||||||
|
this.weight = weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Rebuild(NanoBrainObj brain) {
|
||||||
|
if (brain == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Nucleus nucleus in brain.nuclei) {
|
||||||
|
if (nucleus.id == this.nucleusId) {
|
||||||
|
this.nucleus = nucleus;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Debug.LogError($"Synapse deserialization error: could not find nucleus with id {this.nucleusId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.Serializable]
|
||||||
|
public class Receiver {
|
||||||
|
[System.NonSerialized]
|
||||||
|
public Nucleus nucleus;
|
||||||
|
public int nucleusId;
|
||||||
|
|
||||||
|
public Receiver(Nucleus nucleus) {
|
||||||
|
this.nucleus = nucleus;
|
||||||
|
this.nucleusId = nucleus.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Rebuild(NanoBrainObj brain) {
|
||||||
|
if (brain == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (Nucleus nucleus in brain.nuclei) {
|
||||||
|
if (nucleus.id == this.nucleusId) {
|
||||||
|
this.nucleus = nucleus;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Debug.LogError($"Synapse deserialization error: could not find nucleus with id {this.nucleusId}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -7,19 +7,26 @@ public class Perception : Nucleus {
|
|||||||
|
|
||||||
public class Receiver {
|
public class Receiver {
|
||||||
public int thingType = 0;
|
public int thingType = 0;
|
||||||
public Neuroid neuroid;
|
public Nucleus neuroid;
|
||||||
}
|
}
|
||||||
|
|
||||||
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(NanoBrain neuroidNet) : base("Perception") {
|
public Perception(NanoBrainObj brain) : base(brain, "Perception") {
|
||||||
|
//this.brain = brain;
|
||||||
|
this.positionReceivers = new();
|
||||||
|
this.velocityReceivers = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Perception(NanoBrain neuroidNet) : base(null, "Perception") {
|
||||||
this.brain = neuroidNet;
|
this.brain = neuroidNet;
|
||||||
this.positionReceivers = new();
|
this.positionReceivers = new();
|
||||||
this.velocityReceivers = new();
|
this.velocityReceivers = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendPositions(Neuroid receivingNeuroid, int thingType = 0, float weight = 1.0f) {
|
public void SendPositions(Nucleus receivingNeuroid, int thingType = 0, float weight = 1.0f) {
|
||||||
Receiver receiver = new() {
|
Receiver receiver = new() {
|
||||||
thingType = thingType,
|
thingType = thingType,
|
||||||
neuroid = receivingNeuroid
|
neuroid = receivingNeuroid
|
||||||
@ -28,11 +35,11 @@ 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] = weight;
|
receivingNeuroid.SetWeight(neuroid, weight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void SendVelocities(Neuroid receivingNeuroid, int thingType = 0, float weight = 1.0f) {
|
public void SendVelocities(Nucleus receivingNeuroid, int thingType = 0, float weight = 1.0f) {
|
||||||
Receiver receiver = new() {
|
Receiver receiver = new() {
|
||||||
thingType = thingType,
|
thingType = thingType,
|
||||||
neuroid = receivingNeuroid
|
neuroid = receivingNeuroid
|
||||||
@ -41,7 +48,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] = weight;
|
receivingNeuroid.SetWeight(neuroid, weight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -58,7 +58,10 @@ public class SensoryNeuroid : Neuroid {
|
|||||||
|
|
||||||
public override void UpdateState() {
|
public override void UpdateState() {
|
||||||
Vector3 result = receptor.localPosition;
|
Vector3 result = receptor.localPosition;
|
||||||
foreach ((Nucleus nucleus, float weight) in this.synapses) {
|
//foreach ((Nucleus nucleus, float weight) in this.synapses) {
|
||||||
|
foreach (Synapse synapse in this.synapses) {
|
||||||
|
Nucleus nucleus = synapse.nucleus;
|
||||||
|
float weight = synapse.weight;
|
||||||
Vector3 direction = nucleus.outputValue.normalized;
|
Vector3 direction = nucleus.outputValue.normalized;
|
||||||
float magnitude = nucleus.outputValue.magnitude;
|
float magnitude = nucleus.outputValue.magnitude;
|
||||||
|
|
||||||
@ -71,8 +74,10 @@ public class SensoryNeuroid : Neuroid {
|
|||||||
result /= this.synapses.Count + 1;
|
result /= this.synapses.Count + 1;
|
||||||
|
|
||||||
this.outputValue = result;
|
this.outputValue = result;
|
||||||
foreach (Neuroid neuroid in this.receivers)
|
//foreach (Neuroid neuroid in this.receivers)
|
||||||
neuroid.SetInput(this);
|
foreach (Receiver receiver in this.receivers)
|
||||||
|
if (receiver.nucleus is Neuroid neuroid)
|
||||||
|
neuroid.SetInput(this);
|
||||||
this.stale = 0;
|
this.stale = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,7 +99,8 @@ public class VelocityNeuroid : Neuroid {
|
|||||||
|
|
||||||
public override void UpdateState() {
|
public override void UpdateState() {
|
||||||
// Assuming only one synapse for now....
|
// Assuming only one synapse for now....
|
||||||
Vector3 currentPosition = this.synapses.First().Key.outputValue;
|
//Vector3 currentPosition = this.synapses.First().Key.outputValue;
|
||||||
|
Vector3 currentPosition = this.synapses.First().nucleus.outputValue;
|
||||||
float currentValueTime = Time.time;
|
float currentValueTime = Time.time;
|
||||||
|
|
||||||
if (lastValueTime != 0) {
|
if (lastValueTime != 0) {
|
||||||
@ -106,8 +112,11 @@ public class VelocityNeuroid : Neuroid {
|
|||||||
this.outputValue = velocity;
|
this.outputValue = velocity;
|
||||||
this.stale = 0;
|
this.stale = 0;
|
||||||
|
|
||||||
foreach (Neuroid receiver in receivers)
|
//foreach (Neuroid receiver in receivers)
|
||||||
receiver?.SetInput(this);
|
foreach(Receiver receiver in receivers) {
|
||||||
|
if (receiver.nucleus is Neuroid neuroid)
|
||||||
|
neuroid.SetInput(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.lastValueTime = currentValueTime;
|
this.lastValueTime = currentValueTime;
|
||||||
|
|||||||
@ -0,0 +1,94 @@
|
|||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.UIElements;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UIElements;
|
||||||
|
|
||||||
|
[CustomEditor(typeof(NanoBrainComponent))]
|
||||||
|
public class NanoBrainComponent_Editor : Editor {
|
||||||
|
protected static VisualElement mainContainer;
|
||||||
|
protected static VisualElement inspectorContainer;
|
||||||
|
|
||||||
|
private SerializedProperty brainProp;
|
||||||
|
|
||||||
|
public void OnEnable() {
|
||||||
|
brainProp = serializedObject.FindProperty(nameof(NanoBrainComponent.brain));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override VisualElement CreateInspectorGUI() {
|
||||||
|
NanoBrainComponent component = target as NanoBrainComponent;
|
||||||
|
NanoBrainObj brain = component.brain;
|
||||||
|
|
||||||
|
serializedObject.Update();
|
||||||
|
|
||||||
|
|
||||||
|
VisualElement root = new();
|
||||||
|
root.style.flexDirection = FlexDirection.Column; // side-by-side layout
|
||||||
|
root.style.flexGrow = 1;
|
||||||
|
root.style.minHeight = 600;
|
||||||
|
root.style.paddingLeft = 0;
|
||||||
|
root.style.paddingRight = 0;
|
||||||
|
root.style.paddingTop = 0;
|
||||||
|
root.style.paddingBottom = 0;
|
||||||
|
|
||||||
|
root.styleSheets.Add(Resources.Load<StyleSheet>("GraphStyles"));
|
||||||
|
|
||||||
|
PropertyField brainField = new(brainProp);
|
||||||
|
brainField.label = "Nano Brain";
|
||||||
|
root.Add(brainField);
|
||||||
|
|
||||||
|
mainContainer = new() {
|
||||||
|
name = "main",
|
||||||
|
style = {
|
||||||
|
flexDirection = FlexDirection.Row,
|
||||||
|
flexGrow = 1,
|
||||||
|
minHeight = 500,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
NanoBrainInspector.GraphView board;
|
||||||
|
board = new NanoBrainInspector.GraphView();
|
||||||
|
board.style.flexGrow = 1;
|
||||||
|
|
||||||
|
inspectorContainer = new VisualElement {
|
||||||
|
name = "inspector",
|
||||||
|
style = {
|
||||||
|
width = 400,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mainContainer.Add(board);
|
||||||
|
mainContainer.Add(inspectorContainer);
|
||||||
|
root.Add(mainContainer);
|
||||||
|
|
||||||
|
// Run once for initial state (use resolved style width if available)
|
||||||
|
float initialWidth = root.layout.width > 0 ? root.layout.width : root.contentRect.width;
|
||||||
|
UpdateLayout(initialWidth);
|
||||||
|
|
||||||
|
// React to size changes of root (or parent if appropriate)
|
||||||
|
root.RegisterCallback<GeometryChangedEvent>(evt => {
|
||||||
|
UpdateLayout(evt.newRect.width);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (brain != null)
|
||||||
|
board.SetGraph(brain, brain.root, inspectorContainer);
|
||||||
|
else
|
||||||
|
Debug.LogWarning(" No brain!");
|
||||||
|
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateLayout(float containerWidth) {
|
||||||
|
if (containerWidth > 800f) {
|
||||||
|
mainContainer.style.flexDirection = FlexDirection.Row;
|
||||||
|
inspectorContainer.style.width = 400; // fixed sidebar width
|
||||||
|
inspectorContainer.style.flexGrow = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mainContainer.style.flexDirection = FlexDirection.Column;
|
||||||
|
inspectorContainer.style.width = Length.Percent(100); // full width below
|
||||||
|
inspectorContainer.style.flexDirection = FlexDirection.Column;
|
||||||
|
inspectorContainer.style.flexGrow = 1; // can set 0 or keep as needed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f05072314d39990639a2dbf99f322664
|
||||||
@ -1,6 +1,7 @@
|
|||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UIElements;
|
using UnityEngine.UIElements;
|
||||||
|
using UnityEditor.Callbacks;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
@ -10,7 +11,7 @@ public class NucleusLayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class NanoBrainEditor : EditorWindow {
|
public class NanoBrainEditor : EditorWindow {
|
||||||
public static NanoBrainObj brain;
|
public NanoBrainObj brain;
|
||||||
|
|
||||||
public static VisualElement inspectorContainer;
|
public static VisualElement inspectorContainer;
|
||||||
|
|
||||||
@ -19,20 +20,28 @@ public class NanoBrainEditor : EditorWindow {
|
|||||||
GetWindow<NanoBrainEditor>("NanoBrain Editor");
|
GetWindow<NanoBrainEditor>("NanoBrain Editor");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Open(NanoBrainObj asset) {
|
||||||
|
NanoBrainEditor editor = GetWindow<NanoBrainEditor>("NanoBrain Editor");
|
||||||
|
editor.brain = asset;
|
||||||
|
editor.Show();
|
||||||
|
}
|
||||||
|
|
||||||
GraphBoardView board;
|
GraphBoardView board;
|
||||||
|
|
||||||
private void OnEnable() {
|
private void OnEnable() {
|
||||||
|
OnFocus();
|
||||||
|
}
|
||||||
|
private void OnFocus() {
|
||||||
if (brain == null) {
|
if (brain == null) {
|
||||||
brain = CreateInstance<NanoBrainObj>();
|
// brain = CreateInstance<NanoBrainObj>();
|
||||||
EditorUtility.SetDirty(brain);
|
// EditorUtility.SetDirty(brain);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
VisualElement root = rootVisualElement;
|
VisualElement root = rootVisualElement;
|
||||||
|
root.Clear();
|
||||||
root.styleSheets.Add(Resources.Load<StyleSheet>("GraphStyles"));
|
root.styleSheets.Add(Resources.Load<StyleSheet>("GraphStyles"));
|
||||||
|
|
||||||
root.Add(board);
|
|
||||||
root.Add(inspectorContainer);
|
|
||||||
|
|
||||||
VisualElement main = new() {
|
VisualElement main = new() {
|
||||||
name = "main",
|
name = "main",
|
||||||
style = {
|
style = {
|
||||||
@ -53,11 +62,15 @@ public class NanoBrainEditor : EditorWindow {
|
|||||||
main.Add(inspectorContainer);
|
main.Add(inspectorContainer);
|
||||||
root.Add(main);
|
root.Add(main);
|
||||||
|
|
||||||
board.SetGraph(brain.rootNucleus);
|
board.SetGraph(brain, brain.root);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GraphBoardView : VisualElement {
|
public class GraphBoardView : VisualElement {
|
||||||
|
NanoBrainObj brain;
|
||||||
|
SerializedObject serializedBrain;
|
||||||
Nucleus currentNucleus;
|
Nucleus currentNucleus;
|
||||||
private List<NeuroidLayer> layers = new();
|
private List<NeuroidLayer> layers = new();
|
||||||
private Dictionary<Nucleus, Vector2Int> neuroidPositions = new();
|
private Dictionary<Nucleus, Vector2Int> neuroidPositions = new();
|
||||||
@ -86,7 +99,9 @@ public class GraphBoardView : VisualElement {
|
|||||||
RegisterCallback<MouseUpEvent>(OnMouseUp);
|
RegisterCallback<MouseUpEvent>(OnMouseUp);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetGraph(Nucleus nucleus) {
|
public void SetGraph(NanoBrainObj brain, Nucleus nucleus) {
|
||||||
|
this.brain = brain;
|
||||||
|
this.serializedBrain = new SerializedObject(brain);
|
||||||
this.currentNucleus = nucleus;
|
this.currentNucleus = nucleus;
|
||||||
Rebuild();
|
Rebuild();
|
||||||
}
|
}
|
||||||
@ -101,7 +116,7 @@ public class GraphBoardView : VisualElement {
|
|||||||
|
|
||||||
if (currentWrapper != null)
|
if (currentWrapper != null)
|
||||||
Object.DestroyImmediate(currentWrapper);
|
Object.DestroyImmediate(currentWrapper);
|
||||||
currentWrapper = ScriptableObject.CreateInstance<GraphNodeWrapper>().Init(currentNucleus, NanoBrainEditor.brain);
|
currentWrapper = ScriptableObject.CreateInstance<GraphNodeWrapper>().Init(currentNucleus, brain);
|
||||||
DrawInspector();
|
DrawInspector();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +130,9 @@ public class GraphBoardView : VisualElement {
|
|||||||
return;
|
return;
|
||||||
NeuroidLayer currentLayer = new() { ix = layerIx };
|
NeuroidLayer currentLayer = new() { ix = layerIx };
|
||||||
|
|
||||||
foreach (Nucleus outputNeuroid in selectedNucleus.receivers) {
|
//foreach (Nucleus outputNeuroid in selectedNucleus.receivers) {
|
||||||
|
foreach (Receiver receiver in selectedNucleus.receivers) {
|
||||||
|
Nucleus outputNeuroid = receiver.nucleus;
|
||||||
if (outputNeuroid != null) {
|
if (outputNeuroid != null) {
|
||||||
AddToLayer(currentLayer, outputNeuroid);
|
AddToLayer(currentLayer, outputNeuroid);
|
||||||
// Debug.Log($"layer {layerIx} nucleus {outputNeuroid.name}");
|
// Debug.Log($"layer {layerIx} nucleus {outputNeuroid.name}");
|
||||||
@ -134,7 +151,9 @@ public class GraphBoardView : VisualElement {
|
|||||||
layerIx++;
|
layerIx++;
|
||||||
currentLayer = new() { ix = layerIx };
|
currentLayer = new() { ix = layerIx };
|
||||||
|
|
||||||
foreach (Nucleus input in selectedNucleus.synapses.Keys) {
|
//foreach (Nucleus input in selectedNucleus.synapses.Keys) {
|
||||||
|
foreach (Synapse synapse in selectedNucleus.synapses) {
|
||||||
|
Nucleus input = synapse.nucleus;
|
||||||
AddToLayer(currentLayer, input);
|
AddToLayer(currentLayer, input);
|
||||||
// Debug.Log($"layer {layerIx} nucleus {input.name}");
|
// Debug.Log($"layer {layerIx} nucleus {input.name}");
|
||||||
}
|
}
|
||||||
@ -144,6 +163,8 @@ public class GraphBoardView : VisualElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void AddToLayer(NeuroidLayer layer, Nucleus nucleus) {
|
private void AddToLayer(NeuroidLayer layer, Nucleus nucleus) {
|
||||||
|
if (nucleus == null)
|
||||||
|
return;
|
||||||
layer.neuroids.Add(nucleus);
|
layer.neuroids.Add(nucleus);
|
||||||
nucleus.layerIx = layer.ix;
|
nucleus.layerIx = layer.ix;
|
||||||
// Store its position
|
// Store its position
|
||||||
@ -186,10 +207,13 @@ public class GraphBoardView : VisualElement {
|
|||||||
if (currentNucleus == null)
|
if (currentNucleus == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
serializedBrain.Update();
|
||||||
|
|
||||||
Handles.BeginGUI();
|
Handles.BeginGUI();
|
||||||
foreach (NeuroidLayer layer in layers)
|
foreach (NeuroidLayer layer in layers)
|
||||||
DrawLayer(layer);
|
DrawLayer(layer);
|
||||||
Handles.EndGUI();
|
Handles.EndGUI();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawLayer(NeuroidLayer layer) {
|
private void DrawLayer(NeuroidLayer layer) {
|
||||||
@ -212,19 +236,24 @@ public class GraphBoardView : VisualElement {
|
|||||||
float inputSpacing = 400f / layerNucleus.synapses.Count;
|
float inputSpacing = 400f / layerNucleus.synapses.Count;
|
||||||
float inputMargin = 10 + inputSpacing / 2;
|
float inputMargin = 10 + inputSpacing / 2;
|
||||||
int minStale = 10000;
|
int minStale = 10000;
|
||||||
foreach ((Nucleus nucleus, float weight) in layerNucleus.synapses) {
|
//foreach ((Nucleus nucleus, float weight) in layerNucleus.synapses) {
|
||||||
if (this.neuroidPositions.ContainsKey(nucleus)) {
|
foreach (Synapse synapse in layerNucleus.synapses) {
|
||||||
Vector2Int inputNeuroidPos = this.neuroidPositions[nucleus];
|
Nucleus nucleus = synapse.nucleus;
|
||||||
if (inputNeuroidPos.x == layerNeuroidPos.x + 1) {
|
if (nucleus != null) {
|
||||||
Vector3 pos = new(100 + inputNeuroidPos.x * 100, inputMargin + inputNeuroidPos.y * inputSpacing, 0.0f);
|
float weight = synapse.weight;
|
||||||
|
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);
|
||||||
|
|
||||||
float brightness = weight / 10.0f;
|
float brightness = weight / 10.0f;
|
||||||
Handles.color = new Color(brightness, brightness, brightness);
|
Handles.color = new Color(brightness, brightness, brightness);
|
||||||
Handles.DrawLine(parentPos, pos);
|
Handles.DrawLine(parentPos, pos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (nucleus is Neuroid neuroid && neuroid.stale < minStale)
|
||||||
|
minStale = neuroid.stale;
|
||||||
}
|
}
|
||||||
if (nucleus is Neuroid neuroid && neuroid.stale < minStale)
|
|
||||||
minStale = neuroid.stale;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (layerNucleus.synapses.Count > 0 && minStale > 2 && layerNucleus.stale < 3)
|
if (layerNucleus.synapses.Count > 0 && minStale > 2 && layerNucleus.stale < 3)
|
||||||
@ -255,7 +284,7 @@ public class GraphBoardView : VisualElement {
|
|||||||
// Process Hover
|
// Process Hover
|
||||||
HandleMouseHover(layerNucleus, neuronRect);
|
HandleMouseHover(layerNucleus, neuronRect);
|
||||||
// Process click
|
// Process click
|
||||||
Debug.Log($"{et} {e.type}");
|
// Debug.Log($"{et} {e.type}");
|
||||||
if (e.type == EventType.MouseDown && e.button == 0) {
|
if (e.type == EventType.MouseDown && e.button == 0) {
|
||||||
// Consume the event so the scene doesn't also handle it
|
// Consume the event so the scene doesn't also handle it
|
||||||
e.Use();
|
e.Use();
|
||||||
@ -298,6 +327,9 @@ public class GraphBoardView : VisualElement {
|
|||||||
|
|
||||||
|
|
||||||
void DrawInspector() {
|
void DrawInspector() {
|
||||||
|
if (NanoBrainEditor.inspectorContainer == null)
|
||||||
|
return;
|
||||||
|
|
||||||
NanoBrainEditor.inspectorContainer.Clear();
|
NanoBrainEditor.inspectorContainer.Clear();
|
||||||
if (this.currentNucleus == null)
|
if (this.currentNucleus == null)
|
||||||
return;
|
return;
|
||||||
@ -315,18 +347,20 @@ public class GraphBoardView : VisualElement {
|
|||||||
EditorGUILayout.LabelField("Synapses");
|
EditorGUILayout.LabelField("Synapses");
|
||||||
EditorGUI.indentLevel++;
|
EditorGUI.indentLevel++;
|
||||||
|
|
||||||
List<Nucleus> nuclei = currentNucleus.synapses.Keys.ToList();
|
//List<Nucleus> nuclei = currentNucleus.synapses.Keys.ToList();
|
||||||
foreach (Nucleus nucleus in nuclei) {
|
// foreach (Nucleus nucleus in nuclei) {
|
||||||
EditorGUI.BeginDisabledGroup(nucleus.isSleeping);
|
foreach (Synapse synapse in currentNucleus.synapses) {
|
||||||
|
EditorGUI.BeginDisabledGroup(synapse.nucleus.isSleeping);
|
||||||
|
|
||||||
EditorGUILayout.BeginHorizontal();
|
EditorGUILayout.BeginHorizontal();
|
||||||
EditorGUILayout.LabelField(nucleus.name, GUILayout.Width(120));
|
EditorGUILayout.LabelField(synapse.nucleus.name, GUILayout.Width(120));
|
||||||
EditorGUI.indentLevel--;
|
EditorGUI.indentLevel--;
|
||||||
EditorGUILayout.LabelField("Weight", GUILayout.Width(45));
|
EditorGUILayout.LabelField("Weight", GUILayout.Width(45));
|
||||||
float weight = currentNucleus.synapses[nucleus];
|
// float weight = currentNucleus.synapses[nucleus];
|
||||||
currentNucleus.synapses[nucleus] = EditorGUILayout.FloatField(weight, GUILayout.Width(40));
|
// currentNucleus.synapses[nucleus] = EditorGUILayout.FloatField(weight, GUILayout.Width(40));
|
||||||
|
synapse.weight = EditorGUILayout.FloatField(synapse.weight, GUILayout.Width(40));
|
||||||
EditorGUI.indentLevel++;
|
EditorGUI.indentLevel++;
|
||||||
EditorGUILayout.Vector3Field(GUIContent.none, nucleus.outputValue, GUILayout.Width(180));
|
EditorGUILayout.Vector3Field(GUIContent.none, synapse.nucleus.outputValue, GUILayout.Width(180));
|
||||||
EditorGUILayout.EndHorizontal();
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
EditorGUI.EndDisabledGroup();
|
EditorGUI.EndDisabledGroup();
|
||||||
@ -337,11 +371,12 @@ public class GraphBoardView : VisualElement {
|
|||||||
AddInputNeuron(currentNucleus);
|
AddInputNeuron(currentNucleus);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
NanoBrainEditor.inspectorContainer.Add(container);
|
NanoBrainEditor.inspectorContainer.Add(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void AddInputNeuron(Nucleus receiver) {
|
protected virtual void AddInputNeuron(Nucleus receiver) {
|
||||||
Neuroid newNeuroid = new(NanoBrainEditor.brain, "New neuron");
|
Neuroid newNeuroid = new(brain, "New neuron");
|
||||||
newNeuroid.AddReceiver(receiver);
|
newNeuroid.AddReceiver(receiver);
|
||||||
Rebuild();
|
Rebuild();
|
||||||
}
|
}
|
||||||
@ -363,21 +398,6 @@ public class GraphBoardView : VisualElement {
|
|||||||
return parentPos;
|
return parentPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
// helper to save node position back to graph
|
|
||||||
// public void UpdateNodePosition(NucleusObj node, Vector2 worldPos) {
|
|
||||||
// Undo.RecordObject(graph, "Move Node");
|
|
||||||
// node.position = (worldPos - pan) / zoom;
|
|
||||||
// EditorUtility.SetDirty(graph);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// create & delete APIs called by NodeView or toolbar
|
|
||||||
public void CreateNode(Vector2 worldPos) {
|
|
||||||
// Undo.RecordObject(graph, "Create Node");
|
|
||||||
// var n = new NucleusObj("New Node"); //, position = (worldPos - pan) / zoom };
|
|
||||||
// graph.nodes.Add(n);
|
|
||||||
// EditorUtility.SetDirty(graph);
|
|
||||||
// Rebuild();
|
|
||||||
}
|
|
||||||
|
|
||||||
// public void CreateEdge(string fromId, string toId) {
|
// public void CreateEdge(string fromId, string toId) {
|
||||||
// if (fromId == toId) return;
|
// if (fromId == toId) return;
|
||||||
@ -469,9 +489,23 @@ public class GraphNodeWrapper : ScriptableObject {
|
|||||||
node.name = title;
|
node.name = title;
|
||||||
//node.position = position;
|
//node.position = position;
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
UnityEditor.EditorUtility.SetDirty(graph);
|
if (graph != null)
|
||||||
|
UnityEditor.EditorUtility.SetDirty(graph);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//static class EdgeDragState { public static EdgeDragState active; public GraphNode fromNode; public bool fromIsOutput; }
|
//static class EdgeDragState { public static EdgeDragState active; public GraphNode fromNode; public bool fromIsOutput; }
|
||||||
|
|
||||||
|
public static class OpenAssetHandler {
|
||||||
|
// Called when an asset is double-clicked or opened.
|
||||||
|
[OnOpenAsset]
|
||||||
|
public static bool OpenMyScriptableObject(int instanceID, int line) {
|
||||||
|
NanoBrainObj obj = EditorUtility.InstanceIDToObject(instanceID) as NanoBrainObj;
|
||||||
|
if (obj != null) {
|
||||||
|
NanoBrainEditor.Open(obj);
|
||||||
|
return true; // handled
|
||||||
|
}
|
||||||
|
return false; // let Unity open normally
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
484
Assets/NanoBrain/VisualEditor/Editor/NanoBrainInspector.cs
Normal file
484
Assets/NanoBrain/VisualEditor/Editor/NanoBrainInspector.cs
Normal file
@ -0,0 +1,484 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UIElements;
|
||||||
|
|
||||||
|
[CustomEditor(typeof(NanoBrainObj))]
|
||||||
|
public class NanoBrainInspector : Editor {
|
||||||
|
protected static VisualElement mainContainer;
|
||||||
|
protected static VisualElement inspectorContainer;
|
||||||
|
|
||||||
|
//private Nucleus currentNucleus = null;
|
||||||
|
private List<NeuroidLayer> layers = new();
|
||||||
|
private Dictionary<Nucleus, Vector2Int> neuroidPositions = new();
|
||||||
|
|
||||||
|
protected bool breakOnWake = false;
|
||||||
|
|
||||||
|
#region Start
|
||||||
|
|
||||||
|
public override VisualElement CreateInspectorGUI() {
|
||||||
|
NanoBrainObj brain = target as NanoBrainObj;
|
||||||
|
|
||||||
|
serializedObject.Update();
|
||||||
|
|
||||||
|
VisualElement root = new();
|
||||||
|
root.style.flexDirection = FlexDirection.Column; // side-by-side layout
|
||||||
|
root.style.flexGrow = 1;
|
||||||
|
root.style.minHeight = 600;
|
||||||
|
root.style.paddingLeft = 0;
|
||||||
|
root.style.paddingRight = 0;
|
||||||
|
root.style.paddingTop = 0;
|
||||||
|
root.style.paddingBottom = 0;
|
||||||
|
|
||||||
|
root.styleSheets.Add(Resources.Load<StyleSheet>("GraphStyles"));
|
||||||
|
|
||||||
|
mainContainer = new() {
|
||||||
|
name = "main",
|
||||||
|
style = {
|
||||||
|
flexDirection = FlexDirection.Row,
|
||||||
|
flexGrow = 1,
|
||||||
|
minHeight = 500,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
GraphView board;
|
||||||
|
board = new GraphView();
|
||||||
|
board.style.flexGrow = 1;
|
||||||
|
|
||||||
|
inspectorContainer = new VisualElement {
|
||||||
|
name = "inspector",
|
||||||
|
style = {
|
||||||
|
width = 400,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mainContainer.Add(board);
|
||||||
|
mainContainer.Add(inspectorContainer);
|
||||||
|
root.Add(mainContainer);
|
||||||
|
|
||||||
|
// Run once for initial state (use resolved style width if available)
|
||||||
|
float initialWidth = root.layout.width > 0 ? root.layout.width : root.contentRect.width;
|
||||||
|
UpdateLayout(initialWidth);
|
||||||
|
|
||||||
|
// React to size changes of root (or parent if appropriate)
|
||||||
|
root.RegisterCallback<GeometryChangedEvent>(evt => {
|
||||||
|
UpdateLayout(evt.newRect.width);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (brain != null)
|
||||||
|
board.SetGraph(brain, brain.root, inspectorContainer);
|
||||||
|
else
|
||||||
|
Debug.LogWarning(" No brain!");
|
||||||
|
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GraphView : VisualElement {
|
||||||
|
NanoBrainObj brain;
|
||||||
|
SerializedObject serializedBrain;
|
||||||
|
Nucleus currentNucleus;
|
||||||
|
private List<NeuroidLayer> layers = new();
|
||||||
|
private readonly Dictionary<Nucleus, Vector2Int> neuroidPositions = new();
|
||||||
|
|
||||||
|
Vector2 pan = Vector2.zero;
|
||||||
|
//float zoom = 1f;
|
||||||
|
bool draggingCanvas = false;
|
||||||
|
Vector2 lastMouse;
|
||||||
|
GraphNodeWrapper currentWrapper;
|
||||||
|
|
||||||
|
public GraphView() {
|
||||||
|
name = "content";
|
||||||
|
style.flexGrow = 1;
|
||||||
|
|
||||||
|
IMGUIContainer imguiContainer = new(OnIMGUI);
|
||||||
|
imguiContainer.style.position = Position.Absolute;
|
||||||
|
imguiContainer.style.left = 0; imguiContainer.style.top = 0;
|
||||||
|
imguiContainer.style.right = 0; imguiContainer.style.bottom = 0;
|
||||||
|
imguiContainer.pickingMode = PickingMode.Position;
|
||||||
|
imguiContainer.focusable = true;
|
||||||
|
Add(imguiContainer);
|
||||||
|
|
||||||
|
//RegisterCallback<WheelEvent>(OnWheel);
|
||||||
|
RegisterCallback<MouseDownEvent>(OnMouseDown);
|
||||||
|
RegisterCallback<MouseMoveEvent>(OnMouseMove);
|
||||||
|
RegisterCallback<MouseUpEvent>(OnMouseUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetGraph(NanoBrainObj brain, Nucleus nucleus, VisualElement inspectorContainer) {
|
||||||
|
this.brain = brain;
|
||||||
|
this.serializedBrain = new SerializedObject(brain);
|
||||||
|
this.currentNucleus = nucleus;
|
||||||
|
Rebuild(inspectorContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rebuild(VisualElement inspectorContainer) {
|
||||||
|
BuildLayers();
|
||||||
|
|
||||||
|
if (this.currentNucleus == null) {
|
||||||
|
inspectorContainer.Clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentWrapper != null)
|
||||||
|
DestroyImmediate(currentWrapper);
|
||||||
|
currentWrapper = CreateInstance<GraphNodeWrapper>().Init(this.currentNucleus, brain);
|
||||||
|
DrawInspector(inspectorContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 };
|
||||||
|
|
||||||
|
if (selectedNucleus.receivers != null) {
|
||||||
|
foreach (Receiver receiver in selectedNucleus.receivers) {
|
||||||
|
Nucleus outputNeuroid = receiver.nucleus;
|
||||||
|
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 };
|
||||||
|
|
||||||
|
if (selectedNucleus.synapses != null) {
|
||||||
|
foreach (Synapse synapse in selectedNucleus.synapses) {
|
||||||
|
Nucleus input = synapse.nucleus;
|
||||||
|
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) {
|
||||||
|
if (nucleus == null)
|
||||||
|
return;
|
||||||
|
layer.neuroids.Add(nucleus);
|
||||||
|
nucleus.layerIx = layer.ix;
|
||||||
|
// Store its position
|
||||||
|
Vector2Int neuroidPosition = new(layer.ix, layer.neuroids.Count - 1);
|
||||||
|
neuroidPositions[nucleus] = neuroidPosition;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// basic pan/zoom handling
|
||||||
|
// void OnWheel(WheelEvent e) {
|
||||||
|
// if (e.ctrlKey) {
|
||||||
|
// float delta = -e.delta.y * 0.001f;
|
||||||
|
// zoom = Mathf.Clamp(zoom + delta, 0.25f, 2f);
|
||||||
|
// content.transform.rotation = Quaternion.identity; // keep transform accessible
|
||||||
|
// content.transform.scale = new Vector3(zoom, zoom, 1);
|
||||||
|
// e.StopPropagation();
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// pan += e.delta;
|
||||||
|
// content.style.left = pan.x;
|
||||||
|
// content.style.top = pan.y;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
void OnMouseDown(MouseDownEvent e) {
|
||||||
|
if (e.button == 2) { draggingCanvas = true; lastMouse = e.mousePosition; e.StopPropagation(); }
|
||||||
|
}
|
||||||
|
void OnMouseMove(MouseMoveEvent e) {
|
||||||
|
if (draggingCanvas) {
|
||||||
|
var delta = e.mousePosition - lastMouse;
|
||||||
|
pan += delta;
|
||||||
|
//content.style.left = pan.x;
|
||||||
|
//content.style.top = pan.y;
|
||||||
|
lastMouse = e.mousePosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void OnMouseUp(MouseUpEvent e) { if (e.button == 2) draggingCanvas = false; }
|
||||||
|
|
||||||
|
void OnIMGUI() {
|
||||||
|
if (currentNucleus == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
serializedBrain.Update();
|
||||||
|
|
||||||
|
Handles.BeginGUI();
|
||||||
|
foreach (NeuroidLayer layer in layers)
|
||||||
|
DrawLayer(layer);
|
||||||
|
Handles.EndGUI();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
Vector2Int layerNeuroidPos = this.neuroidPositions[layerNucleus];
|
||||||
|
Vector3 parentPos = new(100 + layerNeuroidPos.x * 100, margin + layerNeuroidPos.y * spacing, 0.1f);
|
||||||
|
|
||||||
|
//int i = 0;
|
||||||
|
float inputSpacing = 400f / layerNucleus.synapses.Count;
|
||||||
|
float inputMargin = 10 + inputSpacing / 2;
|
||||||
|
int minStale = 10000;
|
||||||
|
//foreach ((Nucleus nucleus, float weight) in layerNucleus.synapses) {
|
||||||
|
foreach (Synapse synapse in layerNucleus.synapses) {
|
||||||
|
Nucleus nucleus = synapse.nucleus;
|
||||||
|
if (nucleus != null) {
|
||||||
|
float weight = synapse.weight;
|
||||||
|
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);
|
||||||
|
|
||||||
|
//float brightness = weight / 10.0f;
|
||||||
|
Handles.color = Color.white; //new Color(brightness, brightness, brightness);
|
||||||
|
Handles.DrawLine(parentPos, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nucleus is Neuroid neuroid && neuroid.stale < minStale)
|
||||||
|
minStale = neuroid.stale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (layerNucleus.synapses.Count > 0 && minStale > 2 && layerNucleus.stale < 3)
|
||||||
|
// Debug.LogWarning($"Strange {minStale} is big duing update");
|
||||||
|
|
||||||
|
|
||||||
|
float size = 20;
|
||||||
|
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
|
||||||
|
// Debug.Log($"{et} {e.type}");
|
||||||
|
if (e.type == EventType.MouseDown && e.button == 0) {
|
||||||
|
// Consume the event so the scene doesn't also handle it
|
||||||
|
e.Use();
|
||||||
|
HandleClicked(layerNucleus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleMouseHover(Nucleus 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 HandleClicked(Nucleus nucleus) {
|
||||||
|
this.currentNucleus = nucleus;
|
||||||
|
BuildLayers();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DrawInspector(VisualElement inspectorContainer) {
|
||||||
|
if (inspectorContainer == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
inspectorContainer.Clear();
|
||||||
|
if (this.currentNucleus == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// create a SerializedObject wrapper so Unity inspector controls work (and Undo)
|
||||||
|
SerializedObject so = new(currentWrapper);
|
||||||
|
IMGUIContainer container = new(() => {
|
||||||
|
if (so.targetObject == null)
|
||||||
|
return;
|
||||||
|
so.Update();
|
||||||
|
this.currentNucleus.name = EditorGUILayout.TextField(this.currentNucleus.name);
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
EditorGUILayout.LabelField("Output Value", GUILayout.Width(100));
|
||||||
|
EditorGUILayout.Vector3Field(GUIContent.none, this.currentNucleus.outputValue);
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
if (this.currentNucleus.synapses.Count > 0) {
|
||||||
|
EditorGUILayout.LabelField("Synapses");
|
||||||
|
EditorGUI.indentLevel++;
|
||||||
|
|
||||||
|
foreach (Synapse synapse in this.currentNucleus.synapses) {
|
||||||
|
if (synapse.nucleus != null) {
|
||||||
|
EditorGUI.BeginDisabledGroup(synapse.nucleus.isSleeping);
|
||||||
|
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
EditorGUILayout.LabelField(synapse.nucleus.name, GUILayout.Width(120));
|
||||||
|
EditorGUI.indentLevel--;
|
||||||
|
EditorGUILayout.LabelField("Weight", GUILayout.Width(45));
|
||||||
|
synapse.weight = EditorGUILayout.FloatField(synapse.weight, GUILayout.Width(40));
|
||||||
|
EditorGUI.indentLevel++;
|
||||||
|
EditorGUILayout.Vector3Field(GUIContent.none, synapse.nucleus.outputValue, GUILayout.Width(180));
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUI.EndDisabledGroup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EditorGUI.indentLevel--;
|
||||||
|
}
|
||||||
|
if (GUILayout.Button("Add Neuron"))
|
||||||
|
AddInputNeuron(this.currentNucleus);
|
||||||
|
if (GUILayout.Button("Add Position Perception"))
|
||||||
|
AddPositionPerception(this.currentNucleus);
|
||||||
|
if (GUILayout.Button("Add Velocity Perception"))
|
||||||
|
AddVelocityPerception(this.currentNucleus);
|
||||||
|
if (GUILayout.Button("Delete this neuron"))
|
||||||
|
DeleteNeuron(this.currentNucleus);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
inspectorContainer.Add(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void AddInputNeuron(Nucleus receiver) {
|
||||||
|
Neuroid newNeuroid = new(this.brain, "New neuron");
|
||||||
|
newNeuroid.AddReceiver(receiver);
|
||||||
|
//Rebuild(inspectorContainer);
|
||||||
|
BuildLayers();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void DeleteNeuron(Nucleus nucleus) {
|
||||||
|
this.currentNucleus = nucleus.receivers[0].nucleus;
|
||||||
|
Nucleus.Delete(nucleus);
|
||||||
|
//Rebuild(inspectorContainer);
|
||||||
|
BuildLayers();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void AddPositionPerception(Nucleus receiver) {
|
||||||
|
this.brain.perception.SendPositions(receiver);
|
||||||
|
}
|
||||||
|
protected virtual void AddVelocityPerception(Nucleus receiver) {
|
||||||
|
this.brain.perception.SendVelocities(receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector3 NodePosition(Nucleus nucleus, int layerNodeCount = 1) {
|
||||||
|
if (this.neuroidPositions.ContainsKey(nucleus)) {
|
||||||
|
Vector2Int nucleusPos = this.neuroidPositions[nucleus];
|
||||||
|
return NodePosition(nucleusPos, layerNodeCount);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return Vector3.zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private Vector3 NodePosition(Vector2Int location, int layerNodeCount = 1) {
|
||||||
|
float spacing = 400f / layerNodeCount;
|
||||||
|
float margin = 10 + spacing / 2;
|
||||||
|
float size = 20;
|
||||||
|
Vector3 parentPos = new(100 + location.x * 100 - size, margin + location.y * spacing - size, 0.1f);
|
||||||
|
return parentPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// public void CreateEdge(string fromId, string toId) {
|
||||||
|
// if (fromId == toId) return;
|
||||||
|
// Undo.RecordObject(graph, "Create Edge");
|
||||||
|
// graph.edges.Add(new GraphEdge { fromNodeId = fromId, toNodeId = toId });
|
||||||
|
// EditorUtility.SetDirty(graph);
|
||||||
|
// Rebuild();
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Start
|
||||||
|
|
||||||
|
#region Update
|
||||||
|
|
||||||
|
private void UpdateLayout(float containerWidth) {
|
||||||
|
if (containerWidth > 800f) {
|
||||||
|
mainContainer.style.flexDirection = FlexDirection.Row;
|
||||||
|
inspectorContainer.style.width = 400; // fixed sidebar width
|
||||||
|
inspectorContainer.style.flexGrow = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mainContainer.style.flexDirection = FlexDirection.Column;
|
||||||
|
inspectorContainer.style.width = Length.Percent(100); // full width below
|
||||||
|
inspectorContainer.style.flexDirection = FlexDirection.Column;
|
||||||
|
inspectorContainer.style.flexGrow = 1; // can set 0 or keep as needed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnSceneGUI() {
|
||||||
|
NanoBrain brain = target as NanoBrain;
|
||||||
|
if (brain == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Vector3 position = brain.transform.position;
|
||||||
|
float radius = 1;
|
||||||
|
|
||||||
|
|
||||||
|
Handles.DrawWireDisc(position, Vector3.up, radius); // horizontal circle
|
||||||
|
Handles.DrawWireDisc(position, Vector3.right, radius); // X-plane
|
||||||
|
Handles.DrawWireDisc(position, Vector3.forward, radius); // Z-plane
|
||||||
|
|
||||||
|
|
||||||
|
// Debug.DrawRay(brain.transform.position, Vector3.forward, Color.magenta);
|
||||||
|
// Handles.color = Color.green;
|
||||||
|
// Handles.DrawLine(brain.transform.position, brain.transform.position + Vector3.up);
|
||||||
|
|
||||||
|
// Handles.color = Color.yellow;
|
||||||
|
// Vector3 worldForce = brain.transform.TransformDirection(this.currentNucleus.outputValue);
|
||||||
|
// //Debug.DrawRay(position, worldForce * 10, Color.yellow);
|
||||||
|
// Handles.DrawLine(position, position + worldForce * 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Update
|
||||||
|
}
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c96ad47c3d4498640b52630789e38573
|
||||||
15
Assets/NanoBrain/VisualEditor/NanoBrainComponent.cs
Normal file
15
Assets/NanoBrain/VisualEditor/NanoBrainComponent.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class NanoBrainComponent : MonoBehaviour {
|
||||||
|
public NanoBrainObj brain;
|
||||||
|
public Nucleus root {
|
||||||
|
get {
|
||||||
|
return brain.root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public Perception perception {
|
||||||
|
get {
|
||||||
|
return brain.perception;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/NanoBrain/VisualEditor/NanoBrainComponent.cs.meta
Normal file
2
Assets/NanoBrain/VisualEditor/NanoBrainComponent.cs.meta
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 92f34a5e4027a1dc39efd8ce63cf6aba
|
||||||
@ -1,22 +1,49 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
public class NanoBrainObj : ScriptableObject {
|
[CreateAssetMenu(menuName = "Passer/NanoBrain")]
|
||||||
public Nucleus rootNucleus = new("root");
|
public class NanoBrainObj : ScriptableObject, ISerializationCallbackReceiver {
|
||||||
|
|
||||||
public List<Neuroid> neuroids = new();
|
public string title;
|
||||||
|
public int count;
|
||||||
|
public Color color = Color.white;
|
||||||
|
public Texture2D texture;
|
||||||
|
|
||||||
|
public List<Nucleus> nuclei = new();
|
||||||
|
|
||||||
|
// This is probably always the first element in the nuclei list...
|
||||||
|
[System.NonSerialized]
|
||||||
|
public Nucleus root;
|
||||||
|
public int rootId;
|
||||||
|
|
||||||
|
public Perception perception;
|
||||||
|
|
||||||
|
public NanoBrainObj() {
|
||||||
|
this.root = new(this, "Root");
|
||||||
|
this.perception = new Perception(this);
|
||||||
|
}
|
||||||
|
|
||||||
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 UpdateNeurons() {
|
public void UpdateNuclei() {
|
||||||
foreach (Neuroid neuroid in neuroids) {
|
foreach (Neuroid neuroid in nuclei) {
|
||||||
neuroid.stale++;
|
neuroid.stale++;
|
||||||
if (neuroid.isSleeping)
|
if (neuroid.isSleeping)
|
||||||
neuroid.outputValue = Vector3.zero;
|
neuroid.outputValue = Vector3.zero;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnBeforeSerialize() {
|
||||||
|
this.rootId = root.id;
|
||||||
|
}
|
||||||
|
public void OnAfterDeserialize() {
|
||||||
|
foreach (Nucleus nucleus in nuclei) {
|
||||||
|
if (this.rootId == nucleus.id)
|
||||||
|
this.root = nucleus;
|
||||||
|
nucleus.Rebuild(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,17 +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: 95e66c6366d904e98bc83428217d4fd7, type: 3}
|
|
||||||
m_Name: Boid Graph
|
|
||||||
m_EditorClassIdentifier: Unity.VisualScripting.Flow::Unity.VisualScripting.ScriptGraphAsset
|
|
||||||
_data:
|
|
||||||
_json: '{"graph":{"variables":{"Kind":"Flow","collection":{"$content":[],"$version":"A"},"$version":"A"},"controlInputDefinitions":[],"controlOutputDefinitions":[],"valueInputDefinitions":[],"valueOutputDefinitions":[],"title":null,"summary":null,"pan":{"x":0.0,"y":0.0},"zoom":1.0,"elements":[],"$version":"A"}}'
|
|
||||||
_objectReferences: []
|
|
||||||
28
Assets/Scenes/Boids/New Nano Brain Obj.asset
Normal file
28
Assets/Scenes/Boids/New Nano Brain Obj.asset
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
%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: 36081359186edfec998d891a1feeb17b, type: 3}
|
||||||
|
m_Name: New Nano Brain Obj
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::NanoBrainObj
|
||||||
|
title:
|
||||||
|
count: 0
|
||||||
|
color: {r: 1, g: 1, b: 1, a: 1}
|
||||||
|
texture: {fileID: 0}
|
||||||
|
nuclei:
|
||||||
|
- id: 257807948
|
||||||
|
_name: Root
|
||||||
|
synapses: []
|
||||||
|
receivers: []
|
||||||
|
- id: -1868865374
|
||||||
|
_name: Perception
|
||||||
|
synapses: []
|
||||||
|
receivers: []
|
||||||
|
rootId: 257807948
|
||||||
@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 1a94b234cb4edce40bbe7e748f0508b3
|
guid: 55099766f6f09071ab4e8c89b02fa302
|
||||||
NativeFormatImporter:
|
NativeFormatImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
mainObjectFileID: 11400000
|
mainObjectFileID: 11400000
|
||||||
@ -123,6 +123,7 @@ GameObject:
|
|||||||
m_Component:
|
m_Component:
|
||||||
- component: {fileID: 8702527964058765412}
|
- component: {fileID: 8702527964058765412}
|
||||||
- component: {fileID: 9169912378811971808}
|
- component: {fileID: 9169912378811971808}
|
||||||
|
- component: {fileID: 85370558829139006}
|
||||||
m_Layer: 0
|
m_Layer: 0
|
||||||
m_Name: Boid
|
m_Name: Boid
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
@ -158,16 +159,21 @@ MonoBehaviour:
|
|||||||
m_Script: {fileID: 11500000, guid: fe92e8a3728a1e444a25b79acf6b1d00, type: 3}
|
m_Script: {fileID: 11500000, guid: fe92e8a3728a1e444a25b79acf6b1d00, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier:
|
m_EditorClassIdentifier:
|
||||||
speed: 0.2
|
|
||||||
neighbourCount: 0
|
|
||||||
inertia: 0
|
|
||||||
alignmentForce: 1
|
|
||||||
cohesionForce: 1
|
|
||||||
separationForce: 1
|
|
||||||
separationDistance: 0.5
|
|
||||||
bodyForce: 1
|
|
||||||
debug: 0
|
|
||||||
sc: {fileID: 0}
|
sc: {fileID: 0}
|
||||||
velocity: {x: 0, y: 0, z: 0}
|
velocity: {x: 0, y: 0, z: 0}
|
||||||
acceleration: {x: 0, y: 0, z: 0}
|
acceleration: {x: 0, y: 0, z: 0}
|
||||||
|
nanoBrain: {fileID: 0}
|
||||||
id: 0
|
id: 0
|
||||||
|
--- !u!114 &85370558829139006
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 8702527964058765413}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 92f34a5e4027a1dc39efd8ce63cf6aba, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::NanoBrainComponent
|
||||||
|
brain: {fileID: 11400000, guid: 55099766f6f09071ab4e8c89b02fa302, type: 2}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
[RequireComponent(typeof(NanoBrain))]
|
//[RequireComponent(typeof(NanoBrain))]
|
||||||
|
[RequireComponent(typeof(NanoBrainComponent))]
|
||||||
public class Boid : MonoBehaviour {
|
public class Boid : MonoBehaviour {
|
||||||
public static int BoundaryType = 1;
|
public static int BoundaryType = 1;
|
||||||
public static int BoidType = 2;
|
public static int BoidType = 2;
|
||||||
@ -11,17 +12,18 @@ public class Boid : MonoBehaviour {
|
|||||||
|
|
||||||
private Bounds innerBounds;
|
private Bounds innerBounds;
|
||||||
|
|
||||||
public NanoBrain neuroidNet;
|
public NanoBrainComponent nanoBrain;
|
||||||
public Perception perception;
|
|
||||||
|
|
||||||
public Nucleus behaviour;
|
// public NanoBrain neuroidNet;
|
||||||
|
// public Perception perception;
|
||||||
|
|
||||||
public Neuroid totalForce;
|
// public Nucleus behaviour;
|
||||||
|
|
||||||
|
// public Neuroid totalForce;
|
||||||
|
|
||||||
public int id;
|
public int id;
|
||||||
|
|
||||||
void Awake() {
|
void Awake() {
|
||||||
neuroidNet = GetComponent<NanoBrain>();
|
|
||||||
|
|
||||||
this.id = this.GetInstanceID();
|
this.id = this.GetInstanceID();
|
||||||
|
|
||||||
@ -29,13 +31,14 @@ public class Boid : MonoBehaviour {
|
|||||||
|
|
||||||
innerBounds = new(sc.transform.position, sc.spaceSize - 2 * sc.boundaryWidth);
|
innerBounds = new(sc.transform.position, sc.spaceSize - 2 * sc.boundaryWidth);
|
||||||
|
|
||||||
perception = new Perception(neuroidNet);
|
// neuroidNet = GetComponent<NanoBrain>();
|
||||||
|
// perception = new Perception(neuroidNet);
|
||||||
|
|
||||||
//behaviour = new Roaming(neuroidNet, perception, sc);
|
// //behaviour = new Roaming(neuroidNet, perception, sc);
|
||||||
behaviour = new Swarming(neuroidNet, perception, sc);
|
// behaviour = new Swarming(neuroidNet, perception, sc);
|
||||||
|
|
||||||
totalForce = new(neuroidNet, "Total");
|
// totalForce = new(neuroidNet, "Total");
|
||||||
behaviour.AddReceiver(totalForce);
|
// behaviour.AddReceiver(totalForce);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Update() {
|
void Update() {
|
||||||
@ -50,7 +53,7 @@ public class Boid : MonoBehaviour {
|
|||||||
//Debug.DrawRay(this.transform.position, this.transform.TransformDirection(localPosition), Color.magenta);
|
//Debug.DrawRay(this.transform.position, this.transform.TransformDirection(localPosition), Color.magenta);
|
||||||
|
|
||||||
int thingId = neighbour.GetInstanceID();
|
int thingId = neighbour.GetInstanceID();
|
||||||
perception.ProcessStimulus(thingId, BoidType, localPosition, neighbour.gameObject.name);
|
nanoBrain.perception.ProcessStimulus(thingId, BoidType, localPosition, neighbour.gameObject.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,10 +62,11 @@ public class Boid : MonoBehaviour {
|
|||||||
Vector3 pointOnBounds = innerBounds.ClosestPoint(point);
|
Vector3 pointOnBounds = innerBounds.ClosestPoint(point);
|
||||||
Vector3 desiredWorldSpace = (pointOnBounds - point).normalized * sc.speed;
|
Vector3 desiredWorldSpace = (pointOnBounds - point).normalized * sc.speed;
|
||||||
Vector3 desiredLocalSpace = -this.transform.InverseTransformPoint(desiredWorldSpace);
|
Vector3 desiredLocalSpace = -this.transform.InverseTransformPoint(desiredWorldSpace);
|
||||||
perception.ProcessStimulus(777, BoundaryType, desiredLocalSpace, "Boundary");
|
nanoBrain.perception.ProcessStimulus(777, BoundaryType, desiredLocalSpace, "Boundary");
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 worldForce = this.transform.TransformDirection(behaviour.outputValue);
|
//Vector3 worldForce = this.transform.TransformDirection(behaviour.outputValue);
|
||||||
|
Vector3 worldForce = this.transform.TransformDirection(nanoBrain.root.outputValue);
|
||||||
|
|
||||||
this.velocity = (1 - sc.inertia) * (worldForce * Time.deltaTime) + sc.inertia * velocity;
|
this.velocity = (1 - sc.inertia) * (worldForce * Time.deltaTime) + sc.inertia * velocity;
|
||||||
if (this.velocity.magnitude > 0)
|
if (this.velocity.magnitude > 0)
|
||||||
@ -78,7 +82,8 @@ public class Boid : MonoBehaviour {
|
|||||||
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 2f); // Adjust the speed of rotation
|
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 2f); // Adjust the speed of rotation
|
||||||
}
|
}
|
||||||
|
|
||||||
neuroidNet.UpdateNeurons();
|
//neuroidNet.UpdateNeurons();
|
||||||
|
nanoBrain.brain.UpdateNuclei();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ public class Roaming : Nucleus {
|
|||||||
|
|
||||||
public Neuroid output;
|
public Neuroid output;
|
||||||
|
|
||||||
public Roaming(NanoBrain neuroidNet, Perception perception, SwarmControl sc) : base("Roaming nucleus") {
|
public Roaming(NanoBrain neuroidNet, Perception perception, SwarmControl sc) : base(null, "Roaming nucleus") {
|
||||||
avoidance = new(neuroidNet, "Avoidance") { inverse = true };
|
avoidance = new(neuroidNet, "Avoidance") { inverse = true };
|
||||||
perception.SendPositions(avoidance, Boid.BoundaryType);
|
perception.SendPositions(avoidance, Boid.BoundaryType);
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ public class Swarming : Nucleus {
|
|||||||
|
|
||||||
public override Vector3 outputValue { get => output.outputValue; set => output.outputValue = value; }
|
public override Vector3 outputValue { get => output.outputValue; set => output.outputValue = value; }
|
||||||
|
|
||||||
public Swarming(NanoBrain neuroidNet, Perception perception, SwarmControl sc) : base("Swarming Nucleus") {
|
public Swarming(NanoBrain neuroidNet, Perception perception, SwarmControl sc) : base(null, "Swarming Nucleus") {
|
||||||
this.cohesion = new(neuroidNet, "Cohesion") { inverse = false };
|
this.cohesion = new(neuroidNet, "Cohesion") { inverse = false };
|
||||||
perception.SendPositions(this.cohesion, Boid.BoidType);
|
perception.SendPositions(this.cohesion, Boid.BoidType);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user