Scriptable Object nanobrain editor

This commit is contained in:
Pascal Serrarens 2025-12-04 12:55:55 +01:00
parent 69c6b1e2b7
commit 5a9aa7161b
24 changed files with 607 additions and 1132 deletions

View File

@ -48,6 +48,7 @@
<Analyzer Include="/home/pascal/Unity/Hub/Editor/6000.2.13f1/Editor/Data/Tools/Unity.SourceGenerators/Unity.UIToolkit.SourceGenerator.dll" />
</ItemGroup>
<ItemGroup>
<Compile Include="Assets/NanoBrain/VisualEditor/Editor/NanoBrainEditor.cs" />
<Compile Include="Assets/NanoBrain/Editor/NanoBrain_Editor.cs" />
<Compile Include="Assets/NanoBrain/Editor/NeuroidWindow.cs" />
</ItemGroup>

View File

@ -48,6 +48,7 @@
<Analyzer Include="/home/pascal/Unity/Hub/Editor/6000.2.13f1/Editor/Data/Tools/Unity.SourceGenerators/Unity.UIToolkit.SourceGenerator.dll" />
</ItemGroup>
<ItemGroup>
<Compile Include="Assets/NanoBrain/VisualEditor/NanoBrainObj.cs" />
<Compile Include="Assets/Scenes/Boids/Scripts/SwarmSpawner.cs" />
<Compile Include="Assets/NanoBrain/NeuroidBehaviour.cs" />
<Compile Include="Assets/NanoBrain/NanoBrain.cs" />
@ -57,9 +58,14 @@
<Compile Include="Assets/NanoBrain/Perception.cs" />
<Compile Include="Assets/Scenes/Boids/Scripts/SwarmingNucleus.cs" />
<Compile Include="Assets/Scenes/Boids/Scripts/Boid.cs" />
<Compile Include="Assets/NanoBrain/VisualEditor/NucleusObject.cs" />
<Compile Include="Assets/NanoBrain/Neuroid.cs" />
<Compile Include="Assets/NanoBrain/VisualEditor/NeuroidObject.cs" />
<Compile Include="Assets/NanoBrain/Nucleus.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Assets/NanoBrain/VisualEditor/Resources/GraphStyles.uss" />
</ItemGroup>
<ItemGroup>
<Reference Include="UnityEngine">
<HintPath>/home/pascal/Unity/Hub/Editor/6000.2.13f1/Editor/Data/Managed/UnityEngine/UnityEngine.dll</HintPath>

View File

