浮漂脚本
This commit is contained in:
@@ -493,7 +493,7 @@ GameObject:
|
|||||||
- component: {fileID: 54298866000586118}
|
- component: {fileID: 54298866000586118}
|
||||||
- component: {fileID: 153691655494134957}
|
- component: {fileID: 153691655494134957}
|
||||||
- component: {fileID: 2717383850592950062}
|
- component: {fileID: 2717383850592950062}
|
||||||
- component: {fileID: 4574796762267659512}
|
- component: {fileID: 6678694395924494533}
|
||||||
m_Layer: 16
|
m_Layer: 16
|
||||||
m_Name: Float
|
m_Name: Float
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
@@ -659,7 +659,7 @@ MonoBehaviour:
|
|||||||
m_EditorClassIdentifier: Assembly-CSharp::NBF.BobberController
|
m_EditorClassIdentifier: Assembly-CSharp::NBF.BobberController
|
||||||
_rbody: {fileID: 54298866000586118}
|
_rbody: {fileID: 54298866000586118}
|
||||||
joint: {fileID: 153691655494134957}
|
joint: {fileID: 153691655494134957}
|
||||||
--- !u!114 &4574796762267659512
|
--- !u!114 &6678694395924494533
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
@@ -687,14 +687,24 @@ MonoBehaviour:
|
|||||||
lockXZAroundAnchor: 1
|
lockXZAroundAnchor: 1
|
||||||
xzSmoothTime: 0.15
|
xzSmoothTime: 0.15
|
||||||
maxPlanarOffset: 0.15
|
maxPlanarOffset: 0.15
|
||||||
keepUpright: 1
|
|
||||||
rotationLerpSpeed: 8
|
|
||||||
maxTiltAngle: 18
|
|
||||||
planarTiltFactor: 120
|
|
||||||
downForceToSink: 0.0025
|
downForceToSink: 0.0025
|
||||||
maxExtraSink: 0.08
|
maxExtraSink: 0.08
|
||||||
enableBottomTouchAdjust: 1
|
enableBottomTouchAdjust: 1
|
||||||
bottomTouchLift: 0.01
|
bottomTouchLift: 0.01
|
||||||
|
lureBody: {fileID: 54679398375713381}
|
||||||
|
referenceLength: 0.3
|
||||||
|
minSubmergeToStand: 0.16
|
||||||
|
verticalLieThreshold: 0.18
|
||||||
|
verticalUprightThreshold: 0.75
|
||||||
|
planarTiltThreshold: 0.3
|
||||||
|
planarDominanceMultiplier: 1.2
|
||||||
|
postureHysteresis: 0.04
|
||||||
|
tiltedAngle: 38
|
||||||
|
lyingAngle: 88
|
||||||
|
uprightMaxTiltAngle: 8
|
||||||
|
planarTiltFactor: 120
|
||||||
|
rotationLerpSpeed: 8
|
||||||
|
debugResetKey: 1
|
||||||
debugTapKey: 1
|
debugTapKey: 1
|
||||||
debugSlowSinkKey: 1
|
debugSlowSinkKey: 1
|
||||||
debugLiftKey: 1
|
debugLiftKey: 1
|
||||||
|
|||||||
@@ -26,90 +26,157 @@ public enum BobberControlMode
|
|||||||
public enum BobberBiteType
|
public enum BobberBiteType
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
Tap, // 轻点
|
Tap, // 轻点
|
||||||
SlowSink, // 缓沉
|
SlowSink, // 缓沉
|
||||||
Lift, // 送漂
|
Lift, // 送漂
|
||||||
BlackDrift // 黑漂/快速拖入
|
BlackDrift // 黑漂/快速拖入
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum BobberPosture
|
||||||
|
{
|
||||||
|
Lying,
|
||||||
|
Tilted,
|
||||||
|
Upright
|
||||||
}
|
}
|
||||||
|
|
||||||
[DisallowMultipleComponent]
|
[DisallowMultipleComponent]
|
||||||
[RequireComponent(typeof(Rigidbody))]
|
[RequireComponent(typeof(Rigidbody))]
|
||||||
public class BobberPresentationController : MonoBehaviour
|
public class BobberPresentationController : MonoBehaviour
|
||||||
{
|
{
|
||||||
[Header("Water")] [Tooltip("没有水提供器时使用固定水位")]
|
[Header("Water")]
|
||||||
|
[Tooltip("没有水提供器时使用固定水位")]
|
||||||
public float fallbackWaterLevel = 0f;
|
public float fallbackWaterLevel = 0f;
|
||||||
|
|
||||||
[Tooltip("可选:挂实现了 IWaterSurfaceProvider 的组件")]
|
[Tooltip("可选:挂实现了 IWaterSurfaceProvider 的组件")]
|
||||||
public MonoBehaviour waterProviderBehaviour;
|
public MonoBehaviour waterProviderBehaviour;
|
||||||
|
|
||||||
[Header("Enter Water")] [Tooltip("底部进入水面多少米后切换为漂像控制")]
|
[Header("Enter Water")]
|
||||||
|
[Tooltip("底部进入水面多少米后切换为漂像控制")]
|
||||||
public float enterWaterDepth = 0.002f;
|
public float enterWaterDepth = 0.002f;
|
||||||
|
|
||||||
[Tooltip("离开水面多少米后回到空中物理。一般给负值做滞回")] public float exitWaterDepth = -0.01f;
|
[Tooltip("离开水面多少米后回到空中物理。一般给负值做滞回")]
|
||||||
|
public float exitWaterDepth = -0.01f;
|
||||||
|
|
||||||
[Header("Geometry")] [Tooltip("浮漂总高度(米)")]
|
[Header("Geometry")]
|
||||||
|
[Tooltip("浮漂总高度(米)")]
|
||||||
public float floatHeight = 0.08f;
|
public float floatHeight = 0.08f;
|
||||||
|
|
||||||
[Tooltip("如果 Pivot 在浮漂底部,这里填 0;如果 Pivot 在模型中心,就填底部相对 Pivot 的本地 Y")]
|
[Tooltip("如果 Pivot 在浮漂底部,这里填 0;如果 Pivot 在模型中心,就填底部相对 Pivot 的本地 Y")]
|
||||||
public float bottomOffsetLocalY = 0f;
|
public float bottomOffsetLocalY = 0f;
|
||||||
|
|
||||||
[Header("Base Float")] [Tooltip("基础吃铅比例,决定静止时有多少在水下")] [Range(0.05f, 0.95f)]
|
[Header("Base Float")]
|
||||||
|
[Tooltip("基础吃铅比例,决定静止时有多少在水下")]
|
||||||
|
[Range(0.05f, 0.95f)]
|
||||||
public float baseSubmergeRatio = 0.28f;
|
public float baseSubmergeRatio = 0.28f;
|
||||||
|
|
||||||
[Tooltip("Y 轴平滑时间,越小响应越快")] public float ySmoothTime = 0.08f;
|
[Tooltip("Y 轴平滑时间,越小响应越快")]
|
||||||
|
public float ySmoothTime = 0.08f;
|
||||||
|
|
||||||
[Tooltip("最大竖直速度限制(用于 SmoothDamp)")] public float maxYSpeed = 2f;
|
[Tooltip("最大竖直速度限制(用于 SmoothDamp)")]
|
||||||
|
public float maxYSpeed = 2f;
|
||||||
|
|
||||||
[Tooltip("静止小死区,减少微抖")] public float yDeadZone = 0.0005f;
|
[Tooltip("静止小死区,减少微抖")]
|
||||||
|
public float yDeadZone = 0.0005f;
|
||||||
|
|
||||||
[Header("Surface Motion")] [Tooltip("是否启用轻微水面起伏")]
|
[Header("Surface Motion")]
|
||||||
|
[Tooltip("是否启用轻微水面起伏")]
|
||||||
public bool enableSurfaceBobbing = true;
|
public bool enableSurfaceBobbing = true;
|
||||||
|
|
||||||
[Tooltip("水面轻微起伏振幅(米)")] public float surfaceBobAmplitude = 0.0015f;
|
[Tooltip("水面轻微起伏振幅(米)")]
|
||||||
|
public float surfaceBobAmplitude = 0.0015f;
|
||||||
|
|
||||||
[Tooltip("水面轻微起伏频率")] public float surfaceBobFrequency = 1.2f;
|
[Tooltip("水面轻微起伏频率")]
|
||||||
|
public float surfaceBobFrequency = 1.2f;
|
||||||
|
|
||||||
[Header("XZ Motion")] [Tooltip("入水后是否锁定 XZ 到入水点附近")]
|
[Header("XZ Motion")]
|
||||||
|
[Tooltip("入水后是否锁定 XZ 到入水点附近")]
|
||||||
public bool lockXZAroundAnchor = true;
|
public bool lockXZAroundAnchor = true;
|
||||||
|
|
||||||
[Tooltip("XZ 跟随平滑时间")] public float xzSmoothTime = 0.15f;
|
[Tooltip("XZ 跟随平滑时间")]
|
||||||
|
public float xzSmoothTime = 0.15f;
|
||||||
|
|
||||||
[Tooltip("水流/拖拽带来的额外平面偏移最大值")] public float maxPlanarOffset = 0.15f;
|
[Tooltip("水流/拖拽带来的额外平面偏移最大值")]
|
||||||
|
public float maxPlanarOffset = 0.15f;
|
||||||
|
|
||||||
[Header("Upright")] [Tooltip("是否始终保持漂大致竖直")]
|
[Header("Sink By Weight / Tension")]
|
||||||
public bool keepUpright = true;
|
[Tooltip("外部向下拉力映射为下沉量的系数。你可以把钩/铅/线组的等效向下拉力喂进来")]
|
||||||
|
|
||||||
[Tooltip("姿态平滑速度")] public float rotationLerpSpeed = 8f;
|
|
||||||
|
|
||||||
[Tooltip("允许的最大倾斜角度")] public float maxTiltAngle = 18f;
|
|
||||||
|
|
||||||
[Tooltip("平面拖拽对倾斜的影响强度")] public float planarTiltFactor = 120f;
|
|
||||||
|
|
||||||
[Header("Sink By Weight / Tension")] [Tooltip("外部向下拉力映射为下沉量的系数。你可以把钩/铅/线组的等效向下拉力喂进来")]
|
|
||||||
public float downForceToSink = 0.0025f;
|
public float downForceToSink = 0.0025f;
|
||||||
|
|
||||||
[Tooltip("向下拉力下沉的最大附加量")] public float maxExtraSink = 0.08f;
|
[Tooltip("向下拉力下沉的最大附加量")]
|
||||||
|
public float maxExtraSink = 0.08f;
|
||||||
|
|
||||||
[Header("Bottom Touch")] [Tooltip("触底时是否启用修正")]
|
[Header("Bottom Touch")]
|
||||||
|
[Tooltip("触底时是否启用修正")]
|
||||||
public bool enableBottomTouchAdjust = true;
|
public bool enableBottomTouchAdjust = true;
|
||||||
|
|
||||||
[Tooltip("触底后减少的下沉量(例如铅坠到底,漂会回升一点)")] public float bottomTouchLift = 0.01f;
|
[Tooltip("触底后减少的下沉量(例如铅坠到底,漂会回升一点)")]
|
||||||
|
public float bottomTouchLift = 0.01f;
|
||||||
|
|
||||||
[Header("Debug Input")] [Tooltip("调试:按 R 恢复默认")]
|
[Header("Posture Source")]
|
||||||
|
[Tooltip("下方 Lure / 钩组 / 铅坠的刚体。姿态主要根据它和浮漂的相对位置判断")]
|
||||||
|
public Rigidbody lureBody;
|
||||||
|
|
||||||
|
[Tooltip("用于归一化的参考长度。一般填:浮漂到 Lure 在“正常拉直”时的大致长度")]
|
||||||
|
public float referenceLength = 0.30f;
|
||||||
|
|
||||||
|
[Header("Posture Threshold")]
|
||||||
|
[Tooltip("最小入水比例。不够时优先躺漂")]
|
||||||
|
public float minSubmergeToStand = 0.16f;
|
||||||
|
|
||||||
|
[Tooltip("垂直分量比低于该值时,优先躺漂")]
|
||||||
|
public float verticalLieThreshold = 0.18f;
|
||||||
|
|
||||||
|
[Tooltip("垂直分量比高于该值,且水平分量较小时,允许立漂")]
|
||||||
|
public float verticalUprightThreshold = 0.75f;
|
||||||
|
|
||||||
|
[Tooltip("水平分量比高于该值时,不允许完全立漂")]
|
||||||
|
public float planarTiltThreshold = 0.30f;
|
||||||
|
|
||||||
|
[Tooltip("水平分量明显大于垂直分量时,优先躺漂")]
|
||||||
|
public float planarDominanceMultiplier = 1.20f;
|
||||||
|
|
||||||
|
[Tooltip("姿态切换滞回")]
|
||||||
|
public float postureHysteresis = 0.04f;
|
||||||
|
|
||||||
|
[Header("Posture Rotation")]
|
||||||
|
[Tooltip("倾斜状态角度")]
|
||||||
|
public float tiltedAngle = 38f;
|
||||||
|
|
||||||
|
[Tooltip("躺漂角度")]
|
||||||
|
public float lyingAngle = 88f;
|
||||||
|
|
||||||
|
[Tooltip("立漂时允许的最大附加倾角")]
|
||||||
|
public float uprightMaxTiltAngle = 8f;
|
||||||
|
|
||||||
|
[Tooltip("平面方向对立漂/斜漂附加倾角的影响强度")]
|
||||||
|
public float planarTiltFactor = 120f;
|
||||||
|
|
||||||
|
[Tooltip("姿态平滑速度")]
|
||||||
|
public float rotationLerpSpeed = 8f;
|
||||||
|
|
||||||
|
[Header("Debug Input")]
|
||||||
|
[Tooltip("调试:按 R 恢复默认")]
|
||||||
public bool debugResetKey = true;
|
public bool debugResetKey = true;
|
||||||
[Header("Debug Input")] [Tooltip("调试:按 T 触发轻点")]
|
|
||||||
|
[Tooltip("调试:按 T 触发轻点")]
|
||||||
public bool debugTapKey = true;
|
public bool debugTapKey = true;
|
||||||
|
|
||||||
[Tooltip("调试:按 G 触发缓沉")] public bool debugSlowSinkKey = true;
|
[Tooltip("调试:按 G 触发缓沉")]
|
||||||
|
public bool debugSlowSinkKey = true;
|
||||||
|
|
||||||
[Tooltip("调试:按 H 触发送漂")] public bool debugLiftKey = true;
|
[Tooltip("调试:按 H 触发送漂")]
|
||||||
|
public bool debugLiftKey = true;
|
||||||
|
|
||||||
[Tooltip("调试:按 B 触发黑漂")] public bool debugBlackDriftKey = true;
|
[Tooltip("调试:按 B 触发黑漂")]
|
||||||
|
public bool debugBlackDriftKey = true;
|
||||||
|
|
||||||
[Header("Debug")] public bool drawDebug = false;
|
[Header("Debug")]
|
||||||
|
public bool drawDebug = false;
|
||||||
|
|
||||||
public BobberControlMode CurrentMode => _mode;
|
public BobberControlMode CurrentMode => _mode;
|
||||||
|
public BobberPosture CurrentPosture => _posture;
|
||||||
|
public float CurrentVerticalRatio => _verticalRatio;
|
||||||
|
public float CurrentPlanarRatio => _planarRatio;
|
||||||
|
|
||||||
/// <summary>外部可写:等效向下拉力(不是必须是真实力,作为输入信号即可)</summary>
|
/// <summary>外部可写:等效向下拉力(不是必须是真实力,作为输入信号即可)</summary>
|
||||||
public float ExternalDownForce { get; set; }
|
public float ExternalDownForce { get; set; }
|
||||||
@@ -123,6 +190,7 @@ public class BobberPresentationController : MonoBehaviour
|
|||||||
private Rigidbody _rb;
|
private Rigidbody _rb;
|
||||||
private IWaterSurfaceProvider _waterProvider;
|
private IWaterSurfaceProvider _waterProvider;
|
||||||
private BobberControlMode _mode = BobberControlMode.AirPhysics;
|
private BobberControlMode _mode = BobberControlMode.AirPhysics;
|
||||||
|
private BobberPosture _posture = BobberPosture.Lying;
|
||||||
|
|
||||||
private float _defaultLinearDamping;
|
private float _defaultLinearDamping;
|
||||||
private float _defaultAngularDamping;
|
private float _defaultAngularDamping;
|
||||||
@@ -144,6 +212,12 @@ public class BobberPresentationController : MonoBehaviour
|
|||||||
private float _biteAmplitude;
|
private float _biteAmplitude;
|
||||||
private Vector3 _blackDriftDirection;
|
private Vector3 _blackDriftDirection;
|
||||||
|
|
||||||
|
// posture runtime
|
||||||
|
private float _verticalRatio;
|
||||||
|
private float _planarRatio;
|
||||||
|
private float _verticalDistance;
|
||||||
|
private float _planarDistance;
|
||||||
|
|
||||||
private void Awake()
|
private void Awake()
|
||||||
{
|
{
|
||||||
_rb = GetComponent<Rigidbody>();
|
_rb = GetComponent<Rigidbody>();
|
||||||
@@ -256,8 +330,10 @@ public class BobberPresentationController : MonoBehaviour
|
|||||||
|
|
||||||
transform.position = pos;
|
transform.position = pos;
|
||||||
|
|
||||||
// 3. 算目标旋转
|
// 3. 姿态判定 + 目标旋转
|
||||||
UpdateTargetRotation();
|
EvaluatePostureByComponents(waterY);
|
||||||
|
UpdateTargetRotationByPosture();
|
||||||
|
|
||||||
transform.rotation = Quaternion.Slerp(
|
transform.rotation = Quaternion.Slerp(
|
||||||
transform.rotation,
|
transform.rotation,
|
||||||
_targetRotation,
|
_targetRotation,
|
||||||
@@ -281,6 +357,12 @@ public class BobberPresentationController : MonoBehaviour
|
|||||||
_activeBiteType = BobberBiteType.None;
|
_activeBiteType = BobberBiteType.None;
|
||||||
_biteTimer = 0f;
|
_biteTimer = 0f;
|
||||||
|
|
||||||
|
_posture = BobberPosture.Lying;
|
||||||
|
_verticalRatio = 0f;
|
||||||
|
_planarRatio = 0f;
|
||||||
|
_verticalDistance = 0f;
|
||||||
|
_planarDistance = 0f;
|
||||||
|
|
||||||
_rb.useGravity = false;
|
_rb.useGravity = false;
|
||||||
_rb.linearVelocity = Vector3.zero;
|
_rb.linearVelocity = Vector3.zero;
|
||||||
_rb.angularVelocity = Vector3.zero;
|
_rb.angularVelocity = Vector3.zero;
|
||||||
@@ -327,9 +409,6 @@ public class BobberPresentationController : MonoBehaviour
|
|||||||
surfaceBob = Mathf.Sin(Time.time * surfaceBobFrequency * Mathf.PI * 2f) * surfaceBobAmplitude;
|
surfaceBob = Mathf.Sin(Time.time * surfaceBobFrequency * Mathf.PI * 2f) * surfaceBobAmplitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pivot 对应的目标 Y:
|
|
||||||
// target bottom = waterY - sinkDepth
|
|
||||||
// pivotY = target bottom - bottomOffsetLocalY + 动画偏移
|
|
||||||
float totalSink = baseSinkDepth + sinkByForce + bottomAdjust;
|
float totalSink = baseSinkDepth + sinkByForce + bottomAdjust;
|
||||||
float targetBottomY = waterY - totalSink;
|
float targetBottomY = waterY - totalSink;
|
||||||
float targetPivotY = targetBottomY - bottomOffsetLocalY + surfaceBob + _biteOffsetY;
|
float targetPivotY = targetBottomY - bottomOffsetLocalY + surfaceBob + _biteOffsetY;
|
||||||
@@ -343,7 +422,6 @@ public class BobberPresentationController : MonoBehaviour
|
|||||||
|
|
||||||
Vector3 basePos = lockXZAroundAnchor ? _waterAnchorPos : transform.position;
|
Vector3 basePos = lockXZAroundAnchor ? _waterAnchorPos : transform.position;
|
||||||
|
|
||||||
// 黑漂时额外平面位移
|
|
||||||
if (_activeBiteType == BobberBiteType.BlackDrift)
|
if (_activeBiteType == BobberBiteType.BlackDrift)
|
||||||
{
|
{
|
||||||
float t = Mathf.Clamp01(_biteDuration > 0f ? _biteTimer / _biteDuration : 1f);
|
float t = Mathf.Clamp01(_biteDuration > 0f ? _biteTimer / _biteDuration : 1f);
|
||||||
@@ -359,28 +437,170 @@ public class BobberPresentationController : MonoBehaviour
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateTargetRotation()
|
private void EvaluatePostureByComponents(float waterY)
|
||||||
{
|
{
|
||||||
if (!keepUpright)
|
float submergeRatio = Mathf.Clamp01(
|
||||||
|
(waterY - GetBottomWorldPosition().y) / Mathf.Max(0.0001f, floatHeight)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (lureBody == null)
|
||||||
{
|
{
|
||||||
_targetRotation = transform.rotation;
|
_verticalDistance = 0f;
|
||||||
|
_planarDistance = 0f;
|
||||||
|
_verticalRatio = 0f;
|
||||||
|
_planarRatio = 0f;
|
||||||
|
|
||||||
|
if (submergeRatio < minSubmergeToStand)
|
||||||
|
_posture = BobberPosture.Lying;
|
||||||
|
else if (ExternalPlanarOffset.magnitude > 0.01f)
|
||||||
|
_posture = BobberPosture.Tilted;
|
||||||
|
else
|
||||||
|
_posture = BobberPosture.Upright;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 up = Vector3.up;
|
Vector3 bobberPos = _rb.worldCenterOfMass;
|
||||||
Vector3 planar = new Vector3(_xzSmoothVelocity.x, 0f, _xzSmoothVelocity.z);
|
Vector3 lurePos = lureBody.worldCenterOfMass;
|
||||||
|
Vector3 delta = lurePos - bobberPos;
|
||||||
|
|
||||||
float speed = planar.magnitude;
|
_verticalDistance = Mathf.Max(0f, Vector3.Dot(delta, Vector3.down));
|
||||||
Vector3 tiltAxis = speed > 1e-5f ? Vector3.Cross(up, planar.normalized) : Vector3.zero;
|
_planarDistance = Vector3.ProjectOnPlane(delta, Vector3.up).magnitude;
|
||||||
|
|
||||||
float tiltAngle = Mathf.Clamp(speed * planarTiltFactor, 0f, maxTiltAngle);
|
float refLen = Mathf.Max(0.0001f, referenceLength);
|
||||||
|
_verticalRatio = _verticalDistance / refLen;
|
||||||
|
_planarRatio = _planarDistance / refLen;
|
||||||
|
|
||||||
Quaternion upright = Quaternion.FromToRotation(transform.up, Vector3.up) * transform.rotation;
|
switch (_posture)
|
||||||
Quaternion tilt = tiltAxis.sqrMagnitude > 1e-6f
|
{
|
||||||
? Quaternion.AngleAxis(tiltAngle, tiltAxis.normalized)
|
case BobberPosture.Lying:
|
||||||
: Quaternion.identity;
|
{
|
||||||
|
bool canStandUpright =
|
||||||
|
submergeRatio >= minSubmergeToStand &&
|
||||||
|
_verticalRatio > verticalUprightThreshold + postureHysteresis &&
|
||||||
|
_planarRatio < planarTiltThreshold - postureHysteresis;
|
||||||
|
|
||||||
_targetRotation = tilt * upright;
|
bool canTilt =
|
||||||
|
submergeRatio >= minSubmergeToStand * 0.8f &&
|
||||||
|
_verticalRatio > verticalLieThreshold + postureHysteresis;
|
||||||
|
|
||||||
|
if (canStandUpright)
|
||||||
|
{
|
||||||
|
_posture = BobberPosture.Upright;
|
||||||
|
}
|
||||||
|
else if (canTilt)
|
||||||
|
{
|
||||||
|
_posture = BobberPosture.Tilted;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case BobberPosture.Tilted:
|
||||||
|
{
|
||||||
|
bool shouldLie =
|
||||||
|
submergeRatio < minSubmergeToStand * 0.75f ||
|
||||||
|
_verticalRatio < verticalLieThreshold - postureHysteresis ||
|
||||||
|
_planarDistance > _verticalDistance * planarDominanceMultiplier;
|
||||||
|
|
||||||
|
bool shouldStand =
|
||||||
|
submergeRatio >= minSubmergeToStand &&
|
||||||
|
_verticalRatio > verticalUprightThreshold + postureHysteresis &&
|
||||||
|
_planarRatio < planarTiltThreshold - postureHysteresis;
|
||||||
|
|
||||||
|
if (shouldLie)
|
||||||
|
{
|
||||||
|
_posture = BobberPosture.Lying;
|
||||||
|
}
|
||||||
|
else if (shouldStand)
|
||||||
|
{
|
||||||
|
_posture = BobberPosture.Upright;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case BobberPosture.Upright:
|
||||||
|
{
|
||||||
|
bool shouldLie =
|
||||||
|
submergeRatio < minSubmergeToStand * 0.75f ||
|
||||||
|
_verticalRatio < verticalLieThreshold - postureHysteresis ||
|
||||||
|
_planarDistance > _verticalDistance * (planarDominanceMultiplier + 0.15f);
|
||||||
|
|
||||||
|
bool shouldTilt =
|
||||||
|
_verticalRatio < verticalUprightThreshold - postureHysteresis ||
|
||||||
|
_planarRatio > planarTiltThreshold + postureHysteresis;
|
||||||
|
|
||||||
|
if (shouldLie)
|
||||||
|
{
|
||||||
|
_posture = BobberPosture.Lying;
|
||||||
|
}
|
||||||
|
else if (shouldTilt)
|
||||||
|
{
|
||||||
|
_posture = BobberPosture.Tilted;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateTargetRotationByPosture()
|
||||||
|
{
|
||||||
|
Vector3 planarDir = Vector3.zero;
|
||||||
|
|
||||||
|
if (lureBody != null)
|
||||||
|
{
|
||||||
|
Vector3 delta = lureBody.worldCenterOfMass - _rb.worldCenterOfMass;
|
||||||
|
planarDir = Vector3.ProjectOnPlane(delta, Vector3.up);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (planarDir.sqrMagnitude < 1e-6f)
|
||||||
|
{
|
||||||
|
planarDir = new Vector3(_xzSmoothVelocity.x, 0f, _xzSmoothVelocity.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (planarDir.sqrMagnitude < 1e-6f)
|
||||||
|
{
|
||||||
|
planarDir = new Vector3(ExternalPlanarOffset.x, 0f, ExternalPlanarOffset.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (planarDir.sqrMagnitude < 1e-6f)
|
||||||
|
{
|
||||||
|
planarDir = transform.forward;
|
||||||
|
}
|
||||||
|
|
||||||
|
planarDir.Normalize();
|
||||||
|
|
||||||
|
Vector3 tiltAxis = Vector3.Cross(Vector3.up, planarDir);
|
||||||
|
if (tiltAxis.sqrMagnitude < 1e-6f)
|
||||||
|
{
|
||||||
|
tiltAxis = transform.right;
|
||||||
|
}
|
||||||
|
|
||||||
|
float angle;
|
||||||
|
switch (_posture)
|
||||||
|
{
|
||||||
|
case BobberPosture.Lying:
|
||||||
|
angle = lyingAngle;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BobberPosture.Tilted:
|
||||||
|
{
|
||||||
|
float extra = Mathf.Clamp(_planarRatio * planarTiltFactor, 0f, 18f);
|
||||||
|
angle = Mathf.Clamp(tiltedAngle + extra, tiltedAngle, lyingAngle);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
float extra = Mathf.Clamp(_planarRatio * planarTiltFactor, 0f, uprightMaxTiltAngle);
|
||||||
|
angle = extra;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_targetRotation = Quaternion.AngleAxis(angle, tiltAxis.normalized);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -406,7 +626,7 @@ public class BobberPresentationController : MonoBehaviour
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 送漂:向上抬
|
/// 送漂:向上抬
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void PlayLift(float amplitude = 0.015f, float duration = 0.6f)
|
public void PlayLift(float amplitude = 0.015f, float duration = 1.2f)
|
||||||
{
|
{
|
||||||
StartBite(BobberBiteType.Lift, amplitude, duration);
|
StartBite(BobberBiteType.Lift, amplitude, duration);
|
||||||
}
|
}
|
||||||
@@ -452,8 +672,14 @@ public class BobberPresentationController : MonoBehaviour
|
|||||||
{
|
{
|
||||||
if (_activeBiteType == BobberBiteType.None)
|
if (_activeBiteType == BobberBiteType.None)
|
||||||
{
|
{
|
||||||
_biteOffsetY = Mathf.SmoothDamp(_biteOffsetY, 0f, ref _biteOffsetYVelocity, 0.08f, Mathf.Infinity,
|
_biteOffsetY = Mathf.SmoothDamp(
|
||||||
Time.fixedDeltaTime);
|
_biteOffsetY,
|
||||||
|
0f,
|
||||||
|
ref _biteOffsetYVelocity,
|
||||||
|
0.08f,
|
||||||
|
Mathf.Infinity,
|
||||||
|
Time.fixedDeltaTime
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,7 +691,6 @@ public class BobberPresentationController : MonoBehaviour
|
|||||||
switch (_activeBiteType)
|
switch (_activeBiteType)
|
||||||
{
|
{
|
||||||
case BobberBiteType.Tap:
|
case BobberBiteType.Tap:
|
||||||
// 先快速下压,再回弹
|
|
||||||
if (t < 0.35f)
|
if (t < 0.35f)
|
||||||
{
|
{
|
||||||
float k = t / 0.35f;
|
float k = t / 0.35f;
|
||||||
@@ -476,7 +701,6 @@ public class BobberPresentationController : MonoBehaviour
|
|||||||
float k = (t - 0.35f) / 0.65f;
|
float k = (t - 0.35f) / 0.65f;
|
||||||
targetOffset = -Mathf.Lerp(_biteAmplitude, 0f, k);
|
targetOffset = -Mathf.Lerp(_biteAmplitude, 0f, k);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BobberBiteType.SlowSink:
|
case BobberBiteType.SlowSink:
|
||||||
@@ -505,7 +729,6 @@ public class BobberPresentationController : MonoBehaviour
|
|||||||
{
|
{
|
||||||
if (_activeBiteType == BobberBiteType.SlowSink || _activeBiteType == BobberBiteType.BlackDrift)
|
if (_activeBiteType == BobberBiteType.SlowSink || _activeBiteType == BobberBiteType.BlackDrift)
|
||||||
{
|
{
|
||||||
// 这两种默认停留在最终状态,由外部决定何时 StopBite
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -536,7 +759,7 @@ public class BobberPresentationController : MonoBehaviour
|
|||||||
{
|
{
|
||||||
StopBite();
|
StopBite();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debugTapKey && Input.GetKeyDown(KeyCode.T))
|
if (debugTapKey && Input.GetKeyDown(KeyCode.T))
|
||||||
PlayTap();
|
PlayTap();
|
||||||
|
|
||||||
@@ -569,6 +792,19 @@ public class BobberPresentationController : MonoBehaviour
|
|||||||
Debug.DrawLine(a + Vector3.left * 0.03f, a + Vector3.right * 0.03f, Color.green);
|
Debug.DrawLine(a + Vector3.left * 0.03f, a + Vector3.right * 0.03f, Color.green);
|
||||||
Debug.DrawLine(a + Vector3.forward * 0.03f, a + Vector3.back * 0.03f, Color.green);
|
Debug.DrawLine(a + Vector3.forward * 0.03f, a + Vector3.back * 0.03f, Color.green);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lureBody != null)
|
||||||
|
{
|
||||||
|
Vector3 bobber = _rb.worldCenterOfMass;
|
||||||
|
Vector3 lure = lureBody.worldCenterOfMass;
|
||||||
|
Debug.DrawLine(bobber, lure, Color.magenta);
|
||||||
|
|
||||||
|
Vector3 verticalEnd = bobber + Vector3.down * _verticalDistance;
|
||||||
|
Debug.DrawLine(bobber, verticalEnd, Color.red);
|
||||||
|
|
||||||
|
Vector3 planar = Vector3.ProjectOnPlane(lure - bobber, Vector3.up);
|
||||||
|
Debug.DrawLine(verticalEnd, verticalEnd + planar, Color.blue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
@@ -579,13 +815,26 @@ public class BobberPresentationController : MonoBehaviour
|
|||||||
maxYSpeed = Mathf.Max(0.01f, maxYSpeed);
|
maxYSpeed = Mathf.Max(0.01f, maxYSpeed);
|
||||||
xzSmoothTime = Mathf.Max(0.001f, xzSmoothTime);
|
xzSmoothTime = Mathf.Max(0.001f, xzSmoothTime);
|
||||||
rotationLerpSpeed = Mathf.Max(0.01f, rotationLerpSpeed);
|
rotationLerpSpeed = Mathf.Max(0.01f, rotationLerpSpeed);
|
||||||
maxTiltAngle = Mathf.Clamp(maxTiltAngle, 0f, 89f);
|
|
||||||
maxPlanarOffset = Mathf.Max(0f, maxPlanarOffset);
|
maxPlanarOffset = Mathf.Max(0f, maxPlanarOffset);
|
||||||
downForceToSink = Mathf.Max(0f, downForceToSink);
|
downForceToSink = Mathf.Max(0f, downForceToSink);
|
||||||
maxExtraSink = Mathf.Max(0f, maxExtraSink);
|
maxExtraSink = Mathf.Max(0f, maxExtraSink);
|
||||||
surfaceBobAmplitude = Mathf.Max(0f, surfaceBobAmplitude);
|
surfaceBobAmplitude = Mathf.Max(0f, surfaceBobAmplitude);
|
||||||
surfaceBobFrequency = Mathf.Max(0f, surfaceBobFrequency);
|
surfaceBobFrequency = Mathf.Max(0f, surfaceBobFrequency);
|
||||||
yDeadZone = Mathf.Max(0f, yDeadZone);
|
yDeadZone = Mathf.Max(0f, yDeadZone);
|
||||||
|
|
||||||
|
referenceLength = Mathf.Max(0.0001f, referenceLength);
|
||||||
|
minSubmergeToStand = Mathf.Clamp01(minSubmergeToStand);
|
||||||
|
verticalLieThreshold = Mathf.Clamp(verticalLieThreshold, 0f, 2f);
|
||||||
|
verticalUprightThreshold = Mathf.Max(verticalLieThreshold, verticalUprightThreshold);
|
||||||
|
planarTiltThreshold = Mathf.Clamp(planarTiltThreshold, 0f, 2f);
|
||||||
|
planarDominanceMultiplier = Mathf.Max(0.1f, planarDominanceMultiplier);
|
||||||
|
postureHysteresis = Mathf.Clamp(postureHysteresis, 0f, 0.3f);
|
||||||
|
|
||||||
|
tiltedAngle = Mathf.Clamp(tiltedAngle, 0f, 89f);
|
||||||
|
lyingAngle = Mathf.Clamp(lyingAngle, tiltedAngle, 89.9f);
|
||||||
|
uprightMaxTiltAngle = Mathf.Clamp(uprightMaxTiltAngle, 0f, tiltedAngle);
|
||||||
|
planarTiltFactor = Mathf.Max(0f, planarTiltFactor);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user