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(); 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(); // 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); } }