RayCast无法正常工作,请帮助! (Unity 3D)

时间:2020-09-06 20:17:37

标签: visual-studio unity3d raycasting

好的,所以我正在做一个摄影游戏,当您“拍照”时,Unity会向前发送一些射线广播,以检查照片中是否有某些标记的物品(全部在相机FOV内)。我的问题是,这似乎间歇性地工作!有时它会找到标记的对象,有时它会恰好在视图的前面,但却会完全丢失它!谁能告诉我我在做什么错?

   aggregatedBySum 
      21 
      22 

我实质上是使用要检查的标签调用ObjectCheck()以获得具有该标签的最近的,可见的对象。这段代码有什么问题?

1 个答案:

答案 0 :(得分:0)

在脚本中,仅检查距离主摄像机最近的对象。 SortObjects()确定标记最近的对象,然后您仅处理ObjectCheck()中的单个对象。 -该对象可能被其他物体阻挡,因此该方法返回false。以及其他实际上可见的带标签的对象,不会以这种方式拾取...

因此,您可以重命名和更改SortObjects()函数,以在循环(InFront(target) && HasLineOfSight(objectTag))中检查两个条件,并在那里过滤对象,因为只有那些对象才是您感兴趣的对象

此外,您的HasLineOfSight()方法会检查命中对象的标签,但是您可能想做的是检查光线投射是否确实命中了该精确对象。因此,它应该将匹配的gameObject与目标的gameObject进行比较,而忽略标签,因为仅靠正确的标签是不够的。 (附带说明:将所有“可摄影对象”放置在“照片层”上,并相应地在Physics.Raycast()调用中设置图层蒙版,在较大的场景中这样更有效。)

InFront()方法中计算角度的方式可能会引起问题,因为指向目标的方向向量实际上是3D的。要计算角度,您可以尝试使用Vector3.Project()Vector3.ProjectOnPlane(),但是由于透视相机的问题,这也有问题。

此检查与通常用于渲染的“视锥剔除”主题密切相关。但这类似于您需要的,以滤除相机视场中的所有(可能)可见对象(视锥剔除不能处理障碍物,这只是一种几何检查,以查看点是否位于相机的视锥空间内)。参见:

Unity doc: Understanding the View Frustum

如果您想更深入地研究和优化它,可以通过两种方法完成。但是幸运的是,Unity附带了许多已经内置在Camera class中的有用的相关功能。因此,您可以使用Camera.WorldToScreenPoint()(或Camera.WorldToViewportPoint()),并将生成的屏幕坐标与屏幕尺寸或视口like discussed in Unity forum进行比较。 (平截头数学隐藏在这些紧凑函数的后面,但是请注意,这可能不是实现此目的的最佳方法。)

假设每次游戏运行时都不会创建/销毁对象,则只能在FindGameObjectsWithTag()中执行一次,而不是每次都调用Start()

我已经尝试过修改脚本,因为我也正在再次学习Unity ...可以将该脚本拖到主摄像头,并且该脚本应在“场景”视图中显示绿色的“调试”对象。线。我希望这会有所帮助:

using UnityEngine;

[RequireComponent(typeof(Camera))]
public class PhotoCast : MonoBehaviour
{
    public float maxDistance = 250.0f;
    public string objectTag = "photo";
    protected GameObject[] objs;
    protected GameObject objFocus;
    protected Camera cam;

    public void Start() {
        objs = GameObject.FindGameObjectsWithTag(objectTag);
        cam = GetComponent<Camera>();
    }
    public void Update() {
        if (Input.GetButtonDown("Fire1")) {
            objFocus = CheckObjects();
            if (objFocus) {
                Debug.Log("closest object in view: " + objFocus.name);
                /* TODO: take actual photo here */
            }
        }
        if (objFocus) {
            Debug.DrawLine(transform.position,
              objFocus.transform.position, Color.green);
        }
    }
    GameObject CheckObjects() {
        GameObject obj_closest = null;
        float dist_closest = float.MaxValue;
        foreach (GameObject o in objs) {
            float dist = Vector3.Distance(
              o.transform.position, transform.position);
            if (dist < maxDistance && dist < dist_closest
                  && InViewport(o.transform.position)
                  && HasLineOfSight(o.transform)) {
                dist_closest = dist;
                obj_closest = o;
            }
        }
        return obj_closest;
    }
    bool InViewport(Vector3 worldPos) {
        Vector3 p = cam.WorldToViewportPoint(worldPos);
        return (p.x > 0.0f && p.x <= 1.0f && p.y > 0.0f && p.y <= 1.0f
            && p.z > cam.nearClipPlane);
    }
    bool HasLineOfSight(Transform target) {
        RaycastHit hit;
        Vector3 dir = target.position - transform.position;
        if (Physics.Raycast(transform.position, dir, out hit, maxDistance)) {
            if (hit.collider.gameObject == target.gameObject) {
                return true;
            }
        }
        return false;
    }
}

旁注:

此技术的另一个问题是,可以在相机的正前方放置标记的对象,但是将拾取侧面较近的其他标记的对象,而不是明显的对象。我猜有很多小问题需要微调,直到脚本适合游戏为止。不仅可以使用多个对象,而且可以考虑边界框或实际对撞机形状,而不是每个对象仅使用一个Raycast。

脚本的改进版本可以利用Physics.Overlap *()或Physics。* Cast *()函数,已在here中进行了记录。