using BigSpace.XRCore.Base; using System.Collections; using System.Collections.Generic; using UnityEngine; public class LinerMgr : MonoSingleton { public Transform m_target; public float height = 0.5f; public float arrowLength = 0.5f; public float speed = 1f; public float groundHeight = 0.25f; // 地面导航时的高度 public float startDistance = 2f; // 箭头起始距离(相机前方) public float backStartDistance = 5f; // 目标在后方时,箭头起始距离(更远) public GameObject arrowPrefab; private Vector3 target; private List arrowList = new List(); private bool start = false; // Start is called before the first frame update public void SetPositions(Vector3 pos) { target = pos; UpdateArrow(); } public void ShowArrow(bool display, Transform target) { if (display) { Vector3 pos = target.position; this.target = pos; } else DeActiveArrow(); start = display; } public void DeActiveArrow() { start = false; arrowList.ForEach(o => o.gameObject.SetActive(false)); } void Update() { if (start) { UpdateArrow(); } } void UpdateArrow() { Vector3 actualTarget = target; // 统一使用地面导航模式 Camera mainCamera = Camera.main; if (mainCamera == null) { mainCamera = FindObjectOfType(); } if (mainCamera != null && m_target != null) { actualTarget = m_target.position; // 计算目标相对于相机的方向 Vector3 cameraPos = mainCamera.transform.position; Vector3 cameraForward = mainCamera.transform.forward; Vector3 toTarget = actualTarget - cameraPos; float distanceToTarget = toTarget.magnitude; Vector3 toTargetDir = toTarget.normalized; // 判断目标是否在相机后方(点积小于0表示在后方) float dotProduct = Vector3.Dot(cameraForward, toTargetDir); bool isBehind = dotProduct < 0f; // 计算箭头起始位置 - 始终在相机前方,这样玩家才能看到 Vector3 startPos; if (isBehind) { // 目标在后方:箭头起始点在相机前方,但箭头会向后指 // 使用更远的距离,让箭头更明显 startPos = cameraPos + cameraForward * backStartDistance; } else { // 目标在前方或侧面:从相机前方开始,稍微偏向目标方向 Vector3 startDir = Vector3.Slerp(cameraForward, toTargetDir, 0.3f).normalized; startPos = cameraPos + startDir * startDistance; } // 设置高度为接近地面的高度(目标高度 + 地面高度偏移) startPos.y = actualTarget.y + groundHeight; transform.position = startPos; } else if (m_target != null) { // 没有相机,使用目标位置 actualTarget = m_target.position; } float distance = Vector3.Distance(transform.position, actualTarget); float radius = groundHeight / 2f + distance * distance / (8f * groundHeight); float diff = radius - groundHeight; float angle = 2f * Mathf.Acos(diff / radius); float length = angle * radius; float segmentAngle = arrowLength / radius * Mathf.Rad2Deg; Vector3 center = new Vector3(0, -diff, distance / 2f); int segmentsCount = (int)(length / arrowLength) + 1; ArrowInit(segmentsCount); float offset = Time.time * speed * segmentAngle; Vector3 firstSegmentPos = Quaternion.Euler(Mathf.Repeat(offset, segmentAngle), 0f, 0f) * (Vector3.zero - center) + center; for (int i = 0; i < segmentsCount; i++) { Vector3 pos = Quaternion.Euler(segmentAngle * i, 0f, 0f) * (firstSegmentPos - center) + center; arrowList[i].localPosition = pos; arrowList[i].localRotation = Quaternion.FromToRotation(Vector3.up, pos - center); } if (m_target != null) { transform.LookAt(actualTarget); } } void ArrowInit(int listCount) { while (arrowList.Count < listCount) { GameObject arrow = Instantiate(arrowPrefab, transform); arrowList.Add(arrow.transform); } for (int i = 0; i < arrowList.Count; i++) { GameObject arrow = arrowList[i].gameObject; if (arrow.activeSelf != i < listCount) arrow.SetActive(i < listCount); } } }