134 lines
5.0 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 SwarmControl sc;
public Vector3 velocity = Vector3.zero;
public Vector3 acceleration = Vector3.zero;
private Bounds bounds;
readonly Collider[] results = new Collider[10];
public NeuroidNetwork neuroidNet = new();
public Neuroid bodyVector;
public Neuroid cohesion;
public Neuroid alignment;
public Neuroid separation;
public Neuroid target;
public Neuroid boundary;
public Neuroid totalForce;
public int id;
void Awake()
{
this.id = this.GetInstanceID();
sc = FindFirstObjectByType<SwarmControl>();
bounds = new(sc.transform.position, sc.spaceSize - 2 * sc.boundaryWidth);
cohesion = new(neuroidNet) { name = "Cohesion", mode = Neuroid.Mode.Sum };
alignment = new(neuroidNet) { name = "Alignment", mode = Neuroid.Mode.Average };
separation = new(neuroidNet) { name = "Separation", mode = Neuroid.Mode.Sum };
target = new(neuroidNet) { name = "Target", mode = Neuroid.Mode.Sum };
boundary = new(neuroidNet) { name = "Boundary", mode = Neuroid.Mode.Sum };
totalForce = new(neuroidNet) { name = "Total force", mode = Neuroid.Mode.Sum };
totalForce.GetInputFrom(alignment, sc.alignmentForce);
totalForce.GetInputFrom(cohesion, sc.cohesionForce);
totalForce.GetInputFrom(separation, sc.separationForce);
totalForce.GetInputFrom(target, sc.bodyForce);
totalForce.GetInputFrom(boundary, sc.boundaryForce);
}
void Update()
{
Physics.OverlapSphereNonAlloc(this.transform.position, 10, results);
neighbourCount = 0;
cohesion.ResetWeights();
alignment.ResetWeights();
separation.ResetWeights();
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;
Vector3 relativeVelocity = neighbour.velocity - this.velocity;
int id = neighbour.GetInstanceID();
Vector3 separationForce = -localPosition / localPosition.sqrMagnitude;
// which is equivalent to -(localPosition.normalized / localPosition.magnitude)
separation.SetInput(id, separationForce, sc.separationDistance);
cohesion.SetInput(id, localPosition, sc.cohesionForce);
alignment.SetInput(id, relativeVelocity, sc.alignmentForce);
neighbourCount++;
}
}
//Vector3 spaceLocalPosition = sc.transform.InverseTransformPoint(this.transform.position);
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));
Vector3 dir = Vector3.zero;
dir.x = (below.x > 0f) ? 1f : ((above.x > 0f) ? -1f : 0f);
dir.y = (below.y > 0f) ? 1f : ((above.y > 0f) ? -1f : 0f);
dir.z = (below.z > 0f) ? 1f : ((above.z > 0f) ? -1f : 0f);
outside = Vector3.Scale(outside, dir);
boundary.SetInput(id, outside, sc.boundaryForce);
Debug.Log($"boundary {this.transform.position} {outside} force = {outside * sc.boundaryForce}");
}
Vector3 totalForceVector = totalForce.outputValue;
//Debug.DrawRay(this.transform.position, totalForceVector, Color.magenta);
this.velocity = (1 - sc.inertia) * (totalForceVector * 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
}
}
}