new boundary perception
This commit is contained in:
parent
fdabad2895
commit
bb5939b6ab
@ -55,6 +55,7 @@
|
||||
<Compile Include="Assets/NanoBrain/Perception.cs" />
|
||||
<Compile Include="Assets/Scenes/Boids/Scripts/Boid.cs" />
|
||||
<Compile Include="Assets/NanoBrain/Neuroid.cs" />
|
||||
<Compile Include="Assets/NanoBrain/Nucleus.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="UnityEngine">
|
||||
|
||||
@ -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<int, Neuroid> 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;
|
||||
}
|
||||
|
||||
7
Assets/NanoBrain/Nucleus.cs
Normal file
7
Assets/NanoBrain/Nucleus.cs
Normal file
@ -0,0 +1,7 @@
|
||||
public class Nucleus {
|
||||
public class State {
|
||||
|
||||
}
|
||||
|
||||
public State state;
|
||||
}
|
||||
2
Assets/NanoBrain/Nucleus.cs.meta
Normal file
2
Assets/NanoBrain/Nucleus.cs.meta
Normal file
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 750748f3f0e7d472fbf88ab02987074c
|
||||
@ -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<Neuroid> positionReceivers { get; protected set; }
|
||||
public HashSet<Neuroid> velocityReceivers { get; protected set; }
|
||||
public class Receiver {
|
||||
public int thingType = 0;
|
||||
public Neuroid neuroid;
|
||||
}
|
||||
|
||||
public HashSet<Receiver> positionReceivers { get; protected set; }
|
||||
public HashSet<Receiver> 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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<SwarmControl>();
|
||||
|
||||
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<Boid>();
|
||||
if (neighbour == null || neighbour == this)
|
||||
continue;
|
||||
// if (c as CapsuleCollider != null) {
|
||||
// Boid neighbour = c.GetComponentInParent<Boid>();
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user