153 lines
4.9 KiB
C#
153 lines
4.9 KiB
C#
using BigSpace.XRCore.Base;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
|
||
public class LinerMgr : MonoSingleton<LinerMgr>
|
||
{
|
||
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<Transform> arrowList = new List<Transform>();
|
||
|
||
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<Camera>();
|
||
}
|
||
|
||
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);
|
||
}
|
||
}
|
||
}
|