317 lines
10 KiB
C#
317 lines
10 KiB
C#
using System.Collections.Generic;
|
||
using FluffyUnderware.Curvy;
|
||
using UnityEngine;
|
||
|
||
/// <summary>
|
||
/// 漂流瓶路径生成器
|
||
/// 每次生成随机变化的路径,确保漂流瓶从各种角度出现
|
||
/// </summary>
|
||
public class DriftBottlePathGenerator : MonoBehaviour
|
||
{
|
||
[Header("路径随机配置")]
|
||
[Tooltip("最小起始距离")]
|
||
public float minStartDistance = 1f;
|
||
|
||
[Tooltip("最大起始距离")]
|
||
public float maxStartDistance = 15f;
|
||
|
||
[Tooltip("最小结束距离")]
|
||
public float minEndDistance = 5f;
|
||
|
||
[Tooltip("最大结束距离")]
|
||
public float maxEndDistance = 12f;
|
||
|
||
[Tooltip("最小水平角度范围(度)")]
|
||
public float minHorizontalAngle = -75f;
|
||
|
||
[Tooltip("最大水平角度范围(度)")]
|
||
public float maxHorizontalAngle = 75f;
|
||
|
||
[Tooltip("最小垂直角度(度,负值表示从下方来)")]
|
||
public float minVerticalAngle = -20f;
|
||
|
||
[Tooltip("最大垂直角度(度)")]
|
||
public float maxVerticalAngle = 45f;
|
||
|
||
[Tooltip("最小高度")]
|
||
public float minHeight = 0.5f;
|
||
|
||
[Tooltip("最大高度")]
|
||
public float maxHeight = 5f;
|
||
|
||
[Tooltip("路径曲率范围")]
|
||
public float minCurvature = 1f;
|
||
|
||
[Tooltip("路径曲率范围")]
|
||
public float maxCurvature = 6f;
|
||
|
||
[Header("生成的路径")]
|
||
public List<CurvySpline> generatedPaths = new List<CurvySpline>();
|
||
|
||
[Header("路径历史(用于避免重复)")]
|
||
[Tooltip("记住最近几次的角度,避免重复")]
|
||
public int rememberRecentPaths = 3;
|
||
|
||
private List<float> recentAngles = new List<float>();
|
||
private Transform playerTransform;
|
||
|
||
private void Start()
|
||
{
|
||
playerTransform = GameInit.Ins?.playerCam?.transform;
|
||
if (playerTransform == null)
|
||
{
|
||
Debug.LogError("[DriftBottlePathGenerator] 无法获取玩家相机!");
|
||
return;
|
||
}
|
||
|
||
// 初始生成多条备用路径
|
||
GenerateRandomPaths(5);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成指定数量的随机路径
|
||
/// </summary>
|
||
public void GenerateRandomPaths(int count)
|
||
{
|
||
ClearExistingPaths();
|
||
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
CurvySpline path = CreateRandomPath(i);
|
||
if (path != null)
|
||
{
|
||
generatedPaths.Add(path);
|
||
}
|
||
}
|
||
|
||
Debug.Log($"[DriftBottlePathGenerator] 生成了{generatedPaths.Count}条随机漂流瓶路径");
|
||
|
||
// 路径现在通过 GetRandomPath() 方法动态获取,无需预先传递给管理器
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取一条随机路径(用于生成漂流瓶时)
|
||
/// 每次调用都会生成一条新的随机路径替换旧的
|
||
/// </summary>
|
||
public CurvySpline GetRandomPath()
|
||
{
|
||
if (generatedPaths.Count == 0)
|
||
{
|
||
GenerateRandomPaths(3);
|
||
}
|
||
|
||
// 随机选择一条路径
|
||
if (generatedPaths.Count > 0)
|
||
{
|
||
int randomIndex = Random.Range(0, generatedPaths.Count);
|
||
CurvySpline selectedPath = generatedPaths[randomIndex];
|
||
|
||
// 生成一条新路径替换这条,确保下次更随机
|
||
CurvySpline newPath = CreateRandomPath(randomIndex);
|
||
if (newPath != null)
|
||
{
|
||
// 删除旧路径
|
||
if (generatedPaths[randomIndex] != null)
|
||
{
|
||
Destroy(generatedPaths[randomIndex].gameObject);
|
||
}
|
||
generatedPaths[randomIndex] = newPath;
|
||
}
|
||
|
||
return selectedPath;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建一条完全随机的路径
|
||
/// </summary>
|
||
private CurvySpline CreateRandomPath(int index)
|
||
{
|
||
// 随机生成角度(避免与最近的路径太接近)
|
||
float horizontalAngle = GetRandomAngle();
|
||
float verticalAngle = Random.Range(minVerticalAngle, maxVerticalAngle);
|
||
|
||
// 随机距离
|
||
float startDistance = Random.Range(minStartDistance, maxStartDistance);
|
||
float endDistance = Random.Range(minEndDistance, maxEndDistance);
|
||
float height = Random.Range(minHeight, maxHeight);
|
||
float curvature = Random.Range(minCurvature, maxCurvature);
|
||
|
||
return CreatePathWithParams(index, horizontalAngle, verticalAngle, startDistance, endDistance, height, curvature);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取随机角度(避免与最近的角度重复)
|
||
/// </summary>
|
||
private float GetRandomAngle()
|
||
{
|
||
float angle;
|
||
int maxAttempts = 10;
|
||
int attempts = 0;
|
||
|
||
do
|
||
{
|
||
angle = Random.Range(minHorizontalAngle, maxHorizontalAngle);
|
||
attempts++;
|
||
} while (IsAngleTooCloseToRecent(angle) && attempts < maxAttempts);
|
||
|
||
// 记录这个角度
|
||
recentAngles.Add(angle);
|
||
if (recentAngles.Count > rememberRecentPaths)
|
||
{
|
||
recentAngles.RemoveAt(0);
|
||
}
|
||
|
||
return angle;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查角度是否与最近的角度太接近
|
||
/// </summary>
|
||
private bool IsAngleTooCloseToRecent(float angle)
|
||
{
|
||
foreach (float recentAngle in recentAngles)
|
||
{
|
||
if (Mathf.Abs(angle - recentAngle) < 20f) // 至少相差20度
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 使用指定参数创建路径
|
||
/// </summary>
|
||
private CurvySpline CreatePathWithParams(int index, float horizontalAngle, float verticalAngle,
|
||
float startDistance, float endDistance, float height, float curvature)
|
||
{
|
||
// 确保 playerTransform 已准备好
|
||
if (playerTransform == null)
|
||
{
|
||
playerTransform = GameInit.Ins?.playerCam?.transform;
|
||
if (playerTransform == null)
|
||
{
|
||
Debug.LogError("[DriftBottlePathGenerator] 无法获取玩家相机位置!");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
// 创建路径游戏对象
|
||
GameObject pathObj = new GameObject($"DriftBottlePath_{index}_Random");
|
||
pathObj.transform.SetParent(transform);
|
||
|
||
CurvySpline spline = pathObj.AddComponent<CurvySpline>();
|
||
|
||
// 设置路径参数
|
||
spline.Interpolation = CurvyInterpolation.Bezier;
|
||
spline.Closed = false;
|
||
spline.AutoEndTangents = true;
|
||
|
||
// 计算路径点
|
||
Vector3 playerPos = playerTransform.position;
|
||
Vector3 playerForward = playerTransform.forward;
|
||
Vector3 playerRight = playerTransform.right;
|
||
Vector3 playerUp = Vector3.up;
|
||
|
||
// 水平角度转弧度
|
||
float horizRad = horizontalAngle * Mathf.Deg2Rad;
|
||
float vertRad = verticalAngle * Mathf.Deg2Rad;
|
||
|
||
// 计算起始方向(考虑水平和垂直角度)
|
||
Vector3 startDir = (playerForward * Mathf.Cos(horizRad) * Mathf.Cos(vertRad) +
|
||
playerRight * Mathf.Sin(horizRad) * Mathf.Cos(vertRad) +
|
||
playerUp * Mathf.Sin(vertRad)).normalized;
|
||
|
||
Vector3 startPoint = playerPos + startDir * startDistance;
|
||
|
||
// 计算结束方向(与起始方向有一定偏移,形成弧线)
|
||
float endOffsetAngle = Random.Range(-30f, 30f); // 结束点水平偏移
|
||
float endVertAngle = Random.Range(-10f, 20f); // 结束点垂直偏移
|
||
float endHorizRad = (horizontalAngle + endOffsetAngle) * Mathf.Deg2Rad;
|
||
float endVertRad = (verticalAngle + endVertAngle) * Mathf.Deg2Rad;
|
||
|
||
Vector3 endDir = (playerForward * Mathf.Cos(endHorizRad) * Mathf.Cos(endVertRad) +
|
||
playerRight * Mathf.Sin(endHorizRad) * Mathf.Cos(endVertRad) +
|
||
playerUp * Mathf.Sin(endVertRad)).normalized;
|
||
|
||
Vector3 endPoint = playerPos + endDir * endDistance;
|
||
|
||
// 确保高度在安全范围内
|
||
startPoint.y = Mathf.Max(startPoint.y, 0.5f);
|
||
endPoint.y = Mathf.Max(endPoint.y, 0.5f);
|
||
|
||
// 控制点(形成弧线)
|
||
Vector3 midPoint = (startPoint + endPoint) * 0.5f;
|
||
Vector3 controlPoint = midPoint + Vector3.up * curvature +
|
||
(playerForward + playerRight * Random.Range(-1f, 1f)) * Random.Range(1f, 3f);
|
||
|
||
// 关键:先将路径对象放在起点,然后使用局部坐标创建控制点
|
||
pathObj.transform.position = startPoint;
|
||
|
||
// 计算相对坐标(相对于 pathObj)
|
||
Vector3 localStart = Vector3.zero;
|
||
Vector3 localControl = controlPoint - startPoint;
|
||
Vector3 localEnd = endPoint - startPoint;
|
||
|
||
// 创建控制点(使用局部坐标)
|
||
CurvySplineSegment startCP = spline.Add();
|
||
startCP.transform.localPosition = localStart;
|
||
startCP.HandleIn = Vector3.zero;
|
||
startCP.HandleOut = localControl * Random.Range(0.3f, 0.5f);
|
||
|
||
CurvySplineSegment midCP = spline.Add();
|
||
midCP.transform.localPosition = localControl;
|
||
midCP.HandleIn = -localControl * Random.Range(0.3f, 0.5f);
|
||
midCP.HandleOut = (localEnd - localControl) * Random.Range(0.3f, 0.5f);
|
||
|
||
CurvySplineSegment endCP = spline.Add();
|
||
endCP.transform.localPosition = localEnd;
|
||
endCP.HandleIn = (localControl - localEnd) * Random.Range(0.3f, 0.5f);
|
||
endCP.HandleOut = Vector3.zero;
|
||
|
||
// 刷新路径
|
||
spline.Refresh();
|
||
return spline;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清除现有路径
|
||
/// </summary>
|
||
private void ClearExistingPaths()
|
||
{
|
||
foreach (var path in generatedPaths)
|
||
{
|
||
if (path != null)
|
||
{
|
||
if (Application.isPlaying)
|
||
Destroy(path.gameObject);
|
||
else
|
||
DestroyImmediate(path.gameObject);
|
||
}
|
||
}
|
||
generatedPaths.Clear();
|
||
recentAngles.Clear();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取生成的路径
|
||
/// </summary>
|
||
public CurvySpline[] GetPaths()
|
||
{
|
||
return generatedPaths.ToArray();
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 调试:立即重新生成随机路径
|
||
/// </summary>
|
||
[ContextMenu("重新生成随机路径")]
|
||
public void DebugRegeneratePaths()
|
||
{
|
||
GenerateRandomPaths(5);
|
||
}
|
||
}
|