diff --git a/Assembly-CSharp.csproj b/Assembly-CSharp.csproj
index 97530fd..526dc5f 100644
--- a/Assembly-CSharp.csproj
+++ b/Assembly-CSharp.csproj
@@ -55,6 +55,7 @@
+
diff --git a/Assets/NanoBrain/Neuroid.cs b/Assets/NanoBrain/Neuroid.cs
index 09c5606..2eb26d8 100644
--- a/Assets/NanoBrain/Neuroid.cs
+++ b/Assets/NanoBrain/Neuroid.cs
@@ -102,6 +102,16 @@ public class Neuroid {
UpdateState();
}
+ public void RemoveInputFrom(Neuroid input) {
+ this.synapses.Remove(input);
+ if (this.synapses.Count == 0) {
+ // In case this was the last synapse, we reset the output because in this case no updates from synapses will follow.
+ this.outputValue = Vector3.zero;
+ foreach (Neuroid neuroid in this.outputNeuroids)
+ neuroid.SetInput(this, this.outputValue);
+ }
+ }
+
// public readonly Dictionary fakeNeuroids = new();
// public void SetInput(int thingId, Vector3 value, float weight, NeuroidNetwork net) {
// if (fakeNeuroids.ContainsKey(thingId)) {
@@ -131,7 +141,7 @@ public class Neuroid {
result /= this.synapses.Count;
this.outputValue = result;
- foreach (Neuroid neuroid in outputNeuroids)
+ foreach (Neuroid neuroid in this.outputNeuroids)
neuroid.SetInput(this, this.outputValue);
this.stale = 0;
}
diff --git a/Assets/NanoBrain/Nucleus.cs b/Assets/NanoBrain/Nucleus.cs
new file mode 100644
index 0000000..e3a398d
--- /dev/null
+++ b/Assets/NanoBrain/Nucleus.cs
@@ -0,0 +1,7 @@
+public class Nucleus {
+ public class State {
+
+ }
+
+ public State state;
+}
\ No newline at end of file
diff --git a/Assets/NanoBrain/Nucleus.cs.meta b/Assets/NanoBrain/Nucleus.cs.meta
new file mode 100644
index 0000000..e520090
--- /dev/null
+++ b/Assets/NanoBrain/Nucleus.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 750748f3f0e7d472fbf88ab02987074c
\ No newline at end of file
diff --git a/Assets/NanoBrain/Perception.cs b/Assets/NanoBrain/Perception.cs
index 99729c3..b83b402 100644
--- a/Assets/NanoBrain/Perception.cs
+++ b/Assets/NanoBrain/Perception.cs
@@ -1,13 +1,19 @@
using System.Collections.Generic;
using UnityEngine;
-public class Perception {
+
+public class Perception : Nucleus {
public SensoryNeuroid[] sensoryNeuroids = new SensoryNeuroid[7];
public NeuroidNetwork neuroidNet { get; protected set; }
- public HashSet positionReceivers { get; protected set; }
- public HashSet velocityReceivers { get; protected set; }
+ public class Receiver {
+ public int thingType = 0;
+ public Neuroid neuroid;
+ }
+
+ public HashSet positionReceivers { get; protected set; }
+ public HashSet velocityReceivers { get; protected set; }
public Perception(NeuroidNetwork neuroidNet) {
this.neuroidNet = neuroidNet;
@@ -15,26 +21,34 @@ public class Perception {
this.velocityReceivers = new();
}
- public void SendPositions(Neuroid receiver, float weight = 1.0f) {
+ public void SendPositions(Neuroid receivingNeuroid, float weight = 1.0f, int thingType = 0) {
+ Receiver receiver = new() {
+ thingType = thingType,
+ neuroid = receivingNeuroid
+ };
positionReceivers.Add(receiver);
foreach (SensoryNeuroid neuroid in sensoryNeuroids) {
if (neuroid != null) {
- neuroid.AddReceiver(receiver);
- receiver.synapses[neuroid] = new (neuroid, Vector3.zero, weight);
+ neuroid.AddReceiver(receivingNeuroid);
+ receivingNeuroid.synapses[neuroid] = new(neuroid, Vector3.zero, weight);
}
}
}
- public void SendVelocities(Neuroid receiver) {
+ public void SendVelocities(Neuroid receivingNeuroid, float weight = 1.0f, int thingType = 0) {
+ Receiver receiver = new() {
+ thingType = thingType,
+ neuroid = receivingNeuroid
+ };
velocityReceivers.Add(receiver);
foreach (SensoryNeuroid neuroid in sensoryNeuroids) {
if (neuroid != null && neuroid.velocityNeuroid != null) {
- neuroid.velocityNeuroid.AddReceiver(receiver);
- receiver.synapses[neuroid] = new (neuroid, Vector3.zero, 1.0f);
+ neuroid.velocityNeuroid.AddReceiver(receivingNeuroid);
+ receivingNeuroid.synapses[neuroid] = new(neuroid, Vector3.zero, 1.0f);
}
}
}
- public void ProcessStimulus(int thingId, Vector3 localPosition) {
+ public void ProcessStimulus(int thingId, int thingType, Vector3 localPosition) {
int availableIx = -1;
SensoryNeuroid leastInterestingNeuroid = null;
for (int i = 0; i < sensoryNeuroids.Length; i++) {
@@ -58,10 +72,14 @@ public class Perception {
else {
// Debug.Log($"new receptor for {thingId}");
SensoryNeuroid neuroid = new(neuroidNet, thingId);
- foreach (Neuroid receiver in positionReceivers)
- receiver.GetInputFrom(neuroid);
- foreach (Neuroid receiver in velocityReceivers)
- receiver.GetInputFrom(neuroid.velocityNeuroid);
+ foreach (Receiver receiver in positionReceivers) {
+ if (receiver.thingType == 0 || receiver.thingType == thingType)
+ receiver.neuroid.GetInputFrom(neuroid);
+ }
+ foreach (Receiver receiver in velocityReceivers) {
+ if (receiver.thingType == 0 || receiver.thingType == thingType)
+ receiver.neuroid.GetInputFrom(neuroid.velocityNeuroid);
+ }
sensoryNeuroids[availableIx] = neuroid;
neuroid.receptor.position = localPosition;
@@ -75,4 +93,16 @@ public class Perception {
//Debug.LogWarning($"No available receptor for {id}");
}
+
+ public void RemoveStimulus(int thingId) {
+ for (int i = 0; i < sensoryNeuroids.Length; i++) {
+ if (sensoryNeuroids[i] != null && sensoryNeuroids[i].receptor.thingId == thingId) {
+ foreach (Neuroid outputNeuroid in sensoryNeuroids[i].outputNeuroids)
+ outputNeuroid.RemoveInputFrom(sensoryNeuroids[i]);
+ sensoryNeuroids[i] = null;
+ return;
+ }
+ }
+
+ }
}
\ No newline at end of file
diff --git a/Assets/Scenes/Boids/Boids.unity b/Assets/Scenes/Boids/Boids.unity
index 0f3df0d..ee7a797 100644
--- a/Assets/Scenes/Boids/Boids.unity
+++ b/Assets/Scenes/Boids/Boids.unity
@@ -393,7 +393,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: ec888ca5333d45a438f9f417fa5ce135, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::SwarmSpawn
- count: 10
+ count: 2
boidPrefab: {fileID: 8702527964058765413, guid: f9c706268554ce449a8773675b2864b8, type: 3}
spawnAreaSize: {x: 0.5, y: 0.5, z: 0.5}
minDelay: 0.05
diff --git a/Assets/Scenes/Boids/Scripts/Boid.cs b/Assets/Scenes/Boids/Scripts/Boid.cs
index 1375e50..f239e3e 100644
--- a/Assets/Scenes/Boids/Scripts/Boid.cs
+++ b/Assets/Scenes/Boids/Scripts/Boid.cs
@@ -11,13 +11,17 @@ public class Boid : MonoBehaviour {
public float separationDistance = 0.5f;
public float bodyForce = 1;
+ public const int BoundaryType = 1;
+ public const int BoidType = 2;
+
public bool debug = false;
public SwarmControl sc;
public Vector3 velocity = Vector3.zero;
public Vector3 acceleration = Vector3.zero;
- private Bounds bounds;
+ private Bounds innerBounds;
+ private Bounds outerBounds;
readonly Collider[] results = new Collider[10];
@@ -28,7 +32,7 @@ public class Boid : MonoBehaviour {
public Neuroid bodyVector;
public Neuroid cohesion;
public Neuroid alignment;
- public Neuroid separation;
+ public Neuroid avoidance;
// public Neuroid target;
public Neuroid boundary;
@@ -41,76 +45,73 @@ public class Boid : MonoBehaviour {
sc = FindFirstObjectByType();
- bounds = new(sc.transform.position, sc.spaceSize - 2 * sc.boundaryWidth);
+ innerBounds = new(sc.transform.position, sc.spaceSize - 2 * sc.boundaryWidth);
+ outerBounds = new(sc.transform.position, sc.spaceSize);
perception = new Perception(neuroidNet);
cohesion = new(neuroidNet, "Cohesion");
- perception.SendPositions(cohesion);
+ perception.SendPositions(cohesion, 1.0f, BoidType);
- alignment = new(neuroidNet, "Alignment") { average = true };
- perception.SendVelocities(alignment);
+ // alignment = new(neuroidNet, "Alignment") { average = true };
+ // perception.SendVelocities(alignment);
- separation = new(neuroidNet, "Separation") { inverse = true, exponent = 2 };
- perception.SendPositions(separation, sc.separationForce);
+ avoidance = new(neuroidNet, "Separation") { inverse = true };
+ perception.SendPositions(avoidance, sc.avoidanceForce);
boundary = new(neuroidNet, "Boundary");
totalForce = new(neuroidNet, "Total");
- totalForce.GetInputFrom(alignment, sc.alignmentForce);
- totalForce.GetInputFrom(cohesion, sc.cohesionForce);
- totalForce.GetInputFrom(separation, -sc.separationForce);
- totalForce.GetInputFrom(boundary, sc.boundaryForce);
+ //totalForce.GetInputFrom(alignment, sc.alignmentForce);
+ //totalForce.GetInputFrom(cohesion, sc.cohesionForce);
+ totalForce.GetInputFrom(avoidance, -sc.avoidanceForce);
+ //totalForce.GetInputFrom(boundary, sc.boundaryForce);
}
void Update() {
Physics.OverlapSphereNonAlloc(this.transform.position, sc.perceptionDistance, results);
- foreach (Collider c in results) {
- if (c == null)
- continue;
+ // foreach (Collider c in results) {
+ // if (c == null)
+ // continue;
- if (c as CapsuleCollider != null) {
- Boid neighbour = c.GetComponentInParent();
- if (neighbour == null || neighbour == this)
- continue;
+ // if (c as CapsuleCollider != null) {
+ // Boid neighbour = c.GetComponentInParent();
+ // if (neighbour == null || neighbour == this)
+ // continue;
- Vector3 localPosition = neighbour.transform.position - this.transform.position;
- if (debug)
- Debug.Log($" distance {localPosition.magnitude}");
+ // Vector3 localPosition = neighbour.transform.position - this.transform.position;
+ // if (debug)
+ // Debug.Log($" distance {localPosition.magnitude}");
- int thingId = neighbour.GetInstanceID();
- perception.ProcessStimulus(thingId, localPosition);
- }
- }
-
- // if (!bounds.Contains(this.transform.position)) {
- // Vector3 point = this.transform.position;
- // // Vector3 distanceOutside = Vector3.Max(bounds.min - this.transform.position, this.transform.position - bounds.max);
- // // // Ensure value is > 0 (but isn't this already)
- // // Vector3 outside = distanceOutside; // Vector3.Max(Vector3.zero, distanceOutside);
-
- // Vector3 below = bounds.min - point; // positive where point < min
- // Vector3 above = point - bounds.max; // positive where point > max
-
- // // outside distances per axis (0 if inside on that axis)
- // Vector3 outside = Vector3.Max(Vector3.zero, Vector3.Max(below, above));
- // float magnitude = outside.magnitude;
- // Vector3 direction = (sc.transform.position - this.transform.position).normalized;
- // outside = direction * magnitude;
-
- // boundary.SetInput(id, outside, sc.boundaryForce, neuroidNet);
- // // Debug.Log($"boundary {this.transform.position} {outside} force = {outside * sc.boundaryForce}");
+ // int thingId = neighbour.GetInstanceID();
+ // perception.ProcessStimulus(thingId, localPosition);
+ // }
// }
+ if (!innerBounds.Contains(this.transform.position)) {
+ Vector3 point = this.transform.position;
+ Vector3 toBounds;
+ if (outerBounds.Contains(this.transform.position)) {
+ Vector3 pointOnBounds = ClosestPointOnBoundsSurface(outerBounds, point);
+ toBounds = this.transform.InverseTransformPoint(pointOnBounds);
+ }
+ else {
+ Vector3 pointOnBounds = innerBounds.ClosestPoint(point);
+ toBounds = -this.transform.InverseTransformPoint(pointOnBounds);
+ }
+ perception.ProcessStimulus(777, BoundaryType, toBounds);
+ } else
+ perception.RemoveStimulus(777);
+
Vector3 totalForceVector = totalForce.outputValue;
- //Debug.DrawRay(this.transform.position, totalForceVector, Color.magenta);
if (this.debug) {
- Debug.Log($"Cohesion {cohesion.outputValue.magnitude} separation {separation.outputValue.magnitude} alignment {alignment.outputValue.magnitude}");
+ Debug.Log($"Cohesion {cohesion.outputValue.magnitude} separation {avoidance.outputValue.magnitude} alignment {alignment.outputValue.magnitude}");
}
- this.velocity = (1 - sc.inertia) * (totalForceVector * Time.deltaTime) + sc.inertia * velocity + (sc.speed * transform.forward);
+ Vector3 worldForce = this.transform.TransformDirection(totalForceVector);
+ this.velocity = (1 - sc.inertia) * (worldForce * Time.deltaTime) + sc.inertia * velocity + (sc.speed * transform.forward);
//this.velocity = Vector3.ClampMagnitude(this.velocity, sc.speed);
this.transform.position += this.velocity * Time.deltaTime;
@@ -124,7 +125,22 @@ public class Boid : MonoBehaviour {
neuroidNet.Update();
}
+ Vector3 ClosestPointOnBoundsSurface(Bounds b, Vector3 p) {
+ if (!b.Contains(p)) return b.ClosestPoint(p);
+ Vector3 d = p - b.center;
+ Vector3 ext = b.extents;
+ float sx = ext.x / Mathf.Abs(d.x);
+ float sy = ext.y / Mathf.Abs(d.y);
+ float sz = ext.z / Mathf.Abs(d.z);
+ float m = Mathf.Min(sx, Mathf.Min(sy, sz));
+ return b.center + d * m;
+ }
+
void OnDrawGizmosSelected() {
Gizmos.DrawWireSphere(transform.position, sc.perceptionDistance);
+ Gizmos.color = Color.yellow;
+ Gizmos.DrawRay(transform.position, this.transform.TransformDirection(totalForce.outputValue) * 10);
+ Gizmos.color = Color.magenta;
+ Gizmos.DrawRay(transform.position, this.transform.TransformDirection(avoidance.outputValue) * 10);
}
}
diff --git a/Assets/Scenes/Boids/Scripts/SwarmControl.cs b/Assets/Scenes/Boids/Scripts/SwarmControl.cs
index fa502ab..c98a403 100644
--- a/Assets/Scenes/Boids/Scripts/SwarmControl.cs
+++ b/Assets/Scenes/Boids/Scripts/SwarmControl.cs
@@ -8,7 +8,7 @@ public class SwarmControl : MonoBehaviour
public float inertia = 0.1f;
public float alignmentForce = 0.0f;
public float cohesionForce = 10.0f;
- public float separationForce = 5.0f;
+ public float avoidanceForce = 5.0f;
public float separationDistance = 0.5f;
// public float bodyForce = 20;
public float perceptionDistance = 1.0f;