@ -13,6 +13,15 @@ public class NanoBrain_Editor : Editor {
#region Start
private void OnEnable() {
// NanoBrain brain = target as NanoBrain;
// if (brain == null)
// return;
// if (brain.rootNucleus == null) {
// brain.rootNucleus = CreateInstance<NucleusObj>();
// EditorUtility.SetDirty(brain.rootNucleus);
// }
SelectNeuron();
}
@ -44,7 +53,6 @@ public class NanoBrain_Editor : Editor {
DrawGraph();
//DrawDefaultInspector();
EditorGUILayout.TextField("Name", currentNucleus.name);
EditorGUILayout.Vector3Field("Output Value", currentNucleus.outputValue);
if (currentNucleus.synapses.Count > 0) {
@ -62,6 +70,7 @@ public class NanoBrain_Editor : Editor {
}
EditorGUI.indentLevel--;
}
DrawDefaultInspector();
}
private void BuildLayers() {

View File

@ -40,7 +40,7 @@ public class GraphEditorWindow : EditorWindow {
return;
NeuroidLayer currentLayer = new() { ix = layerIx };
foreach (Neuroid outputNeuroid in selectedNucleus.receivers) {
foreach (Nucleus outputNeuroid in selectedNucleus.receivers) {
if (outputNeuroid != null) {
AddToLayer(currentLayer, outputNeuroid);
Debug.Log($"layer {layerIx} nucleus {outputNeuroid.name}");

View File

@ -2,6 +2,9 @@ using System.Collections.Generic;
using UnityEngine;
public class NanoBrain : MonoBehaviour {
// public NucleusObj rootNucleus;
public List<Neuroid> neuroids = new();
public Neuroid AddNeuron(string name) {

View File

@ -13,6 +13,14 @@ public class Neuroid : Nucleus {
Debug.LogError("No neuroid network");
}
public Neuroid(NanoBrainObj brain, string name) : base(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) {
this.synapses[input] = weight;
}

View File

@ -5,11 +5,12 @@ public class Nucleus {
public int stale = 0;
public NanoBrain brain { get; protected set; }
public NanoBrainObj newBrain { get; protected set; }
public virtual string name { get; set; }
public readonly Dictionary<Nucleus, float> synapses = new();
public HashSet<Neuroid> receivers = new();
public HashSet<Nucleus> receivers = new();
public virtual Vector3 outputValue { get; set; }
public int layerIx;
@ -18,7 +19,7 @@ public class Nucleus {
this.name = name;
}
public virtual void AddReceiver(Neuroid 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(receiver);
receiver.synapses[this] = 1.0f; // new(this);

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 895037c7e323e03ada7f43d011f1390b
guid: 62a58c801eda0c9eab7a49fb1d0840cb
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@ -1,5 +1,6 @@
fileFormatVersion: 2
guid: e5398245724c5668992c64c27db35040
guid: e47ea55fc051fcdcb8ae6197d1105cc0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:

View File

@ -0,0 +1,477 @@
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using System.Linq;
using System.Collections.Generic;
public class NucleusLayer {
public int ix = 0;
public List<Nucleus> neuroids = new();
}
public class NanoBrainEditor : EditorWindow {
public static NanoBrainObj brain;
public static VisualElement inspectorContainer;
[MenuItem("Window/NanoBrain Editor")]
public static void ShowWindow() {
GetWindow<NanoBrainEditor>("NanoBrain Editor");
}
GraphBoardView board;
private void OnEnable() {
if (brain == null) {
brain = CreateInstance<NanoBrainObj>();
EditorUtility.SetDirty(brain);
}
VisualElement root = rootVisualElement;
root.styleSheets.Add(Resources.Load<StyleSheet>("GraphStyles"));
root.Add(board);
root.Add(inspectorContainer);
VisualElement main = new() {
name = "main",
style = {
flexDirection = FlexDirection.Row,
flexGrow = 1
}
};
board = new GraphBoardView();
board.style.flexGrow = 1;
inspectorContainer = new VisualElement {
name = "inspector",
style = {
width = 400
}
};
main.Add(board);
main.Add(inspectorContainer);
root.Add(main);
board.SetGraph(brain.rootNucleus);
}
}
public class GraphBoardView : VisualElement {
Nucleus currentNucleus;
private List<NeuroidLayer> layers = new();
private Dictionary<Nucleus, Vector2Int> neuroidPositions = new();
Vector2 pan = Vector2.zero;
//float zoom = 1f;
bool draggingCanvas = false;
Vector2 lastMouse;
GraphNodeWrapper currentWrapper;
public GraphBoardView() {
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(Nucleus nucleus) {
this.currentNucleus = nucleus;
Rebuild();
}
void Rebuild() {
BuildLayers();
if (currentNucleus == null) {
NanoBrainEditor.inspectorContainer.Clear();
return;
}
if (currentWrapper != null)
Object.DestroyImmediate(currentWrapper);
currentWrapper = ScriptableObject.CreateInstance<GraphNodeWrapper>().Init(currentNucleus, NanoBrainEditor.brain);
DrawInspector();
}
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 (Nucleus outputNeuroid in selectedNucleus.receivers) {
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;
}
// 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;
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) {
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 = 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();
HandleDiscClicked(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 HandleDiscClicked(Nucleus nucleus) {
this.currentNucleus = nucleus;
BuildLayers();
}
void DrawInspector() {
NanoBrainEditor.inspectorContainer.Clear();
if (this.currentNucleus == null)
return;
// create a SerializedObject wrapper so Unity inspector controls work (and Undo)
SerializedObject so = new SerializedObject(currentWrapper);
IMGUIContainer container = new IMGUIContainer(() => {
so.Update();
currentNucleus.name = EditorGUILayout.TextField(currentNucleus.name);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Output Value", GUILayout.Width(100));
EditorGUILayout.Vector3Field(GUIContent.none, currentNucleus.outputValue);
EditorGUILayout.EndHorizontal();
if (currentNucleus.synapses.Count > 0) {
EditorGUILayout.LabelField("Synapses");
EditorGUI.indentLevel++;
List<Nucleus> nuclei = currentNucleus.synapses.Keys.ToList();
foreach (Nucleus nucleus in nuclei) {
EditorGUI.BeginDisabledGroup(nucleus.isSleeping);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(nucleus.name, GUILayout.Width(120));
EditorGUI.indentLevel--;
EditorGUILayout.LabelField("Weight", GUILayout.Width(45));
float weight = currentNucleus.synapses[nucleus];
currentNucleus.synapses[nucleus] = EditorGUILayout.FloatField(weight, GUILayout.Width(40));
EditorGUI.indentLevel++;
EditorGUILayout.Vector3Field(GUIContent.none, nucleus.outputValue, GUILayout.Width(180));
EditorGUILayout.EndHorizontal();
EditorGUI.EndDisabledGroup();
}
EditorGUI.indentLevel--;
}
if (GUILayout.Button("Add Neuron"))
AddInputNeuron(currentNucleus);
});
NanoBrainEditor.inspectorContainer.Add(container);
}
protected virtual void AddInputNeuron(Nucleus receiver) {
Neuroid newNeuroid = new(NanoBrainEditor.brain, "New neuron");
newNeuroid.AddReceiver(receiver);
Rebuild();
}
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;
}
// 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) {
// if (fromId == toId) return;
// Undo.RecordObject(graph, "Create Edge");
// graph.edges.Add(new GraphEdge { fromNodeId = fromId, toNodeId = toId });
// EditorUtility.SetDirty(graph);
// Rebuild();
// }
}
/*
public class NodeView : VisualElement {
Nucleus data;
GraphBoardView board;
Label titleLabel;
//bool dragging = false;
Vector2 localDragStart;
public NodeView(Nucleus node, GraphBoardView boardView) {
data = node;
board = boardView;
name = "node";
style.width = 20; //node.size.x;
style.height = 20; //node.size.y;
titleLabel = new Label(node.name) { name = "title" };
Add(titleLabel);
// ports
// var outPort = new Button(() => StartEdgeDrag(true)) { text = "◀", name = "out" };
// var inPort = new Button(() => StartEdgeDrag(false)) { text = "▶", name = "in" };
// Add(outPort);
// Add(inPort);
RegisterCallback<MouseDownEvent>(OnMouseDown);
// RegisterCallback<MouseMoveEvent>(OnMouseMove);
RegisterCallback<MouseUpEvent>(OnMouseUp);
//RegisterCallback<MouseLeaveEvent>(e => dragging = false);
}
// void StartEdgeDrag(bool isOutput) {
// // simplified: on first click store source; on second click on target port call board.CreateEdge
// if (EdgeDragState.active == null) EdgeDragState.active = new EdgeDragState { fromNode = data, fromIsOutput = isOutput };
// else {
// var src = EdgeDragState.active.fromNode;
// if (src != null && src.id != data.id) board.CreateEdge(src.id, data.id);
// EdgeDragState.active = null;
// }
// }
void OnMouseDown(MouseDownEvent e) {
if (e.button == 0 && e.target == this) {
//dragging = true;
localDragStart = e.mousePosition;
e.StopPropagation();
}
}
// void OnMouseMove(MouseMoveEvent e) {
// if (!dragging) return;
// var delta = e.mousePosition - localDragStart;
// var worldPos = new Vector2(layout.x + delta.x, layout.y + delta.y);
// style.left = worldPos.x;
// style.top = worldPos.y;
// // commit on every move
// board.UpdateNodePosition(data, worldPos);
// }
void OnMouseUp(MouseUpEvent e) {
//dragging = false;
}
}
*/
public class GraphNodeWrapper : ScriptableObject {
// expose fields that map to GraphNode
public string title;
public Vector2 position;
Nucleus node;
NanoBrainObj graph; // needed to write back and mark dirty
public GraphNodeWrapper Init(Nucleus node, NanoBrainObj graphAsset) {
this.node = node;
this.graph = graphAsset;
this.title = " A " + node.name;
//position = node.position;
return this;
}
void OnValidate() {
if (node != null) {
node.name = title;
//node.position = position;
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(graph);
#endif
}
}
}
//static class EdgeDragState { public static EdgeDragState active; public GraphNode fromNode; public bool fromIsOutput; }

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c57f78e25f0e55b96a50fd5592b26317

View File

@ -0,0 +1,22 @@
using System.Collections.Generic;
using UnityEngine;
public class NanoBrainObj : ScriptableObject {
public Nucleus rootNucleus = new("root");
public List<Neuroid> neuroids = new();
public Neuroid AddNeuron(string name) {
Neuroid neuroid = new(this, name);
return neuroid;
}
public void UpdateNeurons() {
foreach (Neuroid neuroid in neuroids) {
neuroid.stale++;
if (neuroid.isSleeping)
neuroid.outputValue = Vector3.zero;
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 36081359186edfec998d891a1feeb17b

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d14e756f390f41a1c9923a0015329389

View File

@ -0,0 +1,28 @@
// using System.Collections.Generic;
// using UnityEngine;
// public class NucleusObj : ScriptableObject {
// public virtual string objName { get; set; }
// public readonly Dictionary<NucleusObj, float> synapses = new();
// public HashSet<NucleusObj> receivers = new();
// public virtual Vector3 outputValue { get; set; }
// public int stale = 0;
// public NucleusObj(string name) {
// this.objName = name;
// }
// public virtual void AddReceiver(NucleusObj receiver) {
// this.receivers.Add(receiver);
// receiver.synapses[this] = 1.0f;
// }
// public bool isSleeping {
// get {
// return this.stale > 2;
// }
// }
// }

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: ca2a41cdae50b5005b5cf10cebb28de6

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7b61a93fc9332d2adae74fe4abe92d53
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,15 @@
#main {
}
#content {
background-color: #2b2b2b,
position: absolute;
}
#inspector {
border-left-width: 1px;
border-left-color: #000;
border-left_style: solid;
padding: 3px;
}
.node { background-color: #222; border-radius:4px; padding:6px; }
#title { unity-font-style: bold; color: white; }

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 28268b644fa8f3948851b25e41f5b03b
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
disableValidation: 0

View File

@ -11,7 +11,7 @@ public class Roaming : Nucleus {
output.GetInputFrom(avoidance, -sc.avoidanceForce);
}
public override void AddReceiver(Neuroid receiver) {
public override void AddReceiver(Nucleus receiver) {
output.AddReceiver(receiver);
}
}

View File

@ -30,7 +30,7 @@ public class Swarming : Nucleus {
this.output.GetInputFrom(boundary, -sc.avoidanceForce);
}
public override void AddReceiver(Neuroid receiver) {
public override void AddReceiver(Nucleus receiver) {
this.output.AddReceiver(receiver);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
<Solution>
<Project Path="Assembly-CSharp.csproj" />
<Project Path="Assembly-CSharp-Editor.csproj" />
<Project Path="Assembly-CSharp.csproj" />
</Solution>