using UnityEngine; namespace NBF { public class ParabolaPlayerThrowAnimation : IPlayerThrowAnimation { public Player Player { get; set; } 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 throwDuration = 0.45f, float throwArcHeight = 4f, float targetHeightOffset = 0f, AnimationCurve throwHeightCurve = null) { _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 = GetHorizontalForward(request.Forward); float distance = Mathf.Lerp(1, 8, _chargedProgress); _castTargetPos = request.ThrowOriginPosition + forward * 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) { 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; if (Player != null && Player.TrajectoryPoints != null) { Player.TrajectoryPoints.Clear(); Player.TrajectoryPoints.AddRange(_lastTrajectoryPoints); } } private Vector3 GetHorizontalForward(Vector3 forward) { forward.y = 0f; if (forward.sqrMagnitude < 0.001f) { return Vector3.forward; } return forward.normalized; } private Vector3 EvaluateTrajectoryPosition(float progress) { Vector3 position = Vector3.Lerp(_castStartPos, _castTargetPos, progress); float arc = _throwHeightCurve.Evaluate(progress) * _throwArcHeight * _chargedProgress; position.y += arc; return position; } } }