147 lines
5.3 KiB
C#

using UnityEngine;
public class Boid : MonoBehaviour {
public float speed = 0.2f;
public int neighbourCount = 0;
public float inertia = 0.2f;
public float alignmentForce = 1.0f;
public float cohesionForce = 1.0f;
public float separationForce = 1.0f;
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 innerBounds;
private Bounds outerBounds;
readonly Collider[] results = new Collider[10];
//public SensoryNeuroid[] neighbourSensor = new SensoryNeuroid[6];
public Perception perception;
public NeuroidNetwork neuroidNet = new();
public Neuroid bodyVector;
public Neuroid cohesion;
public Neuroid alignment;
public Neuroid avoidance;
// public Neuroid target;
public Neuroid boundary;
public Neuroid totalForce;
public int id;
void Awake() {
this.id = this.GetInstanceID();
sc = FindFirstObjectByType<SwarmControl>();
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, 1.0f, BoidType);
// alignment = new(neuroidNet, "Alignment") { average = true };
// perception.SendVelocities(alignment);
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(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;
// 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}");
// 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;
if (this.debug) {
Debug.Log($"Cohesion {cohesion.outputValue.magnitude} separation {avoidance.outputValue.magnitude} alignment {alignment.outputValue.magnitude}");
}
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;
if (this.velocity != Vector3.zero) {
Quaternion targetRotation = Quaternion.LookRotation(this.velocity);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 2f); // Adjust the speed of rotation
}
//Debug.Log($"neighbours: {neighbourCount} synapses: {cohesion.synapses.Count}");
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);
}
}