Files
MRFishingMaster/Assets/_FishingMaster/Scripts/Statue/DriftBottlePathGenerator.cs

317 lines
10 KiB
C#
Raw 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 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);
}
}