首次提交
This commit is contained in:
17
Assets/Scripts/Fishing~/Player/Animations/BobThrowAnim.cs
Normal file
17
Assets/Scripts/Fishing~/Player/Animations/BobThrowAnim.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using NBC;
|
||||
|
||||
namespace NBF
|
||||
{
|
||||
/// <summary>
|
||||
/// 浮漂
|
||||
/// </summary>
|
||||
public class BobThrowAnim : NTask
|
||||
{
|
||||
private FPlayer _player;
|
||||
|
||||
public BobThrowAnim(FPlayer player)
|
||||
{
|
||||
_player = player;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e061352cd04e482090828b12acb7920f
|
||||
timeCreated: 1746449734
|
||||
312
Assets/Scripts/Fishing~/Player/Animations/LureThrowAnim.cs
Normal file
312
Assets/Scripts/Fishing~/Player/Animations/LureThrowAnim.cs
Normal file
@@ -0,0 +1,312 @@
|
||||
using System.Collections.Generic;
|
||||
using NBC;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBF
|
||||
{
|
||||
[System.Serializable]
|
||||
public struct FishingLureCastInput
|
||||
{
|
||||
[Range(0, 1)] public float power;
|
||||
[Range(0, 50)] public float windStrength; // 单位 m/s
|
||||
public Vector3 windDirection;
|
||||
[Range(5, 30)] public float lureWeight; // 单位 g
|
||||
[Range(0.1f, 1f)] public float lineDiameter; // 单位:毫米,0.1mm ~ 1mm
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 路亚抛竿轨迹动画
|
||||
/// </summary>
|
||||
public class LureThrowAnim : NTask
|
||||
{
|
||||
private FPlayer _player;
|
||||
|
||||
[Header("基础参数")] public int maxSimulationSteps = 1000;
|
||||
public float simulationDuration = 10f;
|
||||
public float timeStep = 0.02f;
|
||||
|
||||
[Header("抛投力度")] public float minThrowPower = 15f;
|
||||
public float maxThrowPower = 45f;
|
||||
|
||||
[Header("空气阻力参数")] [Tooltip("阻力系数,非流线钓饵建议 0.8~1.2")]
|
||||
public float dragCoefficient = 0.2f;
|
||||
|
||||
[Tooltip("迎风面积,0.005m² ≈ 5cm²")] public float lureArea = 0.001f;
|
||||
|
||||
|
||||
private FWaterDisplacement _lureHookWaterDisplacement;
|
||||
|
||||
private List<Vector3> _trajectory;
|
||||
|
||||
public float totalDuration = 1.5f; // 总飞行时间
|
||||
public float arriveThreshold = 0.01f;
|
||||
|
||||
private float elapsedTime = 0f;
|
||||
private bool isMoving = false;
|
||||
private Vector3 startOffset = Vector3.zero;
|
||||
|
||||
private Transform _transform;
|
||||
|
||||
public float CurrentDistanceTraveled { get; private set; } = 0f;
|
||||
|
||||
private float _throwStartLineLenght = 0f;
|
||||
|
||||
|
||||
public LureThrowAnim(FPlayer player)
|
||||
{
|
||||
_player = player;
|
||||
}
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
_throwStartLineLenght = _player.Data.lineLength;
|
||||
_lureHookWaterDisplacement = _player.Gears.Rod.LureHookWaterDisplacement;
|
||||
float angle = 25f;
|
||||
float radians = angle * Mathf.Deg2Rad;
|
||||
Vector3 forwardUp = _player.transform.forward * Mathf.Cos(radians) +
|
||||
_player.transform.up * Mathf.Sin(radians);
|
||||
// // 施加力
|
||||
// lureHookWaterDisplacement.rigidbody.AddForce(forwardUpDirection * 500, ForceMode.Impulse);
|
||||
_transform = _lureHookWaterDisplacement.transform;
|
||||
_lureHookWaterDisplacement.rigidbody.isKinematic = true;
|
||||
_lureHookWaterDisplacement.rigidbody.useGravity = false;
|
||||
FishingLureCastInput baseInput = new FishingLureCastInput
|
||||
{
|
||||
power = 1f,
|
||||
lureWeight = 2.2f,
|
||||
windStrength = 0f, // 风力大小
|
||||
lineDiameter = 0.14f
|
||||
};
|
||||
|
||||
Vector3 startPos = _player.transform.position + Vector3.up * 2.5f;
|
||||
// Vector3 startPos = _player.Gears.Rod.rodAsset.lineConnector.position;
|
||||
Vector3 throwDir = forwardUp.normalized;
|
||||
var trajectory = CalculateTrajectory(baseInput, startPos, throwDir);
|
||||
_trajectory = trajectory;
|
||||
_trajectory = SimplifyTrajectoryRDP(trajectory, 0.1f);
|
||||
// SceneSettings.Instance.LineRenderer.startWidth = 0.1f;
|
||||
// SceneSettings.Instance.LineRenderer.endWidth = 0.1f;
|
||||
// SceneSettings.Instance.LineRenderer.positionCount = _trajectory.Count;
|
||||
// SceneSettings.Instance.LineRenderer.useWorldSpace = true;
|
||||
// SceneSettings.Instance.LineRenderer.SetPositions(_trajectory.ToArray());
|
||||
|
||||
elapsedTime = 0f;
|
||||
isMoving = true;
|
||||
CurrentDistanceTraveled = 0f;
|
||||
|
||||
// 如果不在起点,先移动过去
|
||||
if ((_transform.position - _trajectory[0]).magnitude > arriveThreshold)
|
||||
{
|
||||
startOffset = _trajectory[0] - _transform.position;
|
||||
}
|
||||
else
|
||||
{
|
||||
startOffset = Vector3.zero;
|
||||
}
|
||||
}
|
||||
|
||||
protected override NTaskStatus OnProcess()
|
||||
{
|
||||
OnMove();
|
||||
return base.OnProcess();
|
||||
}
|
||||
|
||||
|
||||
private void MoveDone()
|
||||
{
|
||||
var rb = _lureHookWaterDisplacement.rigidbody;
|
||||
|
||||
// 强制同步位置和朝向
|
||||
rb.position = rb.transform.position;
|
||||
rb.rotation = rb.transform.rotation;
|
||||
rb.isKinematic = false;
|
||||
// 停止物理残留影响
|
||||
rb.linearVelocity = Vector3.zero;
|
||||
rb.angularVelocity = Vector3.zero;
|
||||
rb.useGravity = true;
|
||||
Finish();
|
||||
}
|
||||
|
||||
private void OnMove()
|
||||
{
|
||||
if (!isMoving || _trajectory == null || _trajectory.Count < 2) return;
|
||||
// Debug.LogError("OnMove");
|
||||
// 初始补位阶段(瞬移或平滑补过去)
|
||||
if (startOffset.magnitude > arriveThreshold)
|
||||
{
|
||||
float moveSpeed = startOffset.magnitude / 0.1f;
|
||||
Vector3 move = startOffset.normalized * moveSpeed * Time.deltaTime;
|
||||
if (move.magnitude >= startOffset.magnitude)
|
||||
{
|
||||
_transform.position = _trajectory[0];
|
||||
startOffset = Vector3.zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
_transform.position += move;
|
||||
startOffset -= move;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
elapsedTime += Time.deltaTime;
|
||||
float t = Mathf.Clamp01(elapsedTime / totalDuration);
|
||||
|
||||
// 插值路径
|
||||
int segmentCount = _trajectory.Count - 1;
|
||||
float totalSegmentLength = segmentCount;
|
||||
float pathT = t * totalSegmentLength;
|
||||
int index = Mathf.FloorToInt(pathT);
|
||||
|
||||
if (index >= _trajectory.Count - 1)
|
||||
{
|
||||
_transform.position = _trajectory[^1];
|
||||
isMoving = false;
|
||||
CurrentDistanceTraveled = CalculateTotalDistance(_trajectory);
|
||||
MoveDone();
|
||||
return;
|
||||
}
|
||||
|
||||
float localT = pathT - index;
|
||||
Vector3 p0 = _trajectory[index];
|
||||
Vector3 p1 = _trajectory[index + 1];
|
||||
_transform.position = Vector3.Lerp(p0, p1, localT);
|
||||
|
||||
var distance = Vector3.Distance(_lureHookWaterDisplacement.transform.position,
|
||||
_player.Gears.Rod.rodAsset.lineConnector.position);
|
||||
if (distance > _throwStartLineLenght)
|
||||
{
|
||||
_throwStartLineLenght = distance;
|
||||
_player.Data.lineLength = _throwStartLineLenght + 0.5f;
|
||||
}
|
||||
// _throwStartLineLenght
|
||||
}
|
||||
|
||||
float CalculateTotalDistance(List<Vector3> points)
|
||||
{
|
||||
float dist = 0f;
|
||||
for (int i = 1; i < points.Count; i++)
|
||||
dist += Vector3.Distance(points[i - 1], points[i]);
|
||||
return dist;
|
||||
}
|
||||
|
||||
public List<Vector3> CalculateTrajectory(FishingLureCastInput input, Vector3 startPosition,
|
||||
Vector3 castDirection)
|
||||
{
|
||||
List<Vector3> trajectory = new List<Vector3>();
|
||||
|
||||
Vector3 position = startPosition;
|
||||
Vector3 direction = castDirection.normalized;
|
||||
float throwPower = Mathf.Lerp(minThrowPower, maxThrowPower, input.power);
|
||||
Vector3 velocity = direction * throwPower;
|
||||
|
||||
float lureMass = input.lureWeight / 1000f; // 转 kg
|
||||
Vector3 windDir = input.windDirection.normalized;
|
||||
float windStrength = input.windStrength;
|
||||
|
||||
float currentTime = 0f;
|
||||
int steps = 0;
|
||||
totalDuration = 0;
|
||||
while (currentTime < simulationDuration && steps < maxSimulationSteps)
|
||||
{
|
||||
if (position.y <= 0f) break;
|
||||
|
||||
// 模拟风力逐渐生效
|
||||
float windInfluenceFactor = Mathf.Clamp01(currentTime / 1.5f); // 1.5秒内增长
|
||||
Vector3 windVelocity = windDir * windStrength * windInfluenceFactor;
|
||||
|
||||
// 真实空气阻力模型
|
||||
Vector3 relVelocity = velocity - windVelocity;
|
||||
|
||||
// 空气阻力
|
||||
float dragMag = 0.5f * PhysicsHelper.AirDensity *
|
||||
relVelocity.sqrMagnitude *
|
||||
dragCoefficient * lureArea;
|
||||
|
||||
// --- 钓线空气阻力模拟 ---
|
||||
// 假设飞行中展开的线长度近似为当前位置的XZ平面长度
|
||||
float lineLength = Vector3.Distance(new Vector3(position.x, 0, position.z),
|
||||
new Vector3(startPosition.x, 0, startPosition.z));
|
||||
float lineRadius = input.lineDiameter / 2000f; // mm转m再除以2得到半径
|
||||
|
||||
// 钓线的迎风面积估算:长度 * 直径
|
||||
float lineArea = lineLength * (lineRadius * 2f); // 近似为圆柱体侧面积的一部分
|
||||
|
||||
// 简化模型:线的附加空气阻力方向与当前速度方向相反
|
||||
float lineDragMag = 0.5f * PhysicsHelper.AirDensity * velocity.sqrMagnitude * dragCoefficient *
|
||||
lineArea;
|
||||
Vector3 lineDragForce = -velocity.normalized * lineDragMag;
|
||||
|
||||
|
||||
Vector3 dragForce = -relVelocity.normalized * dragMag;
|
||||
|
||||
// 合力 = 重力 + 空气阻力
|
||||
// Vector3 acceleration = (Physics.gravity + dragForce / lureMass);
|
||||
Vector3 totalForce = dragForce + lineDragForce;
|
||||
// 合力 = 重力 + 空气阻力 + 线阻力
|
||||
Vector3 acceleration = (Physics.gravity + totalForce / lureMass);
|
||||
|
||||
|
||||
velocity += acceleration * timeStep;
|
||||
position += velocity * timeStep;
|
||||
|
||||
trajectory.Add(position);
|
||||
currentTime += timeStep;
|
||||
steps++;
|
||||
}
|
||||
|
||||
totalDuration = currentTime;
|
||||
return trajectory;
|
||||
}
|
||||
|
||||
|
||||
public static List<Vector3> SimplifyTrajectoryRDP(List<Vector3> points, float tolerance)
|
||||
{
|
||||
if (points == null || points.Count < 3)
|
||||
return new List<Vector3>(points);
|
||||
|
||||
List<Vector3> result = new List<Vector3>();
|
||||
SimplifySection(points, 0, points.Count - 1, tolerance, result);
|
||||
result.Add(points[points.Count - 1]);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static 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;
|
||||
Vector3 startPoint = points[start];
|
||||
Vector3 endPoint = points[end];
|
||||
|
||||
for (int i = start + 1; i < end; i++)
|
||||
{
|
||||
float distance = PerpendicularDistance(points[i], startPoint, endPoint);
|
||||
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 static float PerpendicularDistance(Vector3 point, Vector3 lineStart, Vector3 lineEnd)
|
||||
{
|
||||
if (lineStart == lineEnd) return Vector3.Distance(point, lineStart);
|
||||
Vector3 projected = Vector3.Project(point - lineStart, lineEnd - lineStart);
|
||||
Vector3 closest = lineStart + projected;
|
||||
return Vector3.Distance(point, closest);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b1397324abdd458495e11ac971167bbc
|
||||
timeCreated: 1746449919
|
||||
Reference in New Issue
Block a user