Files
Fishing2/Assets/Scripts/FloatBobberController.cs
2025-11-26 23:44:01 +08:00

361 lines
12 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;
public class FloatBobberController : MonoBehaviour
{
[Header("水属性")]
public float waterLevel = 0f;
[Header("浮漂属性")]
public float bobberVolume = 30f; // 浮漂最大排水体积 (cm³)
public float bobberMass = 1f; // 浮漂自重 (g)
public float bobberHeight = 0.25f; // 浮漂总高度
[Range(0, 1)] public float bobberFloatPartRatio = 0.7f; // 浮漂浮体部分占总高度的比例
[Header("配件重量")]
public float sinkerWeight = 2f;
public float baitWeight = 0.5f;
public float hookWeight = 0.2f;
[Header("行为参数")]
public float fallSpeed = 8f;
public float riseSpeed = 3f;
public float smoothDamping = 8f;
// 私有变量
private float totalDownwardWeight;
private float maxBuoyancyForce;
private float currentSubmergedLength; // 当前浸没长度
private float bobberFloatPartHeight; // 浮体部分高度
// 浮漂状态
private float currentBuoyancy; // 当前实际浮力
private Vector3 targetPosition;
// 冲击力相关
private float impulseForce = 0f;
private float impulseDecay = 4f;
void Start()
{
InitializeBobber();
}
void InitializeBobber()
{
// 计算浮体部分高度(能够产生浮力的部分)
bobberFloatPartHeight = bobberHeight * bobberFloatPartRatio;
// 最大浮力 = 浮体部分完全浸没时的排水量
maxBuoyancyForce = bobberFloatPartHeight * 100f; // 简化计算,可根据需要调整系数
// 初始位置调整:让浮漂底部刚好在水面
Vector3 pos = transform.position;
pos.y = waterLevel - (bobberHeight * 0.5f); // 假设原点在中心,调整到浮漂底部在水面
transform.position = pos;
RecalculateWeights();
}
void FixedUpdate()
{
SimulateBobber();
}
void RecalculateWeights()
{
totalDownwardWeight = bobberMass + sinkerWeight + baitWeight + hookWeight;
}
void SimulateBobber()
{
RecalculateWeights();
// -------------------------
// 1. 计算当前浸没长度和浮力
// -------------------------
// 浮漂的基准位置(底部位置)
float bobberBottomY = transform.position.y;
float bobberTopY = bobberBottomY + bobberHeight;
// 计算浸没长度浮漂底部到水面的距离限制在0到浮体高度之间
float submergedLength = Mathf.Clamp(waterLevel - bobberBottomY, 0f, bobberFloatPartHeight);
currentSubmergedLength = submergedLength;
// 当前浮力 = (浸没长度 / 浮体高度) * 最大浮力
float submergedRatio = submergedLength / bobberFloatPartHeight;
currentBuoyancy = submergedRatio * maxBuoyancyForce;
// -------------------------
// 2. 计算净力并决定运动
// -------------------------
float netForce = currentBuoyancy - totalDownwardWeight;
// 目标Y位置基于净力计算
float targetY = transform.position.y;
if (Mathf.Abs(netForce) < 0.01f) // 基本平衡
{
// 保持当前位置,微小波动可以在这里添加
targetY = transform.position.y;
}
else if (netForce > 0) // 浮力大于重力,上浮
{
float riseAmount = netForce * 0.001f * riseSpeed;
targetY += riseAmount * Time.deltaTime;
// 限制不能浮出太多(露出部分不能超过非浮体部分)
float maxBobberTopY = waterLevel + (bobberHeight - bobberFloatPartHeight);
if (bobberTopY + riseAmount > maxBobberTopY)
{
targetY = maxBobberTopY - bobberHeight;
}
}
else // 重力大于浮力,下沉
{
float sinkAmount = Mathf.Abs(netForce) * 0.001f * fallSpeed;
targetY -= sinkAmount * Time.deltaTime;
// 限制不能沉没太多(浮体部分完全浸没后浮力达到最大)
float minBobberBottomY = waterLevel - bobberFloatPartHeight;
if (bobberBottomY - sinkAmount < minBobberBottomY)
{
targetY = minBobberBottomY;
}
}
// -------------------------
// 3. 应用冲击力
// -------------------------
if (Mathf.Abs(impulseForce) > 0.01f)
{
targetY += impulseForce * Time.deltaTime;
impulseForce = Mathf.Lerp(impulseForce, 0, Time.deltaTime * impulseDecay);
}
// -------------------------
// 4. 平滑移动
// -------------------------
targetPosition = new Vector3(transform.position.x, targetY, transform.position.z);
transform.position = Vector3.Lerp(transform.position, targetPosition, Time.deltaTime * smoothDamping);
// 调试信息
DebugDisplay();
}
void DebugDisplay()
{
Debug.Log($"浸没比例: {(currentSubmergedLength / bobberFloatPartHeight):P1} " +
$"当前浮力: {currentBuoyancy:F2} " +
$"总重力: {totalDownwardWeight:F2} " +
$"净力: {currentBuoyancy - totalDownwardWeight:F2}");
}
// ----------------------------------------
// 公共方法 - 用于调漂和鱼咬钩
// ----------------------------------------
/// <summary>
/// 调整配重(用于调漂)
/// </summary>
public void AdjustSinkerWeight(float newWeight)
{
sinkerWeight = newWeight;
RecalculateWeights();
}
/// <summary>
/// 获取当前调目(浮漂露出水面的格数)
/// </summary>
public float GetCurrentVisibleMarks()
{
float bobberBottomY = transform.position.y;
float visibleHeight = (bobberBottomY + bobberHeight) - waterLevel;
float markHeight = bobberHeight / 10f; // 假设浮漂有10格
return Mathf.Max(0, visibleHeight / markHeight);
}
/// <summary>
/// 设置目标调目(自动计算需要的配重)
/// </summary>
public void SetTargetMarks(float targetMarks)
{
float markHeight = bobberHeight / 10f;
float targetVisibleHeight = targetMarks * markHeight;
float targetSubmergedLength = bobberFloatPartHeight - targetVisibleHeight;
// 需要的浮力 = (浸没长度 / 浮体高度) * 最大浮力
float requiredBuoyancy = (targetSubmergedLength / bobberFloatPartHeight) * maxBuoyancyForce;
// 配重 = 浮漂自重 + 钩饵重 - 需要的浮力
float requiredSinkerWeight = bobberMass + hookWeight + baitWeight - requiredBuoyancy;
sinkerWeight = Mathf.Max(0, requiredSinkerWeight);
RecalculateWeights();
}
// ----------------------------------------
// 鱼咬钩相关方法
// ----------------------------------------
public void TriggerDownPulse(float strength = 0.8f)
{
impulseForce -= Mathf.Abs(strength);
}
public void TriggerUpPulse(float strength = 0.8f)
{
impulseForce += Mathf.Abs(strength);
}
public void AddFishPull(float pullForce)
{
// 鱼的拉力相当于增加向下的力
sinkerWeight += pullForce;
}
public void ReleaseFishPull(float pullForce)
{
sinkerWeight -= pullForce;
}
// ----------------------------------------
// 可视化调试
// ----------------------------------------
void OnDrawGizmos()
{
// 绘制水面
Gizmos.color = Color.blue;
Gizmos.DrawLine(new Vector3(-1, waterLevel, 0), new Vector3(1, waterLevel, 0));
// 绘制浮漂
if (Application.isPlaying)
{
// 浸没部分用蓝色
Gizmos.color = Color.cyan;
float submergedBottom = transform.position.y;
float submergedTop = submergedBottom + currentSubmergedLength;
Gizmos.DrawWireCube(
new Vector3(transform.position.x, (submergedBottom + submergedTop) * 0.5f, transform.position.z),
new Vector3(0.1f, currentSubmergedLength, 0.1f)
);
// 露出部分用红色
Gizmos.color = Color.red;
float visibleBottom = submergedTop;
float visibleTop = transform.position.y + bobberHeight;
float visibleHeight = visibleTop - visibleBottom;
if (visibleHeight > 0)
{
Gizmos.DrawWireCube(
new Vector3(transform.position.x, (visibleBottom + visibleTop) * 0.5f, transform.position.z),
new Vector3(0.1f, visibleHeight, 0.1f)
);
}
}
}
}
// using UnityEngine;
//
// public class FloatBobberController : MonoBehaviour
// {
// [Header("水属性")] public float waterLevel = 0f;
// [Header("浮漂最大浮力")] public float bobberVolume = 30f; // 浮漂最大浮力 (cm³)
// [Header("浮漂自重")] public float bobberMass = 1f; // 浮漂自重 (g)
// public float bobberHeight = 0.25f; // 浮漂长度,用来决定躺漂角度
//
// [Header("配件重量")] 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 smoothDamping = 8f; // 插值平滑
//
// // [Header("Noise")] public float noiseAmp = 0.015f;
// // public float noiseFreq = 1.5f;
//
// float impulseForce = 0f;
// float impulseDecay = 4f;
//
// void FixedUpdate()
// {
// SimulateBobber();
// }
//
// void SimulateBobber()
// {
// float totalDownwardWeight = bobberMass + sinkerWeight + baitWeight + hookWeight;
//
// float maxBuoyancy = bobberVolume; // 最大浮力 = 体积
// float netBuoyancy = maxBuoyancy - totalDownwardWeight;
//
// float targetY;
//
// // -------------------------
// // 1. 判断浮漂应该沉多少(吃水深度)
// // -------------------------
// if (netBuoyancy > 0)
// {
// float buoyPercent = Mathf.Clamp01(netBuoyancy / maxBuoyancy);
// float rise = buoyPercent * 0.1f; // 浮漂露出水面的高度
//
// targetY = waterLevel + rise;
//
// // targetY += Mathf.Sin(Time.time * noiseFreq) * noiseAmp; // 微扰模拟波浪
// }
// else
// {
// // 净浮力为负 → 说明浮漂整体被拉下,沉入水中
// float sinkDistance = Mathf.Abs(netBuoyancy) * 0.03f;
// targetY = waterLevel - sinkDistance;
// }
//
// // 顿口/顶漂力
// 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);
// }
//
//
// // ----------------------------------------
// // 外部控制接口
// // ----------------------------------------
//
// 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;
// }
// }