Files
Zombie/Assets/_Valheim/Scripts/Player/ArrowPathIndicator.cs
2025-12-03 11:25:14 +08:00

388 lines
11 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;
public class ArrowPathIndicator : NetworkBehaviour
{
[Header("箭头设置")]
public GameObject arrowPrefab; // 单个箭头预制体
public int maxArrows = 20; // 最大箭头数量
public float arrowSpacing = 2.0f; // 箭头间距
public float arrowHeight = 1.5f; // 箭头高度
public LayerMask obstacleMask = 1; // 障碍物层
[Header("路径平滑")]
public int pathSmoothingIterations = 5; // 路径平滑迭代次数
public float smoothFactor = 0.5f; // 平滑因子
[Header("动画设置")]
public float arrowMoveSpeed = 1.0f; // 箭头移动速度
public float arrowBobHeight = 0.2f; // 箭头上下浮动高度
public float arrowBobSpeed = 2.0f; // 箭头浮动速度
// 内部变量
private List<GameObject> arrowInstances = new List<GameObject>();
private List<Vector3> currentPath = new List<Vector3>();
private List<Vector3> smoothedPath = new List<Vector3>();
private Vector3 lastStartPoint = Vector3.zero;
private Vector3 lastEndPoint = Vector3.zero;
private float updateCooldown = 0.5f;
private float lastUpdateTime = 0f;
private float arrowBobOffset = 0f;
void Awake()
{
// 初始化箭头池
InitializeArrowPool();
}
void Update()
{
// 更新箭头浮动动画
if (arrowInstances.Count > 0)
{
UpdateArrowAnimation();
}
}
/// <summary>
/// 初始化箭头对象池
/// </summary>
private void InitializeArrowPool()
{
// 清除现有箭头
foreach (GameObject arrow in arrowInstances)
{
if (arrow != null)
Destroy(arrow);
}
arrowInstances.Clear();
// 创建新的箭头池
for (int i = 0; i < maxArrows; i++)
{
if (arrowPrefab != null)
{
GameObject arrow = Instantiate(arrowPrefab, transform);
arrow.SetActive(false);
arrowInstances.Add(arrow);
}
}
}
/// <summary>
/// 设置路径
/// </summary>
public void SetPath(Vector3 start, Vector3 end)
{
// 检查更新冷却
if (Time.time - lastUpdateTime < updateCooldown &&
Vector3.Distance(start, lastStartPoint) < 1.0f &&
Vector3.Distance(end, lastEndPoint) < 1.0f)
{
return;
}
lastStartPoint = start;
lastEndPoint = end;
lastUpdateTime = Time.time;
// 计算路径
CalculatePath(start, end);
// 平滑路径
smoothedPath = SmoothPath(currentPath);
// 更新箭头显示
UpdateArrowsOnPath();
}
/// <summary>
/// 计算路径
/// </summary>
private void CalculatePath(Vector3 start, Vector3 end)
{
currentPath.Clear();
currentPath.Add(start);
// 检查直接路径是否畅通
if (IsDirectPathClear(start, end))
{
// 直接路径,添加中间点
AddPointsAlongLine(start, end, arrowSpacing);
}
else
{
// 需要绕行,寻找绕过点
Vector3 bypassPoint = FindBypassPoint(start, end);
if (bypassPoint != start)
{
// 从起点到绕过点
AddPointsAlongLine(start, bypassPoint, arrowSpacing);
// 从绕过点到终点
AddPointsAlongLine(bypassPoint, end, arrowSpacing);
}
else
{
// 找不到绕过点,使用直线
AddPointsAlongLine(start, end, arrowSpacing);
}
}
currentPath.Add(end);
}
/// <summary>
/// 检查直接路径是否畅通
/// </summary>
private bool IsDirectPathClear(Vector3 start, Vector3 end)
{
Vector3 direction = (end - start).normalized;
float distance = Vector3.Distance(start, end);
// 分段检查
int segments = Mathf.CeilToInt(distance / 1.0f);
for (int i = 0; i <= segments; i++)
{
float t = (float)i / segments;
Vector3 checkPoint = Vector3.Lerp(start, end, t);
if (Physics.CheckSphere(checkPoint, 0.5f, obstacleMask))
{
return false;
}
}
return true;
}
/// <summary>
/// 在直线上添加等间距点
/// </summary>
private void AddPointsAlongLine(Vector3 start, Vector3 end, float spacing)
{
Vector3 direction = (end - start).normalized;
float distance = Vector3.Distance(start, end);
int points = Mathf.FloorToInt(distance / spacing);
for (int i = 1; i <= points; i++)
{
float t = (float)i / (points + 1);
Vector3 point = Vector3.Lerp(start, end, t);
currentPath.Add(point);
}
}
/// <summary>
/// 寻找绕过障碍物的点
/// </summary>
private Vector3 FindBypassPoint(Vector3 start, Vector3 end)
{
Vector3 direction = (end - start).normalized;
float distance = Vector3.Distance(start, end);
// 尝试多个方向
Vector3[] testDirections = {
Vector3.Cross(direction, Vector3.up).normalized, // 右侧
-Vector3.Cross(direction, Vector3.up).normalized, // 左侧
(direction + Vector3.up * 0.5f).normalized, // 前上方
(direction - Vector3.up * 0.5f).normalized // 前下方
};
float[] testDistances = { 2f, 3f, 4f };
foreach (Vector3 testDir in testDirections)
{
foreach (float testDist in testDistances)
{
Vector3 testPoint = start + testDir * testDist;
// 检查测试点是否可行
if (!Physics.CheckSphere(testPoint, 0.5f, obstacleMask))
{
// 检查从起点到测试点,以及从测试点到终点是否都畅通
if (IsDirectPathClear(start, testPoint) &&
IsDirectPathClear(testPoint, end))
{
return testPoint;
}
}
}
}
return start; // 没找到绕过点
}
/// <summary>
/// 平滑路径
/// </summary>
private List<Vector3> SmoothPath(List<Vector3> path)
{
if (path.Count < 3)
return new List<Vector3>(path);
List<Vector3> smoothed = new List<Vector3>(path);
for (int iteration = 0; iteration < pathSmoothingIterations; iteration++)
{
List<Vector3> newPath = new List<Vector3>();
newPath.Add(smoothed[0]); // 保持起点不变
// 对中间点进行平滑
for (int i = 1; i < smoothed.Count - 1; i++)
{
Vector3 prev = smoothed[i - 1];
Vector3 current = smoothed[i];
Vector3 next = smoothed[i + 1];
// 使用加权平均进行平滑
Vector3 smoothedPoint = current * (1 - smoothFactor * 2) +
(prev + next) * smoothFactor;
newPath.Add(smoothedPoint);
}
newPath.Add(smoothed[smoothed.Count - 1]); // 保持终点不变
smoothed = newPath;
}
return smoothed;
}
/// <summary>
/// 更新路径上的箭头
/// </summary>
private void UpdateArrowsOnPath()
{
// 隐藏所有箭头
foreach (GameObject arrow in arrowInstances)
{
if (arrow != null)
arrow.SetActive(false);
}
if (smoothedPath.Count < 2)
return;
// 计算需要在哪些位置显示箭头
List<Vector3> arrowPositions = new List<Vector3>();
// 沿路径等间距取样
float accumulatedDistance = 0f;
for (int i = 0; i < smoothedPath.Count - 1; i++)
{
Vector3 segmentStart = smoothedPath[i];
Vector3 segmentEnd = smoothedPath[i + 1];
float segmentLength = Vector3.Distance(segmentStart, segmentEnd);
// 在当前段内放置箭头
while (accumulatedDistance < segmentLength)
{
float t = accumulatedDistance / segmentLength;
Vector3 arrowPos = Vector3.Lerp(segmentStart, segmentEnd, t);
arrowPositions.Add(arrowPos);
accumulatedDistance += arrowSpacing;
}
accumulatedDistance -= segmentLength;
}
// 限制箭头数量
int arrowCount = Mathf.Min(arrowPositions.Count, maxArrows);
// 激活并放置箭头
for (int i = 0; i < arrowCount; i++)
{
if (i < arrowInstances.Count)
{
GameObject arrow = arrowInstances[i];
// 设置位置
Vector3 arrowPos = arrowPositions[i];
arrowPos.y = arrowHeight;
arrow.transform.position = arrowPos;
// 设置旋转(朝向路径方向)
if (i < arrowPositions.Count - 1)
{
Vector3 lookDirection = (arrowPositions[i + 1] - arrowPositions[i]).normalized;
if (lookDirection.magnitude > 0.01f)
{
Quaternion lookRotation = Quaternion.LookRotation(lookDirection);
arrow.transform.rotation = lookRotation;
}
}
else if (i > 0)
{
// 最后一个箭头使用前一个方向
Vector3 lookDirection = (arrowPositions[i] - arrowPositions[i - 1]).normalized;
if (lookDirection.magnitude > 0.01f)
{
Quaternion lookRotation = Quaternion.LookRotation(lookDirection);
arrow.transform.rotation = lookRotation;
}
}
arrow.SetActive(true);
}
}
}
/// <summary>
/// 更新箭头动画
/// </summary>
private void UpdateArrowAnimation()
{
arrowBobOffset += Time.deltaTime * arrowBobSpeed;
for (int i = 0; i < arrowInstances.Count; i++)
{
GameObject arrow = arrowInstances[i];
if (arrow != null && arrow.activeSelf)
{
// 上下浮动
Vector3 pos = arrow.transform.position;
float bob = Mathf.Sin(arrowBobOffset + i * 0.5f) * arrowBobHeight;
pos.y = arrowHeight + bob;
arrow.transform.position = pos;
// 可选:添加旋转动画
arrow.transform.Rotate(0, arrowMoveSpeed * Time.deltaTime * 30f, 0);
}
}
}
/// <summary>
/// 显示指引
/// </summary>
public void ShowPath()
{
gameObject.SetActive(true);
}
/// <summary>
/// 隐藏指引
/// </summary>
public void HidePath()
{
gameObject.SetActive(false);
}
/// <summary>
/// 清理资源
/// </summary>
public void Cleanup()
{
foreach (GameObject arrow in arrowInstances)
{
if (arrow != null)
Destroy(arrow);
}
arrowInstances.Clear();
if (gameObject != null)
Destroy(gameObject);
}
}