通过输入方向查找gameObject

3

Example

我有一个游戏对象列表添加到了如图所示的区域内。我需要一个方向输入来选择从原点开始的目标。我已经成功获取了这个原点。
我的第一次尝试是通过 rayCast 来获取它,但是这样做有时候需要直接用射线命中目标对象。如果输入像情况 #1 那就没有问题。
然而,我真正需要的是当输入方向像情况 #2 或 #3 时,输入仍将获得目标。我的第二次尝试是使用 sphereCast 来实现这一点,但仍需要在球体范围内有目标,并且由 sphereCast 击中的多个目标仍需要通过输入选择一个更准确的目标。
既然我拥有所有可能目标的 transform.position,以及原点,我想知道是否有一种更优雅的方法,通过比较这些坐标(原点和目标)的 vector3 来解决这个问题。
以下是我的最新方法:
//
// m_targetList is the list containing all GameObjects as GameObjects in other script m_collector. All m_collector does is this.
//

using System.Collections.Generic;
using UnityEngine;

public class TargetSwitcher : MonoBehaviour
{
    private TargetCollector m_collector;
    private Transform m_origin;

    public bool m_targetChanged = false;

    public GameObject m_target;

    public LayerMask m_targetMask;

    private Dictionary<Vector3, float> m_targetInfo = new Dictionary<Vector3, float>();

    private void Awake()
    {
        m_collector = GetComponent<TargetCollector>();
        m_origin = GameObject.Find("TargetOrigin").transform;
        m_tracker = GameObject.Find("TargetTracker").transform;
        m_bound = GetComponent<BoxCollider>();
    }

    public void UpdateBearing(GameObject origin)
    {
        m_origin = origin.transform;

        foreach (GameObject target in m_collector.m_targetList)
        {
            Vector3 dir = (target.transform.position - origin.transform.position).normalized
            float dist = Vector3.Distance(origin.transform.position, target.transform.position);

            m_targetInfo.Add(dir, dist);
        }
    }

    public void SwitchTarget()
    {
        if (!m_targetChanged)
        {
            Vector2 dir = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")).normalized;

            // Find closest direction value from Dictionary to dir of here
            // Compare distance from origin if multiple targets and choose the nearest
        }
    }

    public void ReturnToIdle()
    {
        m_origin.position = m_target.transform.position;
        m_targetChanged = false;
        m_targetInfo.Clear();
    }

    public struct TargetInfo
    {
        public Vector3 bearing;
        public float distance;

        public TargetInfo(Vector3 bearing, float distance)
        {
            this.bearing = bearing;
            this.distance = distance;
        }
    }
}

一般来说,我试图将方向输入的归一化向量与从原点到每个目标的归一化向量在SwitchTarget()之前进行比较。这里的输入方法是游戏手柄轴x和y作为水平和垂直方向。
由于提供的答案与问题相差甚远并被标记为重复发布,因此再次发布此问题(给出的答案仅关于仅按距离查找gameObject,而此问题涉及方向,并且当在方向上发现多个物品时,需要进行二次比较距离部分)。
编辑
经过一些点积试验,现在我确定这更有可能是我想要的方向。尽管还有很多不一致之处需要解决。 以下是我最近的尝试:
private void Update()
{
    UpdateBearing();

    Vector3 input = new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"), 0);

    if (input != Vector3.zero)
    {
        SwitchTarget();
    }
}

public void UpdateBearing(GameObject origin)
{
    m_origin.position = origin.transform.position;

    foreach (GameObject target in m_collector.m_targetList)
    {
        Vector3 dir = (target.transform.position - origin.transform.position).normalized;
        if (!m_targetInfo.ContainsKey(target))
        {
            m_targetInfo.Add(target, dir);
        }
    }
}

