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 "未知"; } }
关键改进点
- 准确的交点计算:使用相机视锥体平面检测,替代固定距离延长,精准找到射线与屏幕边缘的交点
- 视野判断逻辑:先判断敌人是否在视野内,仅在视野外显示警示
- 方向向量修正:计算玩家指向敌人的方向,而非反向
- 玩家视角方位判断:基于相机相对方向输出更符合直觉的方位描述
- UGUI替代OnGUI:相比OnGUI,UGUI性能更优、布局更灵活
内容的提问来源于stack exchange,提问作者RajatRaja
相关产品推荐
相关产品推荐

