using System.Collections.Generic; using UnityEngine; [RequireComponent(typeof(NanoBrain))] public class AnimatedAnt : MonoBehaviour { private readonly float inertia = 0.2f; private readonly float smellRadius = 1.0f; private readonly float smellAngle = 80.0f; public GameObject homePheromonePrefab; public GameObject foodPheromonePrefab; public AntennaTouch touchLeft; public AntennaTouch touchRight; public NanoBrain nanoBrain; // brain output public Neuron hasFood; // brain input public Nucleus pheromoneSteering; public Nucleus hitLeft; public Nucleus hitRight; public Nucleus beat; public Receptor foodReceptor; public Receptor homeReceptor; public new Rigidbody rigidbody; public Animator animator; public float forwardSpeed = 1; public float rotationSpeed = 1; public Vector3 linearVelocity; public Vector3 angularVelocity; protected virtual void Awake() { this.animator = GetComponentInChildren(); if (this.animator == null && this.transform.parent != null) this.animator = this.transform.parent.GetComponentInChildren(); this.nanoBrain = GetComponentInChildren(); this.rigidbody = GetComponentInParent(); this.rigidbody.isKinematic = false; } void Start() { Cluster brain = this.nanoBrain.brain; if (brain != null) { // brain outputs this.pheromoneSteering = brain.GetNucleus("Pheromone Steering"); if (brain.GetNucleus("Home Pheromones") is Neuron homePheromones) homePheromones.WhenFiring += PlaceHomePheromone; if (brain.GetNucleus("Food Pheromones") is Neuron foodPheromones) foodPheromones.WhenFiring += PlaceFoodPheromone; this.hasFood = brain.GetNucleus("Having Food") as Neuron; // brain inputs this.beat = brain.GetNucleus("Beat"); this.hitLeft = brain.GetNucleus("Hit Left"); this.hitRight = brain.GetNucleus("Hit Right"); this.foodReceptor = brain.GetNucleus("Food Receptor") as Receptor; this.homeReceptor = brain.GetNucleus("Home Receptor") as Receptor; } this.linearVelocity = Vector3.forward; if (touchLeft != null) touchLeft.touched += OnAntennaTouchLeft; if (touchRight != null) touchRight.touched += OnAntennaTouchRight; } void PlaceFoodPheromone() { GameObject pheromoneObj = Instantiate(foodPheromonePrefab); pheromoneObj.transform.position = this.transform.position; } void PlaceHomePheromone() { GameObject pheromoneObj = Instantiate(homePheromonePrefab); pheromoneObj.transform.position = this.transform.position; } // Update is called once per frame void Update() { UpdateBeat(); UpdateSmell(); if (this.nanoBrain == null || this.nanoBrain.brain == null || this.animator == null) return; Vector3 localForce = nanoBrain.brain.defaultOutput.outputValue; this.linearVelocity = (1 - inertia) * (Time.deltaTime * localForce.normalized) + inertia * this.linearVelocity; this.linearVelocity = this.linearVelocity.normalized * 0.2f; // Vector3 linearVelocityWorld = this.transform.TransformVector(this.linearVelocity); // this.rigidbody.linearVelocity = linearVelocityWorld; this.animator.SetFloat("forward speed", this.linearVelocity.z * this.forwardSpeed); // Rotate towards the movement direction if (this.linearVelocity != Vector3.zero) { Quaternion targetRotation = Quaternion.LookRotation(this.linearVelocity); Quaternion worldRotation = transform.rotation * targetRotation; Quaternion deltaRotation = worldRotation * Quaternion.Inverse(transform.rotation); Vector3 eulerAngleChange = deltaRotation.eulerAngles; // Normalize the Euler angles to avoid unexpected jumps due to 360-degree rotations eulerAngleChange = new Vector3( LinearAlgebra.Angles.Normalize(eulerAngleChange.x), LinearAlgebra.Angles.Normalize(eulerAngleChange.y), LinearAlgebra.Angles.Normalize(eulerAngleChange.z) ); Vector3 angularVelocity = 5f * Mathf.Deg2Rad * eulerAngleChange; //rigidbody.angularVelocity = angularVelocity; this.animator.SetFloat("rotate speed", eulerAngleChange.y / 45 * this.rotationSpeed); } } public float beatInterval = 3; float lastBeatTime = 0; void UpdateBeat() { if (lastBeatTime == 0) { ulong delay = (ulong)(UnityEngine.Random.value * beatInterval); lastBeatTime = Time.time - delay; } if (Time.time - lastBeatTime >= beatInterval) { lastBeatTime = Time.time; beat?.SetBias(Vector3.one); //, 0); } } void UpdateSmell() { // To generate random basic movement, we add a small with a random direction with low intensity float randomAngle = Random.Range(-smellAngle, smellAngle); Vector3 randomDirection = Quaternion.AngleAxis(randomAngle, Vector3.up) * Vector3.forward * 0.01f; pheromoneSteering?.SetBias(randomDirection); //, 0, "random"); Collider[] colliders = Physics.OverlapSphere(this.transform.position, smellRadius); foreach (Collider collider in colliders) { SmellPheromones(collider); SmellFood(collider); SmellHome(collider); } if (nanoBrain != null && nanoBrain.brain != null) nanoBrain.brain.UpdateNuclei(); } void SmellPheromones(Collider thing) { Pheromone pheromone = thing.GetComponentInParent(); if (pheromone == null) return; // if (hasFood == null) // return; // if ((hasFood.outputValue.x > 0 && pheromone.type == Pheromone.Type.Home) || // (hasFood.outputValue.x < 0 && pheromone.type == Pheromone.Type.Food)) { // Vector3 smellDirection = this.transform.InverseTransformPoint(pheromone.transform.position); // float distance = smellDirection.magnitude; // float angle = Vector3.Angle(Vector3.forward, smellDirection); // if (angle < smellAngle && smellDirection.magnitude > 0.05) { // float intensity = pheromone.StrengthAt(distance);//strength * (1 / distance); // pheromoneSteering?.ProcessStimulus(pheromone.GetInstanceID(), smellDirection.normalized * intensity, // pheromone.type.ToString() + " pheromone"); // //Debug.DrawLine(this.transform.position, pheromone.transform.position, Color.magenta); // } // } Vector3 smellDirection = this.transform.InverseTransformPoint(pheromone.transform.position); float distance = smellDirection.magnitude; float angle = Vector3.Angle(Vector3.forward, smellDirection); if (angle < smellAngle && smellDirection.magnitude > 0.05) { float intensity = pheromone.StrengthAt(distance); Vector3 smell = smellDirection.normalized * intensity; switch (pheromone.type) { case Pheromone.Type.Food: //foodSmell?.ProcessStimulus(pheromone.GetInstanceID(), smell, "food pheromone"); foodReceptor?.ProcessStimulus(smellDirection.normalized * intensity, pheromone.GetInstanceID(), "food pheromone"); break; case Pheromone.Type.Home: //pheromoneSteering?.ProcessStimulus(pheromone.GetInstanceID(), smell, "home pheromone"); //homeSmell?.ProcessStimulus(pheromone.GetInstanceID(), smell, "home pheromone"); homeReceptor?.ProcessStimulus(smellDirection.normalized * intensity, pheromone.GetInstanceID(), "home pheromone"); break; } //Debug.DrawLine(this.transform.position, pheromone.transform.position, Color.magenta); } } void SmellFood(Collider thing) { if (hasFood != null && hasFood.outputValue.x > 0) // if it has food... return; Food food = thing.GetComponentInParent(); if (food == null) return; Vector3 smellDirection = this.transform.InverseTransformPoint(food.transform.position); float distance = smellDirection.magnitude; float angle = Vector3.Angle(Vector3.forward, smellDirection); if (angle < smellAngle && distance > 0.05) { float intensity = food.StrengthAt(distance); //strength * (1 / distance); //foodSmell?.ProcessStimulus(food.GetInstanceID(), smellDirection.normalized * intensity, "food"); foodReceptor?.ProcessStimulus(smellDirection.normalized * intensity, food.GetInstanceID(), "food"); Debug.DrawLine(this.transform.position, food.transform.position, Color.red); } } void SmellHome(Collider thing) { if (hasFood != null && hasFood.outputValue.x < 0) // if it does not have food.... return; AntsNest nest = thing.GetComponentInParent(); if (nest == null) return; Vector3 smellDirection = this.transform.InverseTransformPoint(nest.transform.position); float distance = smellDirection.magnitude; float angle = Vector3.Angle(Vector3.forward, smellDirection); if (angle < smellAngle && distance > 0.05) { float intensity = nest.StrengthAt(distance); //strength * (1 / distance); //homeSmell?.ProcessStimulus(nest.GetInstanceID(), smellDirection.normalized * intensity, "nest"); Vector3 value = smellDirection.normalized * intensity; homeReceptor?.ProcessStimulus(value, nest.GetInstanceID(), "nest"); Debug.DrawLine(this.transform.position, nest.transform.position, Color.red); } } void OnAntennaTouchLeft(Collider other, bool isTouching) { Vector3 touchDirection = Vector3.zero; if (isTouching) touchDirection = this.transform.InverseTransformVector(touchLeft.transform.forward); hitLeft?.SetBias(touchDirection); //, other.GetInstanceID(), "Touch Left"); } void OnAntennaTouchRight(Collider other, bool isTouching) { Vector3 touchDirection = Vector3.zero; if (isTouching) touchDirection = this.transform.InverseTransformVector(touchRight.transform.forward); hitRight?.SetBias(touchDirection); //, other.GetInstanceID(), "Touch Right"); } }