370 lines
14 KiB
C#
370 lines
14 KiB
C#
// 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;
|
||
// }
|
||
// }
|
||
// } |