Home > Software engineering >  transform.LookAt() issue
transform.LookAt() issue

Time:09-21

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