You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中如何实现敌人距离与方向的警示显示?

Unity 屏幕外敌人距离与方向警示实现问题

问题可视化图示

我正在Unity中开发一款游戏,敌人(鸟类)会在玩家一定距离外生成,但有时这些敌人会出现在相机视野之外,导致玩家无法预判威胁。我需要实现一个系统,在屏幕边缘显示敌人的距离和方向警示。

预期目标

  • 当鸟类在玩家视野外生成时,计算玩家与鸟类的连线和屏幕/相机视野边缘的交点
  • 在该交点位置显示敌人的距离和警示标识

我编写了一段代码但未能按预期工作,求指导实现思路或修正方案:

using UnityEngine;

public class BirdDistance : MonoBehaviour
{
    public Transform player1;
    public Camera mainCamera;

    private void Start()
    {
        player1 = GameObject.FindWithTag("Player").transform;
        mainCamera = Camera.main;
    }

    private void OnGUI()
    {
        // 计算玩家到鸟类的方向向量
        Vector3 directionToPlayer = (player1.position - transform.position).normalized;

        // 计算与屏幕边缘的交点
        Vector3 intersectionPoint = CalculateIntersectionPoint(directionToPlayer);

        // 将交点转换为屏幕空间坐标
        Vector3 screenIntersectionPoint = mainCamera.WorldToScreenPoint(intersectionPoint);

        // 检查交点是否在屏幕范围内
        if (IsPointWithinScreenBounds(screenIntersectionPoint))
        {
            // 计算玩家与鸟类之间的距离
            float distance = Vector3.Distance(player1.position, transform.position);

            // 在屏幕上显示距离和方向信息
            GUI.Label(new Rect(screenIntersectionPoint.x, Screen.height - screenIntersectionPoint.y, 200, 50),
                      "Bird approaching from " + GetDirectionFromScreenPoint(screenIntersectionPoint) + "\nDistance: " + distance.ToString("F2"));
        }
    }

    private Vector3 CalculateIntersectionPoint(Vector3 direction)
    {
        // 计算与屏幕边缘的交点
        float t = 100f; // 根据需要调整该值
        Vector3 intersectionPoint = transform.position + direction * t;
        return intersectionPoint;
    }

    private bool IsPointWithinScreenBounds(Vector3 point)
    {
        // 检查点是否在屏幕范围内
        return point.x >= 0 && point.x <= Screen.width && point.y >= 0 && point.y <= Screen.height;
    }

    private string GetDirectionFromScreenPoint(Vector3 screenPoint)
    {
        // 根据屏幕点判断方向
        float screenWidth = Screen.width;
        float screenHeight = Screen.height;

        if (screenPoint.x <= 0)
        {
            return "left";
        }
        else if (screenPoint.x >= screenWidth)
        {
            return "right";
        }
        else if (screenPoint.y <= 0)
        {
            return "bottom";
        }
        else
        {
            return "top";
        }
    }
}

问题分析与修正方案

原代码存在几个核心问题:方向向量逻辑反转、交点计算不准确、视野判断逻辑错误,以下是修正后的实现方案:

using UnityEngine;

public class BirdOffScreenIndicator : MonoBehaviour
{
    public Transform player;
    public Camera mainCamera;
    public GameObject indicatorPrefab; // 建议使用UGUI预制体作为警示标识
    private GameObject currentIndicator;

    private void Start()
    {
        player = GameObject.FindWithTag("Player").transform;
        mainCamera = Camera.main;
        
        if (indicatorPrefab != null)
        {
            currentIndicator = Instantiate(indicatorPrefab, Canvas.main.transform);
            currentIndicator.SetActive(false);
        }
    }

    private void Update()
    {
        if (player == null || mainCamera == null) return;

        // 判断敌人是否在相机视野内
        Vector3 screenPos = mainCamera.WorldToScreenPoint(transform.position);
        bool isVisible = screenPos.z > 0 && screenPos.x >= 0 && screenPos.x <= Screen.width && screenPos.y >= 0 && screenPos.y <= Screen.height;

        if (isVisible)
        {
            if (currentIndicator != null) currentIndicator.SetActive(false);
            return;
        }

        // 计算玩家指向敌人的方向向量
        Vector3 directionToEnemy = (transform.position - player.position).normalized;
        // 获取射线与屏幕边缘的交点
        Vector3 screenEdgePoint = GetScreenEdgeIntersection(directionToEnemy);

        // 更新警示标识的位置与内容
        if (currentIndicator != null)
        {
            currentIndicator.SetActive(true);
            RectTransform rt = currentIndicator.GetComponent<RectTransform>();
            rt.anchoredPosition = new Vector2(screenEdgePoint.x - Screen.width / 2f, screenEdgePoint.y - Screen.height / 2f);
            
            float distance = Vector3.Distance(player.position, transform.position);
            TMPro.TextMeshProUGUI text = currentIndicator.GetComponentInChildren<TMPro.TextMeshProUGUI>();
            if (text != null)
            {
                text.text = $"鸟类来袭\n方向: {GetCardinalDirection(directionToEnemy)}\n距离: {distance:F2}";
            }
        }
    }

    private Vector3 GetScreenEdgeIntersection(Vector3 direction)
    {
        Ray ray = new Ray(player.position, direction);
        Plane[] planes = GeometryUtility.CalculateFrustumPlanes(mainCamera);

        float closestDistance = float.MaxValue;
        Vector3 intersectionPoint = Vector3.zero;

        // 检测射线与视锥体四个侧面的交点(忽略近远平面)
        for (int i = 0; i < 4; i++)
        {
            if (planes[i].Raycast(ray, out float distance))
            {
                if (distance < closestDistance)
                {
                    closestDistance = distance;
                    intersectionPoint = ray.GetPoint(distance);
                }
            }
        }

        return mainCamera.WorldToScreenPoint(intersectionPoint);
    }

    private string GetCardinalDirection(Vector3 direction)
    {
        // 将世界方向转换为相机相对方向
        Vector3 localDir = mainCamera.transform.InverseTransformDirection(direction);
        localDir.y = 0; // 忽略垂直方向,只计算水平方位
        localDir.Normalize();

        float angle = Mathf.Atan2(localDir.x, localDir.z) * Mathf.Rad2Deg;
        if (angle < 0) angle += 360;

        // 判断方位
        if (angle >= 337.5f || angle < 22.5f) return "前";
        if (angle >= 22.5f && angle < 67.5f) return "右前";
        if (angle >= 67.5f && angle < 112.5f) return "右";
        if (angle >= 112.5f && angle < 157.5f) return "右后";
        if (angle >= 157.5f && angle < 202.5f) return "后";
        if (angle >= 202.5f && angle < 247.5f) return "左后";
        if (angle >= 247.5f && angle < 292.5f) return "左";
        if (angle >= 292.5f && angle < 337.5f) return "左前";

        return "未知";
    }
}

关键改进点

  1. 准确的交点计算:使用相机视锥体平面检测,替代固定距离延长,精准找到射线与屏幕边缘的交点
  2. 视野判断逻辑:先判断敌人是否在视野内,仅在视野外显示警示
  3. 方向向量修正:计算玩家指向敌人的方向,而非反向
  4. 玩家视角方位判断:基于相机相对方向输出更符合直觉的方位描述
  5. UGUI替代OnGUI:相比OnGUI,UGUI性能更优、布局更灵活

内容的提问来源于stack exchange,提问作者RajatRaja

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 17:28:10