361 lines
12 KiB
C#
361 lines
12 KiB
C#
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;
|
||
// }
|
||
// } |