I am trying to make an AI that will follow the closest object, but the problem is that when the object gets close it will rotate vertically, which I think is because the transform.LookAt() rotates towards the center of the object. Is there a way to look at an object without rotating vertically? Here's the code for the AI
using System.Collections.Generic;
using UnityEngine;
public class AI : MonoBehaviour
{
[SerializeField] public float speed = 2f;
public float SphereRadius;
public LayerMask mask;
private void FixedUpdate()
{
//Checks if theres any objects nearbye
Collider[] hitColliders = Physics.OverlapSphere(transform.position, SphereRadius, mask, QueryTriggerInteraction.UseGlobal);
//Gets all the transforms of the objects and stores them into an array
List<Transform> Transforms = new List<Transform>();
foreach (var hitCollider in hitColliders)
{
Transforms.Add(hitCollider.transform);
}
//Checks what's the closest object and rotates the AI towards that object
transform.LookAt(
GetClosestEnemy(
Transforms.ToArray()
)
);
transform.Translate(Vector3.forward * speed * Time.deltaTime);
}
Transform GetClosestEnemy(Transform[] enemies)
{
Transform tMin = null;
float minDist = Mathf.Infinity;
Vector3 currentPos = transform.position;
foreach (Transform t in enemies)
{
float dist = Vector3.Distance(t.position, currentPos);
if (dist < minDist)
{
tMin = t;
minDist = dist;
}
}
return tMin;
}
}
CodePudding user response:
Instead of directly using the Transform overload use the one taking a Vector3 target position you can completely customize like e.g.
var target = GetClosestEnemy(Transforms.ToArray());
var localDelta = transform.InverseTransformPoint(target.position);
// Now you can reset any of the axis you do not want to be taken into account e.g.
localDelta.z = 0;
transform.LookAt(transform.TransformPoint(localDelta));
Btw as a little tip magnitude (which is internally used by Vector3.Distance) is significantly slower than the sqrMagnitude as the latter skips taking the square root.
For comparison both work the same so you can use
Transform GetClosestEnemy(IEnumerable<Transform> enemies)
{
Transform tMin = null;
float minDist = Mathf.Infinity;
Vector3 currentPos = transform.position;
foreach (Transform t in enemies)
{
float dist = (t.position - currentPos).sqrMagnitude;
if (dist < minDist)
{
tMin = t;
minDist = dist;
}
}
return tMin;
}
or if you re a fan of Linq you could also do
Transform GetClosestEnemy(IEnumerable<Transform> enemies)
{
Vector3 currentPos = transform.position;
return enemies.OrderBy(enemy => (enemy.position - currentPos).sqrMagnitude).FirstOrDefault();
}
Then you could also shrink down the rest and do
Collider[] hitColliders = Physics.OverlapSphere(transform.position, SphereRadius, mask, QueryTriggerInteraction.UseGlobal);
var target = GetClosestEnemy(hitColliders.Select(collider => collider.transform));
