Almost flocking
This commit is contained in:
parent
ca48381dc2
commit
69c6b1e2b7
@ -8,6 +8,8 @@ public class NanoBrain_Editor : Editor {
|
||||
private List<NeuroidLayer> layers = new();
|
||||
private Dictionary<Nucleus, Vector2Int> neuroidPositions = new();
|
||||
|
||||
protected bool breakOnWake = false;
|
||||
|
||||
#region Start
|
||||
|
||||
private void OnEnable() {
|
||||
@ -19,7 +21,7 @@ public class NanoBrain_Editor : Editor {
|
||||
if (!selectedObject.TryGetComponent(out Boid boid))
|
||||
return;
|
||||
|
||||
Neuroid neuroid = boid.totalForce;
|
||||
Nucleus neuroid = boid.totalForce;
|
||||
this.currentNucleus = neuroid;
|
||||
|
||||
BuildLayers();
|
||||
@ -34,12 +36,32 @@ public class NanoBrain_Editor : Editor {
|
||||
if (this.currentNucleus == null)
|
||||
return;
|
||||
|
||||
breakOnWake = EditorGUILayout.Toggle("Break on wake", breakOnWake);
|
||||
if (breakOnWake && currentNucleus is Neuroid currentNeuroid) {
|
||||
if (!currentNeuroid.isSleeping)
|
||||
Debug.Break();
|
||||
}
|
||||
|
||||
DrawGraph();
|
||||
|
||||
//DrawDefaultInspector();
|
||||
EditorGUILayout.TextField("Name", currentNucleus.name);
|
||||
EditorGUILayout.FloatField("Output Value", currentNucleus.outputValue.magnitude);
|
||||
EditorGUILayout.IntField("# synapses", currentNucleus.synapses.Count);
|
||||
EditorGUILayout.Vector3Field("Output Value", currentNucleus.outputValue);
|
||||
if (currentNucleus.synapses.Count > 0) {
|
||||
EditorGUI.indentLevel++;
|
||||
foreach ((Nucleus nucleus, float weight) in currentNucleus.synapses) {
|
||||
EditorGUI.BeginDisabledGroup(nucleus.isSleeping);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.Vector3Field(nucleus.name, nucleus.outputValue);
|
||||
EditorGUILayout.FloatField(weight, GUILayout.Width(50));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
}
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildLayers() {
|
||||
@ -140,8 +162,8 @@ public class NanoBrain_Editor : Editor {
|
||||
|
||||
|
||||
float size = 20;
|
||||
if (layerNeuroid.IsStale())
|
||||
Handles.color = Color.darkCyan;
|
||||
if (layerNeuroid.isSleeping)
|
||||
Handles.color = Color.darkRed;
|
||||
else {
|
||||
float brightness = layerNeuroid.outputValue.magnitude / maxValue;
|
||||
Handles.color = new Color(brightness, brightness, brightness);
|
||||
@ -224,7 +246,8 @@ public class NanoBrain_Editor : Editor {
|
||||
// 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);
|
||||
//Debug.DrawRay(position, worldForce * 10, Color.yellow);
|
||||
Handles.DrawLine(position, position + worldForce * 10);
|
||||
}
|
||||
|
||||
#endregion Update
|
||||
|
||||
@ -199,7 +199,7 @@ public class GraphEditorWindow : EditorWindow {
|
||||
}
|
||||
|
||||
float size = 20;
|
||||
if (layerNeuroid.IsStale())
|
||||
if (layerNeuroid.isSleeping)
|
||||
Handles.color = Color.black;
|
||||
else {
|
||||
float brightness = layerNeuroid.outputValue.magnitude / maxValue;
|
||||
@ -272,7 +272,7 @@ public class GraphEditorWindow : EditorWindow {
|
||||
if (boid == null)
|
||||
return;
|
||||
|
||||
Neuroid neuroid = boid.totalForce;
|
||||
Nucleus neuroid = boid.behaviour;
|
||||
this.currentNucleus = neuroid;
|
||||
if (neuroid == null)
|
||||
this.allNeuroids = new();
|
||||
|
||||
@ -12,7 +12,7 @@ public class NanoBrain : MonoBehaviour {
|
||||
public void UpdateNeurons() {
|
||||
foreach (Neuroid neuroid in neuroids) {
|
||||
neuroid.stale++;
|
||||
if (neuroid.IsStale())
|
||||
if (neuroid.isSleeping)
|
||||
neuroid.outputValue = Vector3.zero;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class Neuroid : Nucleus {
|
||||
public int stale = 0;
|
||||
|
||||
public bool average = false;
|
||||
public bool inverse = false;
|
||||
public float exponent = 1.0f;
|
||||
@ -15,11 +13,6 @@ public class Neuroid : Nucleus {
|
||||
Debug.LogError("No neuroid network");
|
||||
}
|
||||
|
||||
// public void AddSynapse(Neuroid input) {
|
||||
// input.AddReceiver(this);
|
||||
// this.synapses[input] = 1.0f;
|
||||
// }
|
||||
|
||||
public void SetWeight(Neuroid input, float weight) {
|
||||
this.synapses[input] = weight;
|
||||
}
|
||||
@ -43,7 +36,7 @@ public class Neuroid : Nucleus {
|
||||
public virtual void UpdateState() {
|
||||
Vector3 result = Vector3.zero;
|
||||
foreach ((Nucleus nucleus, float weight) in this.synapses) {
|
||||
if (nucleus is Neuroid neuroid && neuroid.IsStale())
|
||||
if (nucleus is Neuroid neuroid && neuroid.isSleeping)
|
||||
continue;
|
||||
|
||||
Vector3 direction = nucleus.outputValue.normalized;
|
||||
@ -65,8 +58,8 @@ public class Neuroid : Nucleus {
|
||||
receiver.SetInput(this);
|
||||
}
|
||||
|
||||
public bool IsStale() {
|
||||
return this.stale > 2;
|
||||
}
|
||||
// public bool IsStale() {
|
||||
// return this.stale > 2;
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
@ -2,9 +2,11 @@ using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class Nucleus {
|
||||
public int stale = 0;
|
||||
|
||||
public NanoBrain brain { get; protected set; }
|
||||
|
||||
public string name;
|
||||
public virtual string name { get; set; }
|
||||
|
||||
public readonly Dictionary<Nucleus, float> synapses = new();
|
||||
public HashSet<Neuroid> receivers = new();
|
||||
@ -22,4 +24,11 @@ public class Nucleus {
|
||||
receiver.synapses[this] = 1.0f; // new(this);
|
||||
//Debug.Log($"receiver # {this.receivers.Count} synapse count {receiver.synapses.Count}");
|
||||
}
|
||||
|
||||
public bool isSleeping {
|
||||
get {
|
||||
return this.stale > 2;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -19,7 +19,7 @@ public class Perception : Nucleus {
|
||||
this.velocityReceivers = new();
|
||||
}
|
||||
|
||||
public void SendPositions(Neuroid receivingNeuroid, float weight = 1.0f, int thingType = 0) {
|
||||
public void SendPositions(Neuroid receivingNeuroid, int thingType = 0, float weight = 1.0f) {
|
||||
Receiver receiver = new() {
|
||||
thingType = thingType,
|
||||
neuroid = receivingNeuroid
|
||||
@ -32,7 +32,7 @@ public class Perception : Nucleus {
|
||||
}
|
||||
}
|
||||
}
|
||||
public void SendVelocities(Neuroid receivingNeuroid, float weight = 1.0f, int thingType = 0) {
|
||||
public void SendVelocities(Neuroid receivingNeuroid, int thingType = 0, float weight = 1.0f) {
|
||||
Receiver receiver = new() {
|
||||
thingType = thingType,
|
||||
neuroid = receivingNeuroid
|
||||
@ -57,7 +57,7 @@ public class Perception : Nucleus {
|
||||
return;
|
||||
}
|
||||
if (availableIx == -1) {
|
||||
if (sensoryNeuroids[i].IsStale())
|
||||
if (sensoryNeuroids[i].isSleeping)
|
||||
leastInterestingIx = i;
|
||||
else if (sensoryNeuroids[i] != null) {
|
||||
if (leastInterestingIx == -1 || sensoryNeuroids[leastInterestingIx].receptor.position.magnitude > sensoryNeuroids[i].receptor.position.magnitude)
|
||||
@ -77,7 +77,7 @@ public class Perception : Nucleus {
|
||||
}
|
||||
else {
|
||||
// Debug.Log($"new receptor for {thingId} at {availableIx}");
|
||||
neuroid = new(brain, thingId) { name = name };
|
||||
neuroid = new(brain, thingId, name);
|
||||
sensoryNeuroids[availableIx] = neuroid;
|
||||
}
|
||||
foreach (Receiver receiver in positionReceivers) {
|
||||
|
||||
@ -30,33 +30,38 @@ public class SensoryNeuroid : Neuroid {
|
||||
public VelocityNeuroid velocityNeuroid;
|
||||
|
||||
// public SensoryNeuroid(NeuroidNetwork net, int thingId) : base(net, "sensory neuroid") {
|
||||
public SensoryNeuroid(NanoBrain net, int thingId) : base(net, "sensory neuroid") {
|
||||
public SensoryNeuroid(NanoBrain net, int thingId, string name = "sensor") : base(net, name) {
|
||||
this.name = name + ": position";
|
||||
this.receptor = new Receptor {
|
||||
neuroid = this,
|
||||
thingId = thingId
|
||||
};
|
||||
this.velocityNeuroid = new(net);
|
||||
this.velocityNeuroid = new(net, name + ": velocity");
|
||||
// The velocity neuroid received position data from this
|
||||
this.AddReceiver(velocityNeuroid);
|
||||
}
|
||||
|
||||
public void Replace(int thingId, string name = "sensory neuroid") {
|
||||
this.name = name;
|
||||
public void Replace(int thingId, string name = "sensor") {
|
||||
this.name = name + ": position";
|
||||
|
||||
this.receptor.thingId = thingId;
|
||||
this.receptor.localPosition = Vector3.zero;
|
||||
|
||||
this.outputValue = Vector3.zero;
|
||||
this.receivers = new();
|
||||
this.AddReceiver(velocityNeuroid);
|
||||
this.velocityNeuroid.receivers = new();
|
||||
|
||||
// this.velocityNeuroid.name = name + ": velocity";
|
||||
// this.velocityNeuroid.receivers = new();
|
||||
this.velocityNeuroid.Replace(name + ": velocity");
|
||||
}
|
||||
|
||||
public override void UpdateState() {
|
||||
Vector3 result = receptor.localPosition;
|
||||
//foreach ((Nucleus nucleus, Synapse synapse) in this.synapses) {
|
||||
foreach ((Nucleus nucleus, float weight) in this.synapses) {
|
||||
Vector3 direction = nucleus.outputValue.normalized;
|
||||
float magnitude = nucleus.outputValue.magnitude;
|
||||
//magnitude = synapse.weight * Mathf.Pow(magnitude, exponent);
|
||||
|
||||
magnitude = weight * Mathf.Pow(magnitude, exponent);
|
||||
if (inverse)
|
||||
magnitude = 1 / magnitude;
|
||||
@ -77,8 +82,14 @@ public class VelocityNeuroid : Neuroid {
|
||||
private Vector3 lastPosition = Vector3.zero;
|
||||
private float lastValueTime = 0;
|
||||
|
||||
// public VelocityNeuroid(NeuroidNetwork net) : base(net, "Velocity") {
|
||||
public VelocityNeuroid(NanoBrain net) : base(net, "Velocity") {
|
||||
public VelocityNeuroid(NanoBrain net, string name = "velocity") : base(net, name) {
|
||||
}
|
||||
|
||||
public void Replace(string name = "velocity") {
|
||||
this.name = name;
|
||||
this.receivers = new();
|
||||
this.lastPosition = Vector3.zero;
|
||||
this.lastValueTime = 0;
|
||||
}
|
||||
|
||||
public override void UpdateState() {
|
||||
@ -86,17 +97,20 @@ public class VelocityNeuroid : Neuroid {
|
||||
Vector3 currentPosition = this.synapses.First().Key.outputValue;
|
||||
float currentValueTime = Time.time;
|
||||
|
||||
float deltaTime = currentValueTime - lastValueTime;
|
||||
Vector3 translation = currentPosition - lastPosition;
|
||||
Vector3 velocity = translation / deltaTime;
|
||||
if (lastValueTime != 0) {
|
||||
float deltaTime = currentValueTime - lastValueTime;
|
||||
Vector3 translation = currentPosition - lastPosition;
|
||||
Vector3 velocity = translation / deltaTime;
|
||||
|
||||
// No activation function...
|
||||
this.outputValue = velocity;
|
||||
foreach (Neuroid receiver in receivers)
|
||||
receiver?.SetInput(this);
|
||||
this.stale = 0;
|
||||
// No activation function...
|
||||
this.outputValue = velocity;
|
||||
this.stale = 0;
|
||||
|
||||
this.lastValueTime = Time.time;
|
||||
foreach (Neuroid receiver in receivers)
|
||||
receiver?.SetInput(this);
|
||||
}
|
||||
|
||||
this.lastValueTime = currentValueTime;
|
||||
this.lastPosition = currentPosition;
|
||||
}
|
||||
}
|
||||
@ -372,10 +372,10 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::SwarmControl
|
||||
speed: 2
|
||||
inertia: 0.1
|
||||
alignmentForce: 3
|
||||
cohesionForce: 3
|
||||
avoidanceForce: 5
|
||||
inertia: 0.5
|
||||
alignmentForce: 2
|
||||
cohesionForce: 2
|
||||
avoidanceForce: 1
|
||||
separationDistance: 0.3
|
||||
perceptionDistance: 1
|
||||
boundaryForce: 5
|
||||
@ -393,7 +393,7 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: ec888ca5333d45a438f9f417fa5ce135, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::SwarmSpawn
|
||||
count: 5
|
||||
count: 20
|
||||
boidPrefab: {fileID: 8702527964058765413, guid: f9c706268554ce449a8773675b2864b8, type: 3}
|
||||
spawnAreaSize: {x: 0.5, y: 0.5, z: 0.5}
|
||||
minDelay: 0.05
|
||||
|
||||
@ -2,17 +2,15 @@ using UnityEngine;
|
||||
|
||||
[RequireComponent(typeof(NanoBrain))]
|
||||
public class Boid : MonoBehaviour {
|
||||
public const int BoundaryType = 1;
|
||||
public const int BoidType = 2;
|
||||
public static int BoundaryType = 1;
|
||||
public static int BoidType = 2;
|
||||
|
||||
public SwarmControl sc;
|
||||
public Vector3 velocity = Vector3.zero;
|
||||
public Vector3 acceleration = Vector3.zero;
|
||||
|
||||
private Bounds innerBounds;
|
||||
private Bounds outerBounds;
|
||||
|
||||
//public NeuroidNetwork neuroidNet = new();
|
||||
public NanoBrain neuroidNet;
|
||||
public Perception perception;
|
||||
|
||||
@ -30,7 +28,6 @@ public class Boid : MonoBehaviour {
|
||||
sc = FindFirstObjectByType<SwarmControl>();
|
||||
|
||||
innerBounds = new(sc.transform.position, sc.spaceSize - 2 * sc.boundaryWidth);
|
||||
outerBounds = new(sc.transform.position, sc.spaceSize);
|
||||
|
||||
perception = new Perception(neuroidNet);
|
||||
|
||||
@ -49,7 +46,8 @@ public class Boid : MonoBehaviour {
|
||||
if (neighbour == null || neighbour == this)
|
||||
continue;
|
||||
|
||||
Vector3 localPosition = neighbour.transform.position - this.transform.position;
|
||||
Vector3 localPosition = this.transform.InverseTransformPoint(neighbour.transform.position);
|
||||
//Debug.DrawRay(this.transform.position, this.transform.TransformDirection(localPosition), Color.magenta);
|
||||
|
||||
int thingId = neighbour.GetInstanceID();
|
||||
perception.ProcessStimulus(thingId, BoidType, localPosition, neighbour.gameObject.name);
|
||||
@ -64,13 +62,14 @@ public class Boid : MonoBehaviour {
|
||||
perception.ProcessStimulus(777, BoundaryType, desiredLocalSpace, "Boundary");
|
||||
}
|
||||
|
||||
Vector3 worldForce = this.transform.TransformDirection(totalForce.outputValue);
|
||||
Vector3 worldForce = this.transform.TransformDirection(behaviour.outputValue);
|
||||
|
||||
this.velocity = (1 - sc.inertia) * (worldForce * Time.deltaTime) + sc.inertia * velocity;
|
||||
if (this.velocity.magnitude > 0)
|
||||
this.velocity = this.velocity.normalized * sc.speed;
|
||||
else
|
||||
this.velocity = this.transform.forward * sc.speed;
|
||||
Debug.DrawRay(this.transform.position, this.velocity, Color.blue);
|
||||
|
||||
this.transform.position += this.velocity * Time.deltaTime;
|
||||
|
||||
@ -82,14 +81,4 @@ public class Boid : MonoBehaviour {
|
||||
neuroidNet.UpdateNeurons();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ public class Roaming : Nucleus {
|
||||
|
||||
public Roaming(NanoBrain neuroidNet, Perception perception, SwarmControl sc) : base("Roaming nucleus") {
|
||||
avoidance = new(neuroidNet, "Avoidance") { inverse = true };
|
||||
perception.SendPositions(avoidance, 1.0f, 1);
|
||||
perception.SendPositions(avoidance, Boid.BoundaryType);
|
||||
|
||||
this.output = new(neuroidNet, "Roaming");
|
||||
output.GetInputFrom(avoidance, -sc.avoidanceForce);
|
||||
|
||||
@ -4,28 +4,30 @@ public class Swarming : Nucleus {
|
||||
public Neuroid cohesion;
|
||||
public Neuroid alignment;
|
||||
public Neuroid avoidance;
|
||||
public Neuroid boundary;
|
||||
|
||||
public Neuroid output;
|
||||
|
||||
public override Vector3 outputValue { get => output.outputValue; set => output.outputValue = value; }
|
||||
|
||||
public const int BoundaryType = 1;
|
||||
public const int BoidType = 2;
|
||||
|
||||
public Swarming(NanoBrain neuroidNet, Perception perception, SwarmControl sc) : base("Swarming Nucleus") {
|
||||
this.cohesion = new(neuroidNet, "Cohesion");
|
||||
perception.SendPositions(this.cohesion, 1.0f, BoidType);
|
||||
this.cohesion = new(neuroidNet, "Cohesion") { inverse = false };
|
||||
perception.SendPositions(this.cohesion, Boid.BoidType);
|
||||
|
||||
this.alignment = new(neuroidNet, "Alignment") { average = true };
|
||||
perception.SendVelocities(this.alignment, 1.0f, BoidType);
|
||||
perception.SendVelocities(this.alignment, Boid.BoidType);
|
||||
|
||||
this.avoidance = new(neuroidNet, "Avoidance") { inverse = true };
|
||||
perception.SendPositions(this.avoidance);
|
||||
|
||||
this.boundary = new(neuroidNet, "Boundary");
|
||||
perception.SendPositions(this.boundary, Boid.BoundaryType);
|
||||
|
||||
this.output = new(neuroidNet, "Swarming");
|
||||
this.output.GetInputFrom(alignment, sc.alignmentForce);
|
||||
this.output.GetInputFrom(cohesion, sc.cohesionForce);
|
||||
this.output.GetInputFrom(avoidance, -sc.avoidanceForce);
|
||||
this.output.GetInputFrom(boundary, -sc.avoidanceForce);
|
||||
}
|
||||
|
||||
public override void AddReceiver(Neuroid receiver) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user