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));