Files
PrinceOfGlory/Assets/Scripts/ExitMap/LinerMgr.cs
kridoo 333a4f3ecb 1
2025-12-09 11:28:02 +08:00

153 lines
4.9 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
}
}
}