Files
Fishing2/Assets/Scripts/Test/FloatBobberController.cs
2026-02-26 17:58:34 +08:00

370 lines
14 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 UnityEngine;
//
// [DisallowMultipleComponent]
// public class FloatBobberController : MonoBehaviour
// {
// public enum FloatState
// {
// FreePhysics, // 正常物理
// Standing, // 站漂(可选:更“稳”的物理态)
// Laying, // 躺漂(浮力过大/出水过多)
// AnimBite // 漂相动画接管:黑漂/顿漂/移漂/顶漂
// }
//
// public enum BiteType
// {
// Black, // 黑漂:快速下沉
// Dip, // 顿漂:下压一下回弹
// Move, // 移漂:水平拖动
// Lift // 顶漂:上顶(上浮)
// }
//
// [Header("Refs")]
// public Rigidbody rb;
// public ConfigurableJoint joint; // 浮漂与鱼钩端连接的 joint
// public MonoBehaviour waterProviderMB; // 拖一个实现 IWaterHeightProvider 的组件(如 FlatWaterHeightProvider / KWS适配器
// private IWaterHeightProvider water;
//
// [Header("Buoyancy")]
// [Tooltip("浮漂的有效高度(决定淹没比例计算)")]
// public float floatHeight = 0.18f;
// [Tooltip("浮力系数(越大越容易浮起)")]
// public float buoyancy = 18f;
// [Tooltip("水中阻尼(抑制乱抖/失控)")]
// public float waterDrag = 2.0f;
// [Tooltip("水中角阻尼")]
// public float waterAngularDrag = 2.0f;
//
// [Header("Auto Lay (躺漂判定)")]
// [Tooltip("出水比例超过这个值0~1且浮力明显大于重力时进入躺漂")]
// [Range(0f, 1f)] public float tooMuchOutOfWater01 = 0.55f;
// [Tooltip("浮力/重力比超过该值,认为浮力过大(更容易躺漂)")]
// public float buoyancyOverGravityToLay = 1.15f;
// [Tooltip("躺漂时目标倾角(度)")]
// public float layTiltAngle = 75f;
// [Tooltip("躺漂倾斜速度")]
// public float layTiltSpeed = 6f;
//
// [Header("Joint Drive (用于动画接管)")]
// public float animDriveSpring = 1200f;
// public float animDriveDamper = 120f;
// public float animMaxForce = 10000f;
//
// [Header("Bite Animation Settings")]
// public BiteAnim black = new BiteAnim
// {
// duration = 0.35f,
// verticalOffset = -0.14f,
// curve = AnimationCurve.EaseInOut(0, 0, 1, 1)
// };
//
// public BiteAnim dip = new BiteAnim
// {
// duration = 0.55f,
// verticalOffset = -0.08f,
// curve = new AnimationCurve(
// new Keyframe(0, 0),
// new Keyframe(0.25f, 1f),
// new Keyframe(0.6f, 0.2f),
// new Keyframe(1f, 0)
// )
// };
//
// public BiteAnim lift = new BiteAnim
// {
// duration = 0.6f,
// verticalOffset = +0.06f,
// curve = new AnimationCurve(
// new Keyframe(0, 0),
// new Keyframe(0.3f, 1f),
// new Keyframe(1f, 0)
// )
// };
//
// public BiteAnim move = new BiteAnim
// {
// duration = 0.9f,
// horizontalOffset = 0.18f,
// curve = AnimationCurve.EaseInOut(0, 0, 1, 1)
// };
//
// [System.Serializable]
// public struct BiteAnim
// {
// public float duration;
// public AnimationCurve curve;
// [Tooltip("相对水面的竖直偏移幅度m负数=下沉,正数=上顶")]
// public float verticalOffset;
// [Tooltip("水平偏移幅度m用于移漂沿 forward 或指定方向)")]
// public float horizontalOffset;
// }
//
// [Header("Runtime")]
// public FloatState state = FloatState.FreePhysics;
//
// // 内部:动画接管
// private BiteType currentBite;
// private float biteT;
// private float biteDuration;
// private BiteAnim biteAnim;
// private Vector3 biteDirWorld; // 移漂方向
// private Vector3 jointBaseTargetPos;
// private bool jointHadTarget;
//
// private void Reset()
// {
// rb = GetComponent<Rigidbody>();
// joint = GetComponent<ConfigurableJoint>();
// }
//
// private void Awake()
// {
// if (!rb) rb = GetComponent<Rigidbody>();
// if (!joint) joint = GetComponent<ConfigurableJoint>();
//
// water = waterProviderMB as IWaterHeightProvider;
// if (water == null && waterProviderMB != null)
// water = waterProviderMB.GetComponent<IWaterHeightProvider>();
//
// // joint 初始化建议(确保 targetPosition 驱动生效)
// if (joint)
// {
// joint.configuredInWorldSpace = false; // targetPosition 在 joint 的本地坐标系更稳定
// joint.rotationDriveMode = RotationDriveMode.Slerp;
//
// // 建议锁住角度,避免动画接管时乱拧;如果你需要漂转向可放开
// joint.angularXMotion = ConfigurableJointMotion.Locked;
// joint.angularYMotion = ConfigurableJointMotion.Locked;
// joint.angularZMotion = ConfigurableJointMotion.Locked;
//
// // 位置建议X/Z 限制Y 可自由(看你钓组结构)
// // 这里不强制修改,你项目里如果已经配好了就别动
// }
// }
//
// private void FixedUpdate()
// {
// if (water == null)
// {
// // 没水面采样就只能纯物理
// state = FloatState.FreePhysics;
// return;
// }
//
// Vector3 pos = rb.position;
// float waterY = water.GetWaterHeight(pos);
//
// // 计算淹没比例:以浮漂中心为基准,上下 floatHeight/2
// float topY = pos.y + floatHeight * 0.5f;
// float bottomY = pos.y - floatHeight * 0.5f;
//
// float submerged01 = 0f;
// if (waterY <= bottomY) submerged01 = 0f;
// else if (waterY >= topY) submerged01 = 1f;
// else submerged01 = Mathf.InverseLerp(bottomY, topY, waterY);
//
// float outOfWater01 = 1f - submerged01;
//
// // 基础浮力 & 阻尼(只在水中起作用)
// ApplyBuoyancy(submerged01, waterY);
//
// // 躺漂判定(只在非动画接管时)
// if (state != FloatState.AnimBite)
// {
// bool shouldLay = ShouldLay(outOfWater01, submerged01);
// if (shouldLay)
// state = FloatState.Laying;
// else
// state = FloatState.FreePhysics; // 你也可以在这里细分 Standing
// }
//
// // 状态行为
// switch (state)
// {
// case FloatState.Laying:
// DoLay(waterY);
// break;
//
// case FloatState.AnimBite:
// DoBiteAnim(waterY);
// break;
//
// default:
// // 正常物理时恢复 joint drive避免被动画参数影响
// RestoreJointDriveIfNeeded();
// break;
// }
// }
//
// private void ApplyBuoyancy(float submerged01, float waterY)
// {
// // 水中阻尼:淹没越多阻尼越强
// rb.linearDamping = Mathf.Lerp(rb.linearDamping, submerged01 > 0 ? waterDrag : 0f, 0.25f);
// rb.angularDamping = Mathf.Lerp(rb.angularDamping, submerged01 > 0 ? waterAngularDrag : 0.05f, 0.25f);
//
// if (submerged01 <= 0f) return;
//
// // 浮力:与淹没比例近似线性(你也可以换成平方让“接近全淹没时更强”)
// float g = Physics.gravity.magnitude;
// float buoyForce = buoyancy * submerged01;
//
// // 让浮力作用点略低于中心,产生一点“站漂”稳定性(可选)
// Vector3 forcePoint = rb.worldCenterOfMass + Vector3.down * (floatHeight * 0.15f);
//
// rb.AddForceAtPosition(Vector3.up * buoyForce, forcePoint, ForceMode.Force);
//
// // 额外:把浮漂轻轻拉向水面(避免小抖导致上下乱跳)
// float surfacePull = 6f * submerged01;
// float yError = waterY - rb.position.y;
// rb.AddForce(Vector3.up * (yError * surfacePull), ForceMode.Force);
// }
//
// private bool ShouldLay(float outOfWater01, float submerged01)
// {
// if (submerged01 <= 0f) return false;
//
// // “浮力/重力”粗判浮力系数与质量、g相关
// float gravityForce = rb.mass * Physics.gravity.magnitude;
// // 这里的 buoyancy 是 ForceMode.Force 下的“牛顿”,所以直接比值即可
// float buoyForceAtFull = buoyancy; // submerged=1 时
// float ratio = buoyForceAtFull / Mathf.Max(0.0001f, gravityForce);
//
// return (outOfWater01 >= tooMuchOutOfWater01) && (ratio >= buoyancyOverGravityToLay);
// }
//
// private void DoLay(float waterY)
// {
// // 躺漂:把浮漂慢慢倾斜到 layTiltAngle同时让它更贴近水面
// Quaternion targetRot = Quaternion.AngleAxis(layTiltAngle, transform.right) * Quaternion.LookRotation(transform.forward, Vector3.up);
// rb.MoveRotation(Quaternion.Slerp(rb.rotation, targetRot, layTiltSpeed * Time.fixedDeltaTime));
//
// // 贴近水面一点(不要完全锁死,保持自然)
// float targetY = waterY + floatHeight * 0.15f;
// Vector3 p = rb.position;
// p.y = Mathf.Lerp(p.y, targetY, 3f * Time.fixedDeltaTime);
// rb.MovePosition(p);
//
// RestoreJointDriveIfNeeded();
// }
//
// private void DoBiteAnim(float waterY)
// {
// biteT += Time.fixedDeltaTime;
// float t01 = (biteDuration <= 0.0001f) ? 1f : Mathf.Clamp01(biteT / biteDuration);
// float k = biteAnim.curve != null ? biteAnim.curve.Evaluate(t01) : t01;
//
// // 目标偏移:相对水面
// float yOff = biteAnim.verticalOffset * k;
// float xzOff = biteAnim.horizontalOffset * k;
//
// // 用 joint.targetPosition 驱动:更像“鱼线/钓组拉着漂走/压漂”
// if (joint)
// {
// EnsureJointDriveForAnim();
//
// // joint.targetPosition 是在 joint space本地
// // 这里做一个“基准 targetPosition + 偏移”,基准取进入动画时的 targetPosition
// Vector3 target = jointBaseTargetPos;
//
// // 竖直:沿 joint 的 local Y
// target += Vector3.up * yOff;
//
// // 移漂:我们用世界方向投到 joint 的 local XZ近似
// if (currentBite == BiteType.Move && xzOff != 0f)
// {
// Vector3 worldOffset = biteDirWorld.normalized * xzOff;
// Vector3 localOffset = transform.InverseTransformVector(worldOffset);
// target += new Vector3(localOffset.x, 0f, localOffset.z);
// }
//
// joint.targetPosition = target;
// }
// else
// {
// // 没 joint 就退化:用 MovePosition 做受控位移(稳定但不如 joint 自然)
// Vector3 p = rb.position;
// p.y = Mathf.Lerp(p.y, waterY + yOff, 12f * Time.fixedDeltaTime);
// if (currentBite == BiteType.Move && xzOff != 0f)
// p += biteDirWorld.normalized * (xzOff * 0.2f); // 轻推
// rb.MovePosition(p);
// }
//
// // 动画结束,回到物理
// if (t01 >= 1f)
// {
// state = FloatState.FreePhysics;
// RestoreJointDriveIfNeeded();
// }
// }
//
// private void EnsureJointDriveForAnim()
// {
// if (!joint) return;
//
// if (!jointHadTarget)
// {
// jointHadTarget = true;
// jointBaseTargetPos = joint.targetPosition;
// }
//
// // 只设置一次也行,这里每次保证一致
// JointDrive d = new JointDrive
// {
// positionSpring = animDriveSpring,
// positionDamper = animDriveDamper,
// maximumForce = animMaxForce
// };
//
// joint.xDrive = d;
// joint.yDrive = d;
// joint.zDrive = d;
// }
//
// private void RestoreJointDriveIfNeeded()
// {
// // 退出动画时恢复基准 target避免残留偏移
// if (!joint) return;
// if (!jointHadTarget) return;
//
// // 慢慢回到基准 targetPosition更平滑
// joint.targetPosition = Vector3.Lerp(joint.targetPosition, jointBaseTargetPos, 8f * Time.fixedDeltaTime);
//
// // 当接近后释放
// if ((joint.targetPosition - jointBaseTargetPos).sqrMagnitude < 0.000001f)
// {
// joint.targetPosition = jointBaseTargetPos;
// jointHadTarget = false;
// }
// }
//
// /// <summary>
// /// 外部触发漂相(鱼咬钩/流水/风/玩家提竿前的反馈等)
// /// </summary>
// public void PlayBite(BiteType type, Vector3? moveDirWorld = null)
// {
// currentBite = type;
// biteT = 0f;
//
// biteAnim = type switch
// {
// BiteType.Black => black,
// BiteType.Dip => dip,
// BiteType.Move => move,
// BiteType.Lift => lift,
// _ => black
// };
//
// biteDuration = Mathf.Max(0.02f, biteAnim.duration);
// biteDirWorld = moveDirWorld ?? transform.forward;
//
// // 进入动画接管
// state = FloatState.AnimBite;
//
// // 记录进入动画时 joint 的基准 target
// if (joint)
// {
// jointBaseTargetPos = joint.targetPosition;
// jointHadTarget = true;
// }
// }
// }