Files
Fishing2/Assets/Scripts/Fishing/New/View/Player/States/LureThrowTrajectory.cs
2026-03-22 09:33:56 +08:00

231 lines
7.7 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 UnityEngine;
using DG.Tweening;
using System;
namespace NBF
{
/// <summary>
/// 抛竿输入参数
/// </summary>
[Serializable]
public struct ThrowInput
{
[Range(0, 1)] public float power; // 力度 0-1
[Range(5, 50)] public float lureWeight; // 钓饵重量 g
[Range(0.1f, 1f)] public float lineDiameter; // 线直径 mm
public Vector3 windDirection;
public float windStrength;
}
/// <summary>
/// 路亚抛竿轨迹控制器
/// 使用预计算轨迹 + DOTween动画实现确定性抛竿
/// </summary>
public class LureThrowTrajectory : IDisposable
{
// 物理常量
private const float AirDensity = 1.225f;
// 默认参数
protected float _minThrowPower = 15f;
protected float _maxThrowPower = 45f;
private float _dragCoefficient = 0.2f;
private float _lureArea = 0.001f;
private float _timeStep = 0.02f;
private int _maxSteps = 500;
private Sequence _throwSequence;
private List<Vector3> _trajectory;
public bool IsPlaying => _throwSequence != null && _throwSequence.IsPlaying();
public List<Vector3> Trajectory => _trajectory;
/// <summary>
/// 计算抛竿轨迹
/// </summary>
public List<Vector3> CalculateTrajectory(ThrowInput input, Vector3 startPos, Vector3 throwDirection)
{
_trajectory = new List<Vector3>();
Vector3 position = startPos;
Vector3 direction = throwDirection.normalized;
float throwPower = Mathf.Lerp(_minThrowPower, _maxThrowPower, input.power);
Vector3 velocity = direction * throwPower;
float lureMass = input.lureWeight / 1000f;
Vector3 windDir = input.windDirection.normalized;
float windStrength = input.windStrength;
float currentTime = 0f;
int steps = 0;
while (currentTime < 10f && steps < _maxSteps)
{
if (position.y <= 0f) break;
// 风力影响
float windInfluenceFactor = Mathf.Clamp01(currentTime / 1.5f);
Vector3 windVelocity = windDir * windStrength * windInfluenceFactor;
Vector3 relVelocity = velocity - windVelocity;
// 空气阻力
float dragMag = 0.5f * AirDensity * relVelocity.sqrMagnitude * _dragCoefficient * _lureArea;
// 钓线阻力
float lineLength = Vector3.Distance(
new Vector3(position.x, 0, position.z),
new Vector3(startPos.x, 0, startPos.z));
float lineRadius = input.lineDiameter / 2000f;
float lineArea = lineLength * (lineRadius * 2f);
float lineDragMag = 0.5f * AirDensity * velocity.sqrMagnitude * _dragCoefficient * lineArea;
Vector3 lineDragForce = -velocity.normalized * lineDragMag;
Vector3 dragForce = -relVelocity.normalized * dragMag;
Vector3 totalForce = dragForce + lineDragForce;
Vector3 acceleration = Physics.gravity + totalForce / lureMass;
velocity += acceleration * _timeStep;
position += velocity * _timeStep;
_trajectory.Add(position);
currentTime += _timeStep;
steps++;
}
return _trajectory;
}
/// <summary>
/// 执行抛竿动画
/// </summary>
public void ExecuteThrow(
Transform lureTransform,
Rigidbody lureRigidbody,
List<Vector3> trajectory,
float duration,
Action onComplete = null)
{
if (trajectory == null || trajectory.Count < 2)
{
onComplete?.Invoke();
return;
}
// 停止之前的动画
Kill();
// 设置为运动学模式
lureRigidbody.isKinematic = true;
lureRigidbody.useGravity = false;
// 创建路径动画
_throwSequence = DOTween.Sequence();
// 使用 DOPath 沿路径移动
var pathArray = trajectory.ToArray();
_throwSequence.Append(
lureTransform.DOPath(pathArray, duration, PathType.Linear, PathMode.Full3D)
.SetEase(Ease.Linear)
.SetLookAt(0.01f) // 让Lure朝向运动方向
);
_throwSequence.OnComplete(() =>
{
// 动画结束,恢复物理
lureRigidbody.isKinematic = false;
lureRigidbody.useGravity = true;
lureRigidbody.linearVelocity = Vector3.zero;
lureRigidbody.angularVelocity = Vector3.zero;
onComplete?.Invoke();
});
}
/// <summary>
/// 计算合适的飞行时间(基于轨迹长度)
/// </summary>
public float CalculateDuration(List<Vector3> trajectory, float speedFactor = 1f)
{
if (trajectory == null || trajectory.Count < 2) return 1f;
float totalLength = 0f;
for (int i = 1; i < trajectory.Count; i++)
{
totalLength += Vector3.Distance(trajectory[i - 1], trajectory[i]);
}
// 根据轨迹长度计算时间,越长越慢
return Mathf.Clamp(totalLength / (20f * speedFactor), 0.5f, 3f);
}
/// <summary>
/// 使用简化轨迹(减少点位,优化性能)
/// </summary>
public List<Vector3> SimplifyTrajectory(List<Vector3> points, float tolerance = 0.1f)
{
if (points == null || points.Count < 3) return new List<Vector3>(points ?? new List<Vector3>());
var result = new List<Vector3>();
SimplifySection(points, 0, points.Count - 1, tolerance, result);
result.Add(points[points.Count - 1]);
return result;
}
private void SimplifySection(List<Vector3> points, int start, int end, float tolerance, List<Vector3> result)
{
if (end <= start + 1) return;
float maxDistance = -1f;
int index = -1;
for (int i = start + 1; i < end; i++)
{
float distance = PerpendicularDistance(points[i], points[start], points[end]);
if (distance > maxDistance)
{
maxDistance = distance;
index = i;
}
}
if (maxDistance > tolerance)
{
SimplifySection(points, start, index, tolerance, result);
result.Add(points[index]);
SimplifySection(points, index, end, tolerance, result);
}
}
private float PerpendicularDistance(Vector3 point, Vector3 lineStart, Vector3 lineEnd)
{
if (lineStart == lineEnd) return Vector3.Distance(point, lineStart);
Vector3 projected = Vector3.Project(point - lineStart, lineEnd - lineStart);
return Vector3.Distance(point, lineStart + projected);
}
public void Kill()
{
if (_throwSequence != null)
{
_throwSequence.Kill();
_throwSequence = null;
}
}
public void Dispose()
{
Kill();
_trajectory?.Clear();
}
/// <summary>
/// 设置抛竿力度范围
/// </summary>
public void SetPowerRange(float min, float max)
{
_minThrowPower = min;
_maxThrowPower = max;
}
}
}