using UnityEngine; public class FloatBobberControllerPro : MonoBehaviour { [Header("Water")] public float waterLevel = 0f; public float waterDensity = 1f; [Header("Bobber Physical")] public float bobberVolume = 30f; // 浮漂最大浮力 (cm³) public float bobberMass = 1f; // 浮漂自重 (g) public float bobberHeight = 0.25f; // 浮漂长度,用来决定躺漂角度 [Header("Tackle Weight")] public float sinkerWeight = 2f; public float baitWeight = 0.5f; public float hookWeight = 0.2f; [Header("Behaviour")] public float fallSpeed = 8f; public float riseSpeed = 3f; public float angleLaySpeed = 2f; // 躺漂速度 public float uprightSpeed = 2f; // 立漂速度 public float bottomDrag = 1.2f; // 铅坠触底阻力(越大越难被浮漂拉起) [Header("Angles")] public float maxLayAngle = 75f; // 最大躺漂角度 float currentAngle = 0f; [Header("Noise")] public float noiseAmp = 0.015f; public float noiseFreq = 1.5f; float impulseForce = 0f; float impulseDecay = 4f; void Update() { SimulateBobber(); } void SimulateBobber() { float totalWeight = bobberMass + sinkerWeight + baitWeight + hookWeight; float netBuoyancy = bobberVolume - totalWeight; // 正 → 上浮;负 → 下拉 // ----------------------------- // ① 计算浮漂底部 Y 的高度 // ----------------------------- float bobberBottomY = transform.position.y - bobberHeight * 0.5f; float bottomY = waterLevel - 0.02f; // 水底高度(可替换真实地形) bool sinkerOnBottom = (bobberBottomY <= bottomY); // ----------------------------- // ② 计算 targetY // ----------------------------- float targetY; if (!sinkerOnBottom) { // 铅坠悬浮 → 浮漂直立 if (netBuoyancy > 0) { float rise = Mathf.Clamp01(netBuoyancy / bobberVolume) * 0.1f; targetY = waterLevel + rise; } else { float sink = Mathf.Abs(netBuoyancy) * 0.02f; targetY = waterLevel - sink; } } else { // 铅坠触底 → 浮漂无法再被向下拉 if (netBuoyancy > bottomDrag) { // 浮漂浮力足够将其立起来 float rise = Mathf.Clamp01((netBuoyancy - bottomDrag) / bobberVolume) * 0.1f; targetY = waterLevel + rise; // 轻轻立起 } else { // 浮漂浮力不足 → 躺漂 targetY = waterLevel + 0.01f; // 漂身贴水 } } // 水波噪声 targetY += Mathf.Sin(Time.time * noiseFreq) * noiseAmp; // 顿口/顶漂力 if (impulseForce != 0f) { targetY += impulseForce * Time.deltaTime; impulseForce = Mathf.Lerp(impulseForce, 0, Time.deltaTime * impulseDecay); } // ----------------------------- // ③ 上浮 / 下沉差速 // ----------------------------- float y = transform.position.y; float diff = targetY - y; if (diff > 0) // 上浮 y += diff * Time.deltaTime * riseSpeed; else y += diff * Time.deltaTime * fallSpeed; transform.position = new Vector3(transform.position.x, y, transform.position.z); // ----------------------------- // ④ 浮漂角度控制 // ----------------------------- float targetAngle = 0f; if (sinkerOnBottom) { // 触底 → 判断是否能立漂 if (netBuoyancy > bottomDrag) { targetAngle = 0f; // 立漂 } else { // 躺漂 targetAngle = maxLayAngle; } } else { // 铅坠在水中 → 漂直立 targetAngle = 0f; } // 平滑角度 currentAngle = Mathf.Lerp( currentAngle, targetAngle, Time.deltaTime * (targetAngle == 0 ? uprightSpeed : angleLaySpeed) ); transform.rotation = Quaternion.Euler(currentAngle, 0, 0); } // ---------------------------------------- // 外部控制接口 // ---------------------------------------- public void TriggerDownPulse(float s = 0.8f) { impulseForce -= Mathf.Abs(s); } public void TriggerUpPulse(float s = 0.8f) { impulseForce += Mathf.Abs(s); } public void AddFishPull(float v) { sinkerWeight += v; } public void ReleaseFishPull(float v) { sinkerWeight -= v; } }