Files
Fishing2/Assets/Scripts/Fishing/New/View/Player/States/ParabolaPlayerThrowAnimation.cs
2026-03-24 23:46:11 +08:00

159 lines
5.0 KiB
C#

using UnityEngine;
namespace NBF
{
public class ParabolaPlayerThrowAnimation : IPlayerThrowAnimation
{
private const int TrajectorySampleCount = 24;
private readonly float _minThrowDistance;
private readonly float _maxThrowDistance;
private readonly float _throwDuration;
private readonly float _throwArcHeight;
private readonly float _targetHeightOffset;
private readonly AnimationCurve _throwHeightCurve;
private readonly Vector3[] _lastTrajectoryPoints = new Vector3[TrajectorySampleCount + 1];
private bool _hasLastTrajectory;
private float _chargedProgress;
private float _castElapsedTime;
private Vector3 _castStartPos;
private Vector3 _castTargetPos;
private LureController _castingLure;
public bool IsPlaying => _castingLure != null;
public ParabolaPlayerThrowAnimation(
float minThrowDistance = 6f,
float maxThrowDistance = 25f,
float throwDuration = 0.65f,
float throwArcHeight = 4f,
float targetHeightOffset = 0f,
AnimationCurve throwHeightCurve = null)
{
_minThrowDistance = minThrowDistance;
_maxThrowDistance = maxThrowDistance;
_throwDuration = throwDuration;
_throwArcHeight = throwArcHeight;
_targetHeightOffset = targetHeightOffset;
_throwHeightCurve = throwHeightCurve ?? AnimationCurve.EaseInOut(0f, 0f, 1f, 1f);
}
public void Play(ThrowAnimationRequest request)
{
if (request.Lure == null)
{
return;
}
Stop(snapToTarget: false);
_castingLure = request.Lure;
_chargedProgress = Mathf.Clamp01(request.ChargedProgress);
_castElapsedTime = 0f;
var lureBody = request.Lure.RBody;
_castStartPos = request.StartPosition;
Vector3 forward = request.Forward;
forward.y = 0f;
if (forward.sqrMagnitude < 0.001f)
{
forward = Vector3.forward;
}
float distance = Mathf.Lerp(_minThrowDistance, _maxThrowDistance, _chargedProgress);
_castTargetPos = _castStartPos + forward.normalized * distance;
_castTargetPos.y = _castStartPos.y + _targetHeightOffset;
CacheTrajectoryPoints();
lureBody.isKinematic = true;
lureBody.useGravity = false;
lureBody.linearVelocity = Vector3.zero;
lureBody.angularVelocity = Vector3.zero;
lureBody.position = _castStartPos;
}
public void Tick(float deltaTime)
{
DrawLastTrajectory();
UpdateCastAnimation(deltaTime);
}
public void Stop(bool snapToTarget)
{
if (_castingLure == null)
{
return;
}
var lureBody = _castingLure.RBody;
if (snapToTarget)
{
_castingLure.transform.position = _castTargetPos;
lureBody.position = _castTargetPos;
}
lureBody.linearVelocity = Vector3.zero;
lureBody.angularVelocity = Vector3.zero;
lureBody.useGravity = true;
lureBody.isKinematic = false;
_castingLure = null;
}
private void UpdateCastAnimation(float deltaTime)
{
if (_castingLure == null)
{
return;
}
float duration = Mathf.Max(_throwDuration, 0.01f);
_castElapsedTime += deltaTime;
float progress = Mathf.Clamp01(_castElapsedTime / duration);
_castingLure.transform.position = EvaluateTrajectoryPosition(progress);
if (progress >= 1f)
{
Stop(snapToTarget: true);
}
}
private void CacheTrajectoryPoints()
{
for (int i = 0; i <= TrajectorySampleCount; i++)
{
float progress = i / (float)TrajectorySampleCount;
_lastTrajectoryPoints[i] = EvaluateTrajectoryPosition(progress);
}
_hasLastTrajectory = true;
}
private Vector3 EvaluateTrajectoryPosition(float progress)
{
Vector3 position = Vector3.Lerp(_castStartPos, _castTargetPos, progress);
float arc = _throwHeightCurve.Evaluate(progress) * _throwArcHeight * _chargedProgress;
position.y += arc;
return position;
}
private void DrawLastTrajectory()
{
if (!_hasLastTrajectory)
{
return;
}
for (int i = 1; i <= TrajectorySampleCount; i++)
{
Debug.DrawLine(_lastTrajectoryPoints[i - 1], _lastTrajectoryPoints[i], Color.yellow);
}
Debug.DrawRay(_lastTrajectoryPoints[TrajectorySampleCount], Vector3.up * 0.3f, Color.cyan);
}
}
}