public void SwitchTarget()
{
    GameObject oldTarget = m_collector.m_target;

    if (!m_targetChanged)
    {
        Vector3 dir = new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"), 0).normalized;
        Debug.DrawRay(m_origin.position, dir * 100, Color.yellow, 0.5f);

        foreach (KeyValuePair<GameObject, Vector3> possibleTarget in m_targetInfo)
        {
            float dot = Vector3.Dot(dir, possibleTarget.Value);

            if (dot > 0.5f) // Compare DP difference of added dot and input dot
            {
                GameObject newTarget = possibleTarget.Key;

                if (oldTarget != newTarget)
                {
                    Debug.Log(possibleTarget.Value + " // " + dot);

                    m_target = newTarget;
                    m_collector.m_target = newTarget;
                    m_targetChanged = true;
                }
            }
        }
    }
}

这样,我有点像是在没有使用光线投射并且不错过任何目标的情况下获取了gameObject选择。但是,我确定我需要更好的情况比较而不是if(dot > 0.5f)。此外,我的粗略假设是,如果我不为每个Key更新字典m_targetInfo的值,如果那些目标移动,我会有另一个不一致性。无论如何,我仍然困惑如何正确利用这个来实现我的最终目标。

1个回答

3

既然您已经拥有了所需的游戏对象,您可以创建一个for循环并检查您的视线方向与它们位置之间的角度,如果角度小于某个值(您可以将其设置得非常低以使其精确或略高以允许一些误差),则将其放入游戏对象列表中,如果有多个对象,请选择最近的对象。

获取角度最接近的对象的代码如下:

GameObject CheckObjects()
{
    List<GameObject> InAngle = new List<GameObject>();

    for(int i = 0; i < YourObjectsList.Count; i++)
    {
        GameObject tested = YourObjectsList[i];

        Vector3 dir = tested.transform.position - origin.transform.forward;

        // I'm assuming here that youre rotating your origin with the 
        directional input, not then instead of origin.transform.forward 
        place there your directional vector3

        float angle = Vector3.Angle(dir, tested.transform.position);

        if(angle <= desiredAngle)
        {
            InAngle.Add(tested);
        }
    }

    GameObject closest = null;

    for(int j = 0; j < InAngle.Count; i++)
    {
        GameObject tested = InAngle[i];

        Vector3 dir1 = tested.transform.position - origin.transform.position;
        Vector3 dir2 = closest.transform.position - origin.transform.position;

        if(!closest)
        {
            closest = tested;
        }
        else
        {
            if(dir2.sqrMagnitude > dir1.sqrMagnitude)
            {
                closest = tested;
            }
        }
    }

    return closest;
}

感谢您的示例和提示!在您的回答之前,我只是在研究获取近似值并进行比较。如果您看到我的图表,当原点设置在目标向量3上时,列表中的所有其他可能目标都将具有来自原点的方向作为规范化向量3。我尝试存储这些规范化向量,并查找和比较那些存储的值与方向输入,该输入也返回规范化向量3。我正在尝试像您示例代码中所示的那样循环遍历它们,但我认为我仍然偏离了正确/最有效的轨道。 - Arcana Affinity
如果你的起点是动态的,那么在字典中存储方向和距离其实毫无意义,最好的做法是将游戏对象存储在列表中,每次需要获取方向时只需运行函数即可,因为这并不太耗费资源。我相信这比每次移动时更新字典要更加高效。 - Fiffe
2
优化时需要考虑几个因素。首先,我建议使用点积而不是角度,因为角度使用余弦函数。点积只是基本算术运算。您可以将其与实际角度值进行比较,也可以将其与0.7进行比较,以获得90度的视野范围。其次,您可以使用sqrMagnitude而不是Distance。这应该避免了平方根计算。有了这两个优化,我不能保证但我认为它会运行得更快。 - Everts
1
点乘意味着比较两个向量,如果值为负,则表示角度大于180度,因此在后面。如果是1,则表示90度,如果是0,则正好在前方,两个向量对齐。cos(45)大约为0.7,因此表示左右各45度,因此为90度。点乘只涉及加法和乘法。如果您在平面系统上,则甚至可以丢弃所有计算的z部分。 - Everts
1
如果有助于实现目标,那么随时熟悉新事物都是一个好时机。 - Fiffe
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接