修改bobber浮力脚本

This commit is contained in:
Bob.Song
2026-03-03 15:50:04 +08:00
parent 23d06a1efc
commit bc7e3d3c44
23 changed files with 702 additions and 1740 deletions

View File

@@ -391,11 +391,6 @@ Transform:
m_Children: [] m_Children: []
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
--- !u!4 &207925356 stripped
Transform:
m_CorrespondingSourceObject: {fileID: 4916128310923789, guid: c04ce4d92e8236f4d92e9dded282fe4f, type: 3}
m_PrefabInstance: {fileID: 1384521324}
m_PrefabAsset: {fileID: 0}
--- !u!1 &209664164 --- !u!1 &209664164
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -714,7 +709,7 @@ GameObject:
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
m_NavMeshLayer: 0 m_NavMeshLayer: 0
m_StaticEditorFlags: 0 m_StaticEditorFlags: 0
m_IsActive: 1 m_IsActive: 0
--- !u!64 &668361901 --- !u!64 &668361901
MeshCollider: MeshCollider:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -1050,7 +1045,7 @@ GameObject:
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
m_NavMeshLayer: 0 m_NavMeshLayer: 0
m_StaticEditorFlags: 2147483647 m_StaticEditorFlags: 2147483647
m_IsActive: 0 m_IsActive: 1
--- !u!154 &909052970 --- !u!154 &909052970
TerrainCollider: TerrainCollider:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -1120,8 +1115,8 @@ Transform:
m_GameObject: {fileID: 909052969} m_GameObject: {fileID: 909052969}
serializedVersion: 2 serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -23.7, y: -0.46, z: -26.45} m_LocalPosition: {x: -53, y: -1.75, z: -55.52}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 100, y: 100, z: 100}
m_ConstrainProportionsScale: 0 m_ConstrainProportionsScale: 0
m_Children: [] m_Children: []
m_Father: {fileID: 0} m_Father: {fileID: 0}
@@ -1900,63 +1895,6 @@ Transform:
m_CorrespondingSourceObject: {fileID: 8356280719142672529, guid: 84f17dfc7c7a7485296643a4e64d6200, type: 3} m_CorrespondingSourceObject: {fileID: 8356280719142672529, guid: 84f17dfc7c7a7485296643a4e64d6200, type: 3}
m_PrefabInstance: {fileID: 5499094441789200052} m_PrefabInstance: {fileID: 5499094441789200052}
m_PrefabAsset: {fileID: 0} m_PrefabAsset: {fileID: 0}
--- !u!1001 &1384521324
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 8114758377478902151}
m_Modifications:
- target: {fileID: 1951849957809749, guid: c04ce4d92e8236f4d92e9dded282fe4f, type: 3}
propertyPath: m_Name
value: c_hook_20789
objectReference: {fileID: 0}
- target: {fileID: 4916128310923789, guid: c04ce4d92e8236f4d92e9dded282fe4f, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4916128310923789, guid: c04ce4d92e8236f4d92e9dded282fe4f, type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4916128310923789, guid: c04ce4d92e8236f4d92e9dded282fe4f, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4916128310923789, guid: c04ce4d92e8236f4d92e9dded282fe4f, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 4916128310923789, guid: c04ce4d92e8236f4d92e9dded282fe4f, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4916128310923789, guid: c04ce4d92e8236f4d92e9dded282fe4f, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4916128310923789, guid: c04ce4d92e8236f4d92e9dded282fe4f, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4916128310923789, guid: c04ce4d92e8236f4d92e9dded282fe4f, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4916128310923789, guid: c04ce4d92e8236f4d92e9dded282fe4f, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: -0
objectReference: {fileID: 0}
- target: {fileID: 4916128310923789, guid: c04ce4d92e8236f4d92e9dded282fe4f, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: -0
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: c04ce4d92e8236f4d92e9dded282fe4f, type: 3}
--- !u!1001 &1447897287 --- !u!1001 &1447897287
PrefabInstance: PrefabInstance:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -2185,7 +2123,7 @@ Transform:
m_GameObject: {fileID: 1518432884} m_GameObject: {fileID: 1518432884}
serializedVersion: 2 serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0.3, z: 0} m_LocalPosition: {x: 0, y: 2.3, z: 0}
m_LocalScale: {x: 0.01, y: 0.01, z: 0.01} m_LocalScale: {x: 0.01, y: 0.01, z: 0.01}
m_ConstrainProportionsScale: 0 m_ConstrainProportionsScale: 0
m_Children: [] m_Children: []
@@ -2624,6 +2562,205 @@ Transform:
m_Children: [] m_Children: []
m_Father: {fileID: 2058458420} m_Father: {fileID: 2058458420}
m_LocalEulerAnglesHint: {x: 0, y: -0, z: -0} m_LocalEulerAnglesHint: {x: 0, y: -0, z: -0}
--- !u!1 &1738432953
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1738432958}
- component: {fileID: 1738432957}
- component: {fileID: 1738432956}
- component: {fileID: 1738432955}
- component: {fileID: 1738432954}
m_Layer: 7
m_Name: Lure (1)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!65 &1738432954
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1738432953}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Size: {x: 0.1, y: 0.1, z: 0.1}
m_Center: {x: 0, y: 0, z: 0}
--- !u!114 &1738432955
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1738432953}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ed5bbbc032ec4ca1bb56991d9141e311, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::NBF.LureController
rBody: {fileID: 1738432957}
joint: {fileID: 1738432956}
--- !u!153 &1738432956
ConfigurableJoint:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1738432953}
serializedVersion: 4
m_ConnectedBody: {fileID: 8094040829892155629}
m_ConnectedArticulationBody: {fileID: 0}
m_Anchor: {x: 0, y: 0, z: 0}
m_Axis: {x: 0, y: 0, z: 0}
m_AutoConfigureConnectedAnchor: 0
m_ConnectedAnchor: {x: 0, y: 0, z: 0}
m_SecondaryAxis: {x: 0, y: 0, z: 0}
m_XMotion: 1
m_YMotion: 1
m_ZMotion: 1
m_AngularXMotion: 2
m_AngularYMotion: 2
m_AngularZMotion: 2
m_LinearLimitSpring:
spring: 0
damper: 0
m_LinearLimit:
limit: 0.5
bounciness: 0
contactDistance: 0
m_AngularXLimitSpring:
spring: 0
damper: 0
m_LowAngularXLimit:
limit: 0
bounciness: 0
contactDistance: 0
m_HighAngularXLimit:
limit: 0
bounciness: 0
contactDistance: 0
m_AngularYZLimitSpring:
spring: 0
damper: 0
m_AngularYLimit:
limit: 0
bounciness: 0
contactDistance: 0
m_AngularZLimit:
limit: 0
bounciness: 0
contactDistance: 0
m_TargetPosition: {x: 0, y: 0, z: 0}
m_TargetVelocity: {x: 0, y: 0, z: 0}
m_XDrive:
serializedVersion: 4
positionSpring: 0
positionDamper: 0
maximumForce: 3.4028233e+38
useAcceleration: 0
m_YDrive:
serializedVersion: 4
positionSpring: 0
positionDamper: 0
maximumForce: 3.4028233e+38
useAcceleration: 0
m_ZDrive:
serializedVersion: 4
positionSpring: 0
positionDamper: 0
maximumForce: 3.4028233e+38
useAcceleration: 0
m_TargetRotation: {x: 0, y: 0, z: 0, w: 1}
m_TargetAngularVelocity: {x: 0, y: 0, z: 0}
m_RotationDriveMode: 0
m_AngularXDrive:
serializedVersion: 4
positionSpring: 0
positionDamper: 0
maximumForce: 3.4028233e+38
useAcceleration: 0
m_AngularYZDrive:
serializedVersion: 4
positionSpring: 0
positionDamper: 0
maximumForce: 3.4028233e+38
useAcceleration: 0
m_SlerpDrive:
serializedVersion: 4
positionSpring: 0
positionDamper: 0
maximumForce: 3.4028233e+38
useAcceleration: 0
m_ProjectionMode: 1
m_ProjectionDistance: 0
m_ProjectionAngle: 0
m_ConfiguredInWorldSpace: 0
m_SwapBodies: 0
m_BreakForce: Infinity
m_BreakTorque: Infinity
m_EnableCollision: 0
m_EnablePreprocessing: 0
m_MassScale: 1
m_ConnectedMassScale: 1
--- !u!54 &1738432957
Rigidbody:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1738432953}
serializedVersion: 5
m_Mass: 0.01
m_LinearDamping: 1
m_AngularDamping: 0.1
m_CenterOfMass: {x: 0, y: 0, z: 0}
m_InertiaTensor: {x: 0.001, y: 0.001, z: 0.001}
m_InertiaRotation: {x: 0, y: 0, z: 0, w: 1}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_ImplicitCom: 1
m_ImplicitTensor: 0
m_UseGravity: 0
m_IsKinematic: 0
m_Interpolate: 0
m_Constraints: 0
m_CollisionDetection: 0
--- !u!4 &1738432958
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1738432953}
serializedVersion: 2
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!4 &1801125034 stripped --- !u!4 &1801125034 stripped
Transform: Transform:
m_CorrespondingSourceObject: {fileID: 2452750316707852748, guid: c26fe2b4fef6c484089497b549dd6b04, type: 3} m_CorrespondingSourceObject: {fileID: 2452750316707852748, guid: c26fe2b4fef6c484089497b549dd6b04, type: 3}
@@ -2720,6 +2857,147 @@ Transform:
m_CorrespondingSourceObject: {fileID: 8356280719142672529, guid: 84f17dfc7c7a7485296643a4e64d6200, type: 3} m_CorrespondingSourceObject: {fileID: 8356280719142672529, guid: 84f17dfc7c7a7485296643a4e64d6200, type: 3}
m_PrefabInstance: {fileID: 7347139586361227608} m_PrefabInstance: {fileID: 7347139586361227608}
m_PrefabAsset: {fileID: 0} m_PrefabAsset: {fileID: 0}
--- !u!1 &1876288040
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1876288045}
- component: {fileID: 1876288044}
- component: {fileID: 1876288043}
- component: {fileID: 1876288042}
- component: {fileID: 1876288041}
m_Layer: 0
m_Name: Capsule
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!54 &1876288041
Rigidbody:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1876288040}
serializedVersion: 5
m_Mass: 1
m_LinearDamping: 0
m_AngularDamping: 0.05
m_CenterOfMass: {x: 0, y: 0, z: 0}
m_InertiaTensor: {x: 1, y: 1, z: 1}
m_InertiaRotation: {x: 0, y: 0, z: 0, w: 1}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_ImplicitCom: 1
m_ImplicitTensor: 1
m_UseGravity: 1
m_IsKinematic: 0
m_Interpolate: 0
m_Constraints: 0
m_CollisionDetection: 0
--- !u!136 &1876288042
CapsuleCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1876288040}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 2
m_Radius: 0.5
m_Height: 2
m_Direction: 1
m_Center: {x: 0, y: 0, z: 0}
--- !u!23 &1876288043
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1876288040}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!33 &1876288044
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1876288040}
m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0}
--- !u!4 &1876288045
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1876288040}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0.20572, y: 2, z: -0.45572}
m_LocalScale: {x: 0.2, y: 0.2, z: 0.2}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1899034788 --- !u!1 &1899034788
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -3852,8 +4130,8 @@ Rigidbody:
serializedVersion: 5 serializedVersion: 5
m_Mass: 0.1 m_Mass: 0.1
m_LinearDamping: 0.1 m_LinearDamping: 0.1
m_AngularDamping: 0.1 m_AngularDamping: 0.01
m_CenterOfMass: {x: 0, y: 0.03, z: 0} m_CenterOfMass: {x: 0, y: 0.04, z: 0}
m_InertiaTensor: {x: 0.001, y: 0.001, z: 0.001} m_InertiaTensor: {x: 0.001, y: 0.001, z: 0.001}
m_InertiaRotation: {x: 0, y: 0, z: 0, w: 1} m_InertiaRotation: {x: 0, y: 0, z: 0, w: 1}
m_IncludeLayers: m_IncludeLayers:
@@ -3863,7 +4141,7 @@ Rigidbody:
serializedVersion: 2 serializedVersion: 2
m_Bits: 0 m_Bits: 0
m_ImplicitCom: 0 m_ImplicitCom: 0
m_ImplicitTensor: 1 m_ImplicitTensor: 0
m_UseGravity: 0 m_UseGravity: 0
m_IsKinematic: 0 m_IsKinematic: 0
m_Interpolate: 1 m_Interpolate: 1
@@ -4066,9 +4344,9 @@ CapsuleCollider:
m_Enabled: 1 m_Enabled: 1
serializedVersion: 2 serializedVersion: 2
m_Radius: 0.004 m_Radius: 0.004
m_Height: 0.025 m_Height: 0.035
m_Direction: 1 m_Direction: 1
m_Center: {x: 0, y: 0.04, z: 0} m_Center: {x: 0, y: 0.03, z: 0}
--- !u!114 &2358608343594615569 --- !u!114 &2358608343594615569
MonoBehaviour: MonoBehaviour:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -5464,7 +5742,6 @@ MonoBehaviour:
m_Name: m_Name:
m_EditorClassIdentifier: Assembly-CSharp::Buoyancy m_EditorClassIdentifier: Assembly-CSharp::Buoyancy
_water: {fileID: 2531380344179187550} _water: {fileID: 2531380344179187550}
includeDeformation: 1
sphereRadiusApproximation: 0.1 sphereRadiusApproximation: 0.1
waveForceMultiplier: 0.01 waveForceMultiplier: 0.01
currentSpeedMultiplier: 0.01 currentSpeedMultiplier: 0.01
@@ -6414,20 +6691,43 @@ MonoBehaviour:
m_GameObject: {fileID: 2095571565886628079} m_GameObject: {fileID: 2095571565886628079}
m_Enabled: 1 m_Enabled: 1
m_EditorHideFlags: 0 m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 6f273ba33ebe49cd8b31b92db8288a90, type: 3} m_Script: {fileID: 11500000, guid: f91c9d873c83492ca6d5e3e3a67c1760, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: Assembly-CSharp::Buoyancy m_EditorClassIdentifier: Assembly-CSharp::CapsuleBuoyancyStable
_water: {fileID: 2531380344179187550} WaterBehaviour: {fileID: 820800909}
includeDeformation: 1 buoyancyScale: 1.6
sphereRadiusApproximation: 0.25 samplePoints: 9
waveForceMultiplier: 1 submergenceCurve:
currentSpeedMultiplier: 1 serializedVersion: 2
dragMultiplier: 1 m_Curve:
defaultRigidbodyDrag: 0.1 - serializedVersion: 3
underwaterRigidbodyAngularDrag: 1 time: 0
overwaterRigidbodyAngularDrag: 0.05 value: 0
surfaceTensionDamping: 10 inSlope: 0
applyForceWithRandomOffset: 0 outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1
value: 1
inSlope: 1
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
verticalDamping: 3
angularDamping: 0.6
uprightSpring: 0
uprightDamping: 0.5
uprightAxis: 1
extraDragInWater: 0.8
extraAngularDragInWater: 0.8
drawDebug: 0 drawDebug: 0
--- !u!114 &7647511515837834160 --- !u!114 &7647511515837834160
MonoBehaviour: MonoBehaviour:
@@ -6528,9 +6828,9 @@ Rigidbody:
m_ImplicitTensor: 0 m_ImplicitTensor: 0
m_UseGravity: 0 m_UseGravity: 0
m_IsKinematic: 0 m_IsKinematic: 0
m_Interpolate: 0 m_Interpolate: 1
m_Constraints: 0 m_Constraints: 0
m_CollisionDetection: 2 m_CollisionDetection: 1
--- !u!54 &8094040829892155629 --- !u!54 &8094040829892155629
Rigidbody: Rigidbody:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -6551,8 +6851,8 @@ Rigidbody:
m_ExcludeLayers: m_ExcludeLayers:
serializedVersion: 2 serializedVersion: 2
m_Bits: 0 m_Bits: 0
m_ImplicitCom: 0 m_ImplicitCom: 1
m_ImplicitTensor: 1 m_ImplicitTensor: 0
m_UseGravity: 1 m_UseGravity: 1
m_IsKinematic: 0 m_IsKinematic: 0
m_Interpolate: 1 m_Interpolate: 1
@@ -6619,8 +6919,8 @@ GameObject:
- component: {fileID: 8114758377478902151} - component: {fileID: 8114758377478902151}
- component: {fileID: 8093361435069273870} - component: {fileID: 8093361435069273870}
- component: {fileID: 8264839114692736908} - component: {fileID: 8264839114692736908}
- component: {fileID: 8174519546569408267}
- component: {fileID: 7647511515837834160} - component: {fileID: 7647511515837834160}
- component: {fileID: 8264839114692736909}
m_Layer: 7 m_Layer: 7
m_Name: Lure m_Name: Lure
m_TagString: Untagged m_TagString: Untagged
@@ -6659,7 +6959,7 @@ GameObject:
- component: {fileID: 8264424693587136968} - component: {fileID: 8264424693587136968}
- component: {fileID: 8264424693587136966} - component: {fileID: 8264424693587136966}
- component: {fileID: 6135364979465448005} - component: {fileID: 6135364979465448005}
- component: {fileID: 8264424693587136967} - component: {fileID: 8264424693587136969}
m_Layer: 16 m_Layer: 16
m_Name: Float m_Name: Float
m_TagString: Untagged m_TagString: Untagged
@@ -6729,7 +7029,7 @@ Transform:
m_GameObject: {fileID: 8113175342759564802} m_GameObject: {fileID: 8113175342759564802}
serializedVersion: 2 serializedVersion: 2
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalPosition: {x: 0, y: 2, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0 m_ConstrainProportionsScale: 0
m_Children: m_Children:
@@ -6767,8 +7067,7 @@ Transform:
m_LocalPosition: {x: 0, y: -1.5, z: 0} m_LocalPosition: {x: 0, y: -1.5, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0 m_ConstrainProportionsScale: 0
m_Children: m_Children: []
- {fileID: 207925356}
m_Father: {fileID: 8114378222086924161} m_Father: {fileID: 8114378222086924161}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!4 &8114850716269546499 --- !u!4 &8114850716269546499
@@ -7030,27 +7329,6 @@ LineRenderer:
m_UseWorldSpace: 1 m_UseWorldSpace: 1
m_Loop: 0 m_Loop: 0
m_ApplyActiveColorSpace: 1 m_ApplyActiveColorSpace: 1
--- !u!135 &8174519546569408267
SphereCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8111135382106896842}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Radius: 0.003
m_Center: {x: 0, y: -0.0015, z: 0}
--- !u!1 &8188637675592362683 --- !u!1 &8188637675592362683
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -7192,30 +7470,6 @@ ConfigurableJoint:
m_EnablePreprocessing: 0 m_EnablePreprocessing: 0
m_MassScale: 1 m_MassScale: 1
m_ConnectedMassScale: 1 m_ConnectedMassScale: 1
--- !u!114 &8264424693587136967
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8112599368562234729}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 6f273ba33ebe49cd8b31b92db8288a90, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::Buoyancy
_water: {fileID: 2531380344179187550}
includeDeformation: 1
sphereRadiusApproximation: 0.25
waveForceMultiplier: 1
currentSpeedMultiplier: 1
dragMultiplier: 1
defaultRigidbodyDrag: 0.1
underwaterRigidbodyAngularDrag: 1
overwaterRigidbodyAngularDrag: 0.05
surfaceTensionDamping: 10
applyForceWithRandomOffset: 0
drawDebug: 1
--- !u!136 &8264424693587136968 --- !u!136 &8264424693587136968
CapsuleCollider: CapsuleCollider:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -7239,6 +7493,53 @@ CapsuleCollider:
m_Height: 0.025 m_Height: 0.025
m_Direction: 1 m_Direction: 1
m_Center: {x: 0, y: 0.04, z: 0} m_Center: {x: 0, y: 0.04, z: 0}
--- !u!114 &8264424693587136969
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8112599368562234729}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f91c9d873c83492ca6d5e3e3a67c1760, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::CapsuleBuoyancyStable
WaterBehaviour: {fileID: 820800909}
buoyancyScale: 1.6
samplePoints: 9
submergenceCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1
value: 1
inSlope: 1
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
verticalDamping: 3
angularDamping: 0.6
uprightSpring: 0
uprightDamping: 0.5
uprightAxis: 1
extraDragInWater: 0.8
extraAngularDragInWater: 0.8
drawDebug: 0
--- !u!153 &8264839114692736908 --- !u!153 &8264839114692736908
ConfigurableJoint: ConfigurableJoint:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -7341,6 +7642,27 @@ ConfigurableJoint:
m_EnablePreprocessing: 0 m_EnablePreprocessing: 0
m_MassScale: 1 m_MassScale: 1
m_ConnectedMassScale: 1 m_ConnectedMassScale: 1
--- !u!65 &8264839114692736909
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8111135382106896842}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Size: {x: 0.1, y: 0.1, z: 0.1}
m_Center: {x: 0, y: 0, z: 0}
--- !u!54 &8380770180143436832 --- !u!54 &8380770180143436832
Rigidbody: Rigidbody:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -7831,6 +8153,7 @@ SceneRoots:
- {fileID: 3065509872725565573} - {fileID: 3065509872725565573}
- {fileID: 8114378222086924161} - {fileID: 8114378222086924161}
- {fileID: 1518432889} - {fileID: 1518432889}
- {fileID: 1738432958}
- {fileID: 909052972} - {fileID: 909052972}
- {fileID: 668361904} - {fileID: 668361904}
- {fileID: 154764977} - {fileID: 154764977}
@@ -7838,3 +8161,4 @@ SceneRoots:
- {fileID: 7542518911993633412} - {fileID: 7542518911993633412}
- {fileID: 5388330205210097294} - {fileID: 5388330205210097294}
- {fileID: 820800910} - {fileID: 820800910}
- {fileID: 1876288045}

View File

@@ -1,148 +0,0 @@
using UnityEngine;
[DisallowMultipleComponent]
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(CapsuleCollider))]
public class BobberBuoyancyStable : MonoBehaviour
{
[Header("Water Physics")] public float waterLevel = 0f;
[Tooltip("浮力倍数 (建议1.0-1.5)")] public float buoyancyMultiplier = 1.2f;
[Tooltip("阻尼系数")] public float dampingFactor = 0.3f;
[Header("Debug")] public bool showDebugInfo = true;
private Rigidbody rb;
private CapsuleCollider capsule;
void Awake()
{
rb = GetComponent<Rigidbody>();
capsule = GetComponent<CapsuleCollider>();
rb.useGravity = true;
if (showDebugInfo)
{
Debug.Log($"Initial mass: {rb.mass}kg, Volume: {CalculateTotalVolume():F6}m³");
}
}
void FixedUpdate()
{
ApplyCorrectedBuoyancy();
}
void ApplyCorrectedBuoyancy()
{
// 使用本地坐标系计算边界,支持任意旋转角度
Vector3 localUp = transform.up; // 获取物体的本地向上向量
Vector3 localDown = -localUp;
// 正确获取世界坐标下的胶囊体边界(考虑旋转)
Vector3 worldBottom = transform.TransformPoint(capsule.center +
localDown * (capsule.height * 0.5f));
Vector3 worldTop = transform.TransformPoint(capsule.center +
localUp * (capsule.height * 0.5f));
float bottomY = worldBottom.y;
float topY = worldTop.y;
float totalHeight = Mathf.Abs(topY - bottomY); // 使用绝对值确保高度为正
// 计算浸入深度
float immersionDepth = Mathf.Max(0, waterLevel - bottomY);
if (immersionDepth <= 0) return;
// 计算浸入比例
float immersionRatio = Mathf.Clamp01(immersionDepth / totalHeight);
// 计算总重量
float weight = rb.mass * 9.81f;
// 计算排水体积(关键修正)
float totalVolume = CalculateTotalVolume();
float displacedVolume = totalVolume * immersionRatio;
if (displacedVolume < 0)
{
Debug.LogError($"入水深度异常={displacedVolume}");
displacedVolume = 0;
}
// 计算浮力
float buoyantForce = displacedVolume * 1000f * 9.81f * buoyancyMultiplier;
// 净浮力 = 浮力 - 重量
float netForce = buoyantForce - weight;
// 浮力作用点
float buoyancyY = bottomY + (immersionDepth * 0.5f);
Vector3 buoyancyPoint = new Vector3(transform.position.x, buoyancyY, transform.position.z);
// 应用力
rb.AddForceAtPosition(Vector3.up * netForce, buoyancyPoint, ForceMode.Force);
// 添加阻尼稳定浮动
Vector3 velocity = rb.GetPointVelocity(buoyancyPoint);
Vector3 dampingForce = -velocity * dampingFactor * immersionRatio;
rb.AddForceAtPosition(dampingForce, buoyancyPoint, ForceMode.Force);
if (showDebugInfo)
{
Debug.Log($"Immersion: {immersionRatio:P1}, " +
$"DispVol: {displacedVolume:F6}m³, " +
$"Buoyancy: {buoyantForce:F2}N, " +
$"NetForce: {netForce:F2}N");
}
}
float CalculateTotalVolume()
{
// 胶囊体总体积 = 圆柱体 + 两个半球
float radius = capsule.radius;
float cylinderHeight = capsule.height - (2 * radius); // 减去两端球冠
// 圆柱体积
float cylinderVolume = Mathf.PI * radius * radius * cylinderHeight;
// 两个半球 = 一个完整球体
float sphereVolume = (4f / 3f) * Mathf.PI * Mathf.Pow(radius, 3);
float totalVolume = cylinderVolume + sphereVolume;
return totalVolume;
}
void OnDrawGizmos()
{
if (capsule == null) return;
// 显示胶囊体边界
Vector3 worldBottom = transform.TransformPoint(capsule.center -
Vector3.up * (capsule.height * 0.5f));
Vector3 worldTop = transform.TransformPoint(capsule.center +
Vector3.up * (capsule.height * 0.5f));
Gizmos.color = Color.white;
Gizmos.DrawLine(worldBottom, worldTop);
// 显示浸入部分
float immersion = Mathf.Max(0, waterLevel - worldBottom.y);
if (immersion > 0)
{
Gizmos.color = new Color(0, 0, 1, 0.3f);
float submergedHeight = Mathf.Min(immersion, capsule.height);
Vector3 submergedCenter = new Vector3(
transform.position.x,
worldBottom.y + submergedHeight * 0.5f,
transform.position.z
);
Gizmos.DrawSphere(submergedCenter, capsule.radius);
}
// 显示水面
Gizmos.color = Color.blue;
Vector3 waterPos = new Vector3(transform.position.x, waterLevel, transform.position.z);
Gizmos.DrawWireSphere(waterPos, 0.05f);
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 976f4f103cc04f34a8a81bd4abba2244
timeCreated: 1772269304

View File

@@ -1,155 +0,0 @@
using UnityEngine;
[DisallowMultipleComponent]
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(CapsuleCollider))]
public class BobberBuoyancyStable_MultiPoint : MonoBehaviour
{
[Header("Water")]
public float waterLevelY = 0f;
public float enterWaterDepth = 0.003f;
public float smoothDepth = 0.02f;
[Header("Per-Point Buoyancy Spring (IMPORTANT)")]
[Tooltip("每个采样点的弹簧系数(N/m)。总浮力刚度≈ringPoints * perPointSpring")]
public float perPointSpring = 60f;
[Tooltip("每个采样点的阻尼(N·s/m),用点的竖直速度做阻尼")]
public float perPointDamping = 8f;
[Tooltip("最大上浮加速度限制(0=不限制)")]
public float maxUpAcceleration = 0f;
[Header("Water Drag")]
public float extraLinearDampingInWater = 2f;
public float extraAngularDampingInWater = 2f;
[Header("Center Of Mass")]
public bool driveCenterOfMassFromCapsule = true;
[Tooltip("想更容易躺漂把y调成正数(上移重心)。想更站漂y调成负数(下移重心)。")]
public Vector3 extraCenterOfMassOffset = new Vector3(0f, 0.00f, 0f);
[Header("Sample Ring")]
[Range(4, 12)] public int ringPoints = 4;
[Range(0.2f, 1.2f)] public float ringRadiusScale = 0.9f;
[Tooltip("采样环离最低点的高度(米)。0=贴底;建议 0.001~0.003")]
public float ringLiftFromBottom = 0.0015f;
[Header("Righting (建议先关)")]
public float rightingTorque = 0f;
public float rightingDamping = 0f;
Rigidbody rb;
CapsuleCollider cap;
float airLinearDamping;
float airAngularDamping;
void Awake()
{
rb = GetComponent<Rigidbody>();
cap = GetComponent<CapsuleCollider>();
airLinearDamping = rb.linearDamping;
airAngularDamping = rb.angularDamping;
ApplyCenterOfMass();
rb.maxAngularVelocity = 50f;
}
void FixedUpdate()
{
ApplyCenterOfMass();
// === 1) 用胶囊几何算 bottom/top不使用 cap.bounds ===
// 注意:这里假设 Transform 的缩放是等比或至少 x/z 缩放差不多。
Vector3 centerW = transform.TransformPoint(cap.center);
float scaleY = Mathf.Abs(transform.lossyScale.y);
float scaleX = Mathf.Abs(transform.lossyScale.x);
float scaleZ = Mathf.Abs(transform.lossyScale.z);
float radiusW = cap.radius * Mathf.Max(scaleX, scaleZ);
float halfH_W = (cap.height * 0.5f) * scaleY;
float bottomY = centerW.y - halfH_W;
float topY = centerW.y + halfH_W;
// 用“最低点”判定入水
float bottomSub = waterLevelY - bottomY;
if (bottomSub <= enterWaterDepth)
{
rb.linearDamping = airLinearDamping;
rb.angularDamping = airAngularDamping;
return;
}
float w = Smooth01((bottomSub - enterWaterDepth) / Mathf.Max(1e-4f, smoothDepth));
rb.linearDamping = airLinearDamping + extraLinearDampingInWater * w;
rb.angularDamping = airAngularDamping + extraAngularDampingInWater * w;
// === 2) 在胶囊底部附近放一圈采样点(姿态力矩来自这里) ===
// 环中心在最低点上抬一点ringLiftFromBottom
float ringY = bottomY + ringLiftFromBottom;
Vector3 ringCenter = new Vector3(centerW.x, ringY, centerW.z);
float ringR = radiusW * ringRadiusScale;
// 每点加速限制
float maxForcePerPoint = float.PositiveInfinity;
if (maxUpAcceleration > 0f)
maxForcePerPoint = (rb.mass * maxUpAcceleration) / ringPoints;
for (int i = 0; i < ringPoints; i++)
{
float a = (i / (float)ringPoints) * Mathf.PI * 2f;
// 用世界方向的 right/forward随姿态旋转
Vector3 offset = (transform.right * Mathf.Cos(a) + transform.forward * Mathf.Sin(a)) * ringR;
Vector3 p = ringCenter + offset;
float sub = waterLevelY - p.y;
if (sub <= enterWaterDepth) continue;
float pw = Smooth01((sub - enterWaterDepth) / Mathf.Max(1e-4f, smoothDepth)) * w;
float vY = rb.GetPointVelocity(p).y;
float fY = perPointSpring * sub - perPointDamping * vY;
if (fY < 0f) fY = 0f;
fY *= pw;
if (fY > maxForcePerPoint) fY = maxForcePerPoint;
rb.AddForceAtPosition(Vector3.up * fY, p, ForceMode.Force);
}
// === 3) 可选:归正(建议最后再加) ===
if (rightingTorque > 0f)
{
Vector3 axis = Vector3.Cross(transform.up, Vector3.up);
float mag = axis.magnitude;
if (mag > 1e-5f)
{
axis /= mag;
float angle = Mathf.Asin(Mathf.Clamp(mag, -1f, 1f));
float angVelOnAxis = Vector3.Dot(rb.angularVelocity, axis);
float torque = (rightingTorque * angle - rightingDamping * angVelOnAxis) * w;
rb.AddTorque(axis * torque, ForceMode.Acceleration);
}
}
}
void ApplyCenterOfMass()
{
if (!driveCenterOfMassFromCapsule) return;
rb.centerOfMass = cap.center + extraCenterOfMassOffset;
}
static float Smooth01(float t)
{
t = Mathf.Clamp01(t);
return t * t * (3f - 2f * t);
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: d18347d6a7a44aa8be1ea374a3d9067d
timeCreated: 1772295188

View File

@@ -1,9 +0,0 @@
using UnityEngine;
namespace NBF
{
public class BobberFloatingTest : MonoBehaviour
{
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 75d9ef57b5894116b65988fda7760fc6
timeCreated: 1772260103

View File

@@ -1,24 +0,0 @@
using System;
using NBF;
using UnityEngine;
namespace Test
{
public class BobberTest : MonoBehaviour
{
public Rigidbody rb;
public FLine line;
public float lineLength = 1f;
public float floatLength = 0.5f;
public void Start()
{
line.InitTest(rb);
//有浮漂
line.Lure.SetJointDistance(floatLength);
line.Bobber.SetJointDistance(lineLength - floatLength);
line.SetObiRopeStretch(lineLength - floatLength);
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 5e21f6b9b3c2483e92fdf2f3dfdcce62
timeCreated: 1772097978

View File

@@ -8,20 +8,14 @@ public class Buoyancy : MonoBehaviour
{ {
// public WaterSurface targetSurface; // public WaterSurface targetSurface;
public WaterRenderer _water; public WaterRenderer _water;
[Tooltip("物体浮力的近似半径")] public float sphereRadiusApproximation = 0.25f;
public bool includeDeformation = true; [Tooltip("指定由其他变形(波浪、涌浪等)引起的运动乘数。")] public float waveForceMultiplier = 1f;
[Tooltip("Approxative radius of object for buoyancy.")] [Tooltip("指定由水面水流引起的移动的乘数。")] public float currentSpeedMultiplier = 1f;
public float sphereRadiusApproximation = 0.25f;
[Tooltip("Specifies the multiplier for the movement induced by other deformation (waves, swell... etc).")] [Tooltip("指定由介质黏性所引起的阻力的乘数。")] public float dragMultiplier = 1f;
public float waveForceMultiplier = 1f;
[Tooltip("Specifies the multiplier for the movement induced by the current of the water surface.")]
public float currentSpeedMultiplier = 1f;
[Tooltip("Specifies the multiplier for the drag forces induced by the viscosity of the mediums.")]
public float dragMultiplier = 1f;
public float defaultRigidbodyDrag = 0.1f; public float defaultRigidbodyDrag = 0.1f;
@@ -29,15 +23,13 @@ public class Buoyancy : MonoBehaviour
public float overwaterRigidbodyAngularDrag = 0.05f; public float overwaterRigidbodyAngularDrag = 0.05f;
[Tooltip("Specifies the value for surface tension. A high value will stop the object bouncing faster on water.")] [Tooltip("指定表面张力的数值。数值过高时,物体在水面上弹跳的速度会减慢。")]
public float surfaceTensionDamping = 10f; public float surfaceTensionDamping = 10f;
[Tooltip("When enabled, the net force is applied with a random offset to create an angular velocity.")] [Tooltip("启用后,净力会以随机偏移量施加出来,从而产生角速度。")]
public bool applyForceWithRandomOffset; public bool applyForceWithRandomOffset;
[Tooltip( [Tooltip("启用调试绘制")] public bool drawDebug;
"When enabled, a bunch of gizmos will show showing in blue, the position of the sampling for normals, in magenta the computed normal, in green the direction of the deformation force, in red the direction of the current force, and in a yellow sphere, the approximation volume the buoyancy calculations.")]
public bool drawDebug;
private Vector3 currentDirection; private Vector3 currentDirection;
@@ -65,8 +57,7 @@ public class Buoyancy : MonoBehaviour
rigidbodyComponent.linearDamping = defaultRigidbodyDrag; rigidbodyComponent.linearDamping = defaultRigidbodyDrag;
if (_water == null) if (_water == null)
{ {
Debug.LogWarning( Debug.LogWarning("没有找到水组件");
"The variable '_water' needs a valid Water Surface to be assigned for the script to work properly.");
} }
} }
@@ -90,59 +81,59 @@ public class Buoyancy : MonoBehaviour
rigidbodyComponent.useGravity = false; rigidbodyComponent.useGravity = false;
// Cache frequently used values // 缓存常用值
Vector3 pos = transform.position; Vector3 pos = transform.position;
Vector3 velocity = rigidbodyComponent.linearVelocity; Vector3 velocity = rigidbodyComponent.linearVelocity;
float radius = sphereRadiusApproximation; float radius = sphereRadiusApproximation;
float diameter = 2f * radius; float diameter = 2f * radius;
// 1) Sample water surface at current position // 1) 当前位置的水面示例图
FetchWaterSurfaceData(pos, out waterPosition, out normal, out currentDirection); FetchWaterSurfaceData(pos, out waterPosition, out normal, out currentDirection);
// Horizontal “wave push” direction derived from surface normal // 水平“波浪推动”方向源自表面法线
deformationDirection = Vector3.ProjectOnPlane(normal, Vector3.up); deformationDirection = Vector3.ProjectOnPlane(normal, Vector3.up);
// 2) Submersion depth of the approximated sphere (0..2R) // 2) 近似球体的浸没深度0 至 2R
float bottomY = pos.y - radius; float bottomY = pos.y - radius;
h = Mathf.Clamp(waterPosition.y - bottomY, 0f, diameter); h = Mathf.Clamp(waterPosition.y - bottomY, 0f, diameter);
hNormalized = (diameter > 0f) ? (h / diameter) : 0f; hNormalized = (diameter > 0f) ? (h / diameter) : 0f;
// 3) Submerged volume (spherical cap) V(h) = πh²/3 * (3R - h) // 3) 浸没体积(球冠形) V(h) = πh²/3 * (3R - h)
float submergedVolume = MathF.PI * h * h / 3f * (3f * radius - h); float submergedVolume = MathF.PI * h * h / 3f * (3f * radius - h);
// 4) Angular damping transitions from air to water // 4)角向阻尼从空气状态转变为水状态
rigidbodyComponent.angularDamping = Mathf.Lerp( rigidbodyComponent.angularDamping = Mathf.Lerp(
overwaterRigidbodyAngularDrag, overwaterRigidbodyAngularDrag,
underwaterRigidbodyAngularDrag, underwaterRigidbodyAngularDrag,
hNormalized hNormalized
); );
// 5) Forces (kept identical to your original math/units) // 5) 力(保持与您原始的数学计算/单位一致)
Vector3 gravityAcceleration = Physics.gravity; Vector3 gravityAcceleration = Physics.gravity;
// NOTE: this is kept as-is (original mixes accel & force then uses Acceleration mode) // 注意:保持原样(先将加速度和力相加,然后使用加速度模式)
Vector3 weightVector = rigidbodyComponent.mass * gravityAcceleration; Vector3 weightVector = rigidbodyComponent.mass * gravityAcceleration;
Vector3 gravityTerm = Vector3.Lerp(gravityAcceleration, weightVector, hNormalized); Vector3 gravityTerm = Vector3.Lerp(gravityAcceleration, weightVector, hNormalized);
// Physical constants (as in original) // 物理常数(与原文相同)
const float rhoWater = 997f; // kg/m^3 const float rhoWater = 997f; // kg/m^3
const float rhoAir = 0.001293f; // kg/m^3 const float rhoAir = 0.001293f; // kg/m^3
const float muAir = 0.0000181f; // Pa·s const float muAir = 0.0000181f; // Pa·s
const float muWater = 0.001f; // Pa·s const float muWater = 0.001f; // Pa·s
const float dragCoefficient = 0.47f; const float dragCoefficient = 0.47f;
// Buoyancy: -ρ * V * g // 浮力: -ρ * V * g
Vector3 buoyancyTerm = (-rhoWater) * submergedVolume * gravityAcceleration; Vector3 buoyancyTerm = (-rhoWater) * submergedVolume * gravityAcceleration;
// Linear viscous drag (Stokes-like): 6πRμ(-v) -> blend air/water by submersion // 线性粘性阻力类似斯托克斯效应6πRμ(-v) -> 通过浸入使空气/水混合
Vector3 dragAir = MathF.PI * 6f * radius * muAir * (-velocity); Vector3 dragAir = MathF.PI * 6f * radius * muAir * (-velocity);
Vector3 dragWater = MathF.PI * 6f * radius * muWater * (-velocity); Vector3 dragWater = MathF.PI * 6f * radius * muWater * (-velocity);
Vector3 viscousDragTerm = Vector3.Lerp(dragAir, dragWater, hNormalized) * dragMultiplier; Vector3 viscousDragTerm = Vector3.Lerp(dragAir, dragWater, hNormalized) * dragMultiplier;
// Net force (still applied as Acceleration) // 合力(仍被视为加速度)
Vector3 netAcceleration = gravityTerm + buoyancyTerm + viscousDragTerm; Vector3 netAcceleration = gravityTerm + buoyancyTerm + viscousDragTerm;
// Optional random offset to induce angular velocity // 可选的随机偏移量,用于产生角速度
Vector3 randomOffset = Vector3.zero; Vector3 randomOffset = Vector3.zero;
if (applyForceWithRandomOffset) if (applyForceWithRandomOffset)
{ {
@@ -159,12 +150,12 @@ public class Buoyancy : MonoBehaviour
ForceMode.Acceleration ForceMode.Acceleration
); );
// 6) Extra forces only around the surface transition (0<hN<1) // 6) 仅在表面过渡区域0 < hN < 1存在额外的力作用。
if (hNormalized > 0f && hNormalized < 1f) if (hNormalized > 0f && hNormalized < 1f)
{ {
Vector3 gravityDir = gravityAcceleration.normalized; Vector3 gravityDir = gravityAcceleration.normalized;
// Surface tension damping: cancels velocity along gravity direction // 表面张力阻尼:抵消沿重力方向的速度
Vector3 verticalVelocity = Vector3.Dot(velocity, gravityDir) * gravityDir; Vector3 verticalVelocity = Vector3.Dot(velocity, gravityDir) * gravityDir;
Vector3 surfaceTensionAccel = -verticalVelocity * surfaceTensionDamping; Vector3 surfaceTensionAccel = -verticalVelocity * surfaceTensionDamping;
@@ -173,7 +164,7 @@ public class Buoyancy : MonoBehaviour
rigidbodyComponent.AddForce(currentDirection * currentSpeedMultiplier, ForceMode.Acceleration); rigidbodyComponent.AddForce(currentDirection * currentSpeedMultiplier, ForceMode.Acceleration);
} }
// 7) Terminal velocity clamp (same formula, just named) // 7) 终端速度夹具(公式相同,只是命名不同)
float area = MathF.PI * radius * radius; float area = MathF.PI * radius * radius;
float g = -gravityAcceleration.y; // positive value float g = -gravityAcceleration.y; // positive value
@@ -221,6 +212,12 @@ public class Buoyancy : MonoBehaviour
Gizmos.DrawLine(base.transform.position, base.transform.position + currentDirection); Gizmos.DrawLine(base.transform.position, base.transform.position + currentDirection);
Gizmos.color = Color.yellow; Gizmos.color = Color.yellow;
Gizmos.DrawSphere(base.transform.position, sphereRadiusApproximation); Gizmos.DrawSphere(base.transform.position, sphereRadiusApproximation);
// 绘制 Rigidbody 的重心点位
Vector3 centerOfMassWorld = transform.TransformPoint(rigidbodyComponent != null ? rigidbodyComponent.centerOfMass : Vector3.zero);
Gizmos.color = Color.cyan;
Gizmos.DrawSphere(centerOfMassWorld, 0.1f);
Gizmos.DrawLine(centerOfMassWorld, centerOfMassWorld + Vector3.up * 0.5f);
} }
} }
} }

View File

@@ -1,388 +0,0 @@
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(Collider))]
public class Buoyancy2 : MonoBehaviour
{
[Header("物理参数")] [SerializeField] private float waterDensity = 1000f; // 水的密度 (kg/m³)
[SerializeField] private float gravity = 9.81f; // 重力加速度 (m/s²)
[SerializeField] private float buoyancyMultiplier = 1f; // 浮力倍数
[Header("阻尼设置")] [SerializeField] private float linearDragInWater = 1f; // 水中线性阻尼
[SerializeField] private float angularDragInWater = 0.5f; // 水中角阻尼
[SerializeField] private float airLinearDrag = 0.1f; // 空气中线性阻尼
[SerializeField] private float airAngularDrag = 0.05f; // 空气中角阻尼
[Header("水面设置")] [SerializeField] private float waterSurfaceLevel = 0f; // 水面高度
[SerializeField] private bool autoDetectWaterLevel = true; // 自动检测水面高度
[Header("采样精度")] [SerializeField] private int sampleResolution = 5; // 采样分辨率 (每维采样点数)
[SerializeField] private bool useAdaptiveSampling = true; // 使用自适应采样
[SerializeField] private int maxSamples = 125; // 最大采样点数
[Header("稳定性控制")] [SerializeField] private float stabilityFactor = 0.3f; // 稳定性因子
[SerializeField] private float rightingTorqueStrength = 2f; // 扶正扭矩强度
[SerializeField] private bool enableMetacentricStability = true; // 启用稳心稳定性
[Header("调试显示")] [SerializeField] private bool showDebugInfo = false;
[SerializeField] private bool drawGizmos = true;
[SerializeField] private Color immersedColor = Color.blue;
[SerializeField] private Color surfaceColor = Color.cyan;
// 私有变量
private Rigidbody rb;
private Collider objCollider;
private Vector3[] samplePoints;
private Vector3 centerOfBuoyancy; // 浮心位置
private Vector3 centerOfGravity; // 重心位置
private float totalVolume; // 总体积 (m³)
private float immersedVolume; // 浸入体积 (m³)
private float immersedRatio; // 浸入比例
private Vector3 metacenter; // 稳心位置
private bool isPartiallySubmerged;
// 缓存值
private Vector3 lastPosition;
private Quaternion lastRotation;
private float lastWaterLevel;
void Start()
{
InitializeComponents();
CalculatePhysicalProperties();
InitializeSamplingGrid();
ValidateSetup();
}
void FixedUpdate()
{
UpdateWaterLevel();
CalculateBuoyancy();
ApplyForces();
UpdateDrag();
ApplyStabilityCorrection();
}
void InitializeComponents()
{
rb = GetComponent<Rigidbody>();
objCollider = GetComponent<Collider>();
if (rb == null)
{
Debug.LogError($"{gameObject.name}: 需要 Rigidbody 组件!", this);
enabled = false;
return;
}
if (objCollider == null)
{
Debug.LogError($"{gameObject.name}: 需要 Collider 组件!", this);
enabled = false;
return;
}
// 保存初始重心位置
centerOfGravity = rb.centerOfMass;
}
void CalculatePhysicalProperties()
{
// 计算物体的真实体积
totalVolume = CalculateRealVolume();
if (showDebugInfo)
{
Debug.Log(
$"{gameObject.name} - 体积: {totalVolume:F4} m³, 质量: {rb.mass:F2} kg, 密度: {rb.mass / totalVolume:F2} kg/m³");
}
}
float CalculateRealVolume()
{
// 根据碰撞器类型精确计算体积
if (objCollider is BoxCollider box)
{
Vector3 size = Vector3.Scale(box.size, transform.lossyScale);
return size.x * size.y * size.z;
}
else if (objCollider is SphereCollider sphere)
{
float radius = sphere.radius *
Mathf.Max(transform.lossyScale.x, transform.lossyScale.y, transform.lossyScale.z);
return (4f / 3f) * Mathf.PI * Mathf.Pow(radius, 3);
}
else if (objCollider is CapsuleCollider capsule)
{
float radius = capsule.radius * Mathf.Max(transform.lossyScale.x, transform.lossyScale.z);
float height = capsule.height * transform.lossyScale.y;
return Mathf.PI * radius * radius * height;
}
else if (objCollider is MeshCollider mesh)
{
// 对于网格碰撞器,使用包围盒近似计算
Bounds bounds = mesh.bounds;
Vector3 size = Vector3.Scale(bounds.size, transform.lossyScale);
return size.x * size.y * size.z * 0.6f; // 乘以填充系数
}
else
{
// 其他类型使用包围盒
Bounds bounds = objCollider.bounds;
return bounds.size.x * bounds.size.y * bounds.size.z;
}
}
void InitializeSamplingGrid()
{
int resolution = sampleResolution;
// 根据物体大小自适应调整采样精度
if (useAdaptiveSampling)
{
float objectSize = objCollider.bounds.size.magnitude;
resolution = Mathf.Clamp(Mathf.RoundToInt(objectSize * 10), 3, 8);
}
// 限制最大采样点数
while (resolution * resolution * resolution > maxSamples && resolution > 2)
{
resolution--;
}
int totalSamples = resolution * resolution * resolution;
samplePoints = new Vector3[totalSamples];
int index = 0;
for (int x = 0; x < resolution; x++)
{
for (int y = 0; y < resolution; y++)
{
for (int z = 0; z < resolution; z++)
{
// 生成标准化坐标 [-0.5, 0.5]
float nx = ((float)x / (resolution - 1)) - 0.5f;
float ny = ((float)y / (resolution - 1)) - 0.5f;
float nz = ((float)z / (resolution - 1)) - 0.5f;
samplePoints[index] = new Vector3(nx, ny, nz);
index++;
}
}
}
if (showDebugInfo)
{
Debug.Log($"{gameObject.name} - 采样点数: {totalSamples}, 分辨率: {resolution}");
}
}
void UpdateWaterLevel()
{
if (autoDetectWaterLevel)
{
// 可以在这里添加自动检测水面高度的逻辑
// 例如射线检测或其他方法
}
}
void CalculateBuoyancy()
{
immersedVolume = 0f;
centerOfBuoyancy = Vector3.zero;
int immersedCount = 0;
Bounds bounds = objCollider.bounds;
Vector3 boundsCenter = bounds.center;
Vector3 boundsExtents = bounds.extents;
foreach (Vector3 normalizedPoint in samplePoints)
{
// 将标准化坐标转换为世界坐标
Vector3 worldPoint = boundsCenter + Vector3.Scale(normalizedPoint, boundsExtents * 2f);
if (worldPoint.y < waterSurfaceLevel)
{
immersedVolume += 1f;
centerOfBuoyancy += worldPoint;
immersedCount++;
}
}
// 计算浸入比例
immersedRatio = (float)immersedCount / samplePoints.Length;
// 计算实际浸入体积
immersedVolume = totalVolume * immersedRatio;
// 计算浮心位置
if (immersedCount > 0)
{
centerOfBuoyancy /= immersedCount;
}
else
{
centerOfBuoyancy = rb.worldCenterOfMass;
}
isPartiallySubmerged = immersedRatio > 0f && immersedRatio < 1f;
}
void ApplyForces()
{
if (immersedRatio <= 0f) return;
// 计算浮力: F = ρ × V × g
float buoyancyForceMagnitude = waterDensity * immersedVolume * gravity * buoyancyMultiplier;
// 浮力方向始终向上
Vector3 buoyancyForce = Vector3.up * buoyancyForceMagnitude;
// 在浮心位置施加浮力
rb.AddForceAtPosition(buoyancyForce, centerOfBuoyancy, ForceMode.Force);
// 添加由于浮力分布不均产生的扭矩
if (isPartiallySubmerged)
{
Vector3 buoyancyTorque = Vector3.Cross(centerOfBuoyancy - rb.worldCenterOfMass, buoyancyForce);
rb.AddTorque(buoyancyTorque * stabilityFactor, ForceMode.Force);
}
if (showDebugInfo && immersedRatio > 0.1f)
{
Debug.DrawRay(centerOfBuoyancy, buoyancyForce * 0.01f, Color.blue, Time.fixedDeltaTime);
}
}
void UpdateDrag()
{
// 根据浸入程度插值阻尼系数
float currentLinearDrag = Mathf.Lerp(airLinearDrag, linearDragInWater, immersedRatio);
float currentAngularDrag = Mathf.Lerp(airAngularDrag, angularDragInWater, immersedRatio);
rb.linearDamping = currentLinearDrag;
rb.angularDamping = currentAngularDrag;
}
void ApplyStabilityCorrection()
{
if (!enableMetacentricStability || immersedRatio <= 0f) return;
// 计算稳心位置(简化模型)
CalculateMetacenter();
// 应用扶正扭矩
if (isPartiallySubmerged)
{
Vector3 rightingArm = metacenter - rb.worldCenterOfMass;
Vector3 restoringTorque = Vector3.Cross(rightingArm, Vector3.down) * rightingTorqueStrength * immersedRatio;
rb.AddTorque(restoringTorque, ForceMode.Force);
}
}
void CalculateMetacenter()
{
// 简化的稳心计算:假设稳心在浮心上方一定距离
float metacentricHeight = Mathf.Max(objCollider.bounds.size.y * 0.3f, 0.1f);
metacenter = centerOfBuoyancy + Vector3.up * metacentricHeight;
}
void ValidateSetup()
{
// 检查物体密度是否合理
float objectDensity = rb.mass / totalVolume;
if (objectDensity < waterDensity * 0.1f)
{
Debug.LogWarning($"{gameObject.name}: 物体密度过低 ({objectDensity:F2} kg/m³),可能导致异常行为");
}
else if (objectDensity > waterDensity * 3f)
{
Debug.LogWarning($"{gameObject.name}: 物体密度过高 ({objectDensity:F2} kg/m³),可能快速沉没");
}
}
// 公共接口方法
public float GetImmersedRatio() => immersedRatio;
public float GetImmersedVolume() => immersedVolume;
public Vector3 GetCenterOfBuoyancy() => centerOfBuoyancy;
public bool IsSubmerged() => immersedRatio >= 0.99f;
public bool IsFloating() => immersedRatio > 0f && immersedRatio < 0.99f;
public void SetWaterLevel(float level)
{
waterSurfaceLevel = level;
autoDetectWaterLevel = false;
}
public void SetBuoyancyMultiplier(float multiplier)
{
buoyancyMultiplier = Mathf.Clamp(multiplier, 0.1f, 5f);
}
void OnDrawGizmosSelected()
{
if (!drawGizmos || !Application.isPlaying) return;
// 绘制浮心
Gizmos.color = Color.blue;
Gizmos.DrawSphere(centerOfBuoyancy, 0.05f);
// 绘制重心
if (rb != null)
{
Gizmos.color = Color.red;
Gizmos.DrawSphere(rb.worldCenterOfMass, 0.05f);
}
// 绘制稳心
if (enableMetacentricStability)
{
Gizmos.color = Color.green;
Gizmos.DrawSphere(metacenter, 0.03f);
}
// 绘制采样点
if (samplePoints != null && objCollider != null)
{
Bounds bounds = objCollider.bounds;
Vector3 boundsCenter = bounds.center;
Vector3 boundsExtents = bounds.extents;
foreach (Vector3 normalizedPoint in samplePoints)
{
Vector3 worldPoint = boundsCenter + Vector3.Scale(normalizedPoint, boundsExtents * 2f);
if (worldPoint.y < waterSurfaceLevel)
{
Gizmos.color = immersedColor;
}
else
{
Gizmos.color = surfaceColor;
}
Gizmos.DrawSphere(worldPoint, 0.01f);
}
}
// 绘制水面线
if (Camera.current != null)
{
Vector3 cameraPos = Camera.current.transform.position;
Vector3 waterCenter = new Vector3(cameraPos.x, waterSurfaceLevel, cameraPos.z);
Vector3 waterSize = new Vector3(10f, 0f, 10f);
Gizmos.color = Color.cyan;
Gizmos.DrawWireCube(waterCenter, waterSize);
}
}
void OnValidate()
{
// 参数验证
sampleResolution = Mathf.Clamp(sampleResolution, 2, 10);
buoyancyMultiplier = Mathf.Clamp(buoyancyMultiplier, 0.1f, 5f);
stabilityFactor = Mathf.Clamp(stabilityFactor, 0f, 1f);
rightingTorqueStrength = Mathf.Clamp(rightingTorqueStrength, 0f, 10f);
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 8fb7a174acde428b84360b349d8ca2e3
timeCreated: 1772376865

View File

@@ -1,294 +0,0 @@
using System;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 多点采样浮力:考虑形状(采样点分布)、重心(Rigidbody.centerOfMass)、扭矩(在点上施力)。
/// 适合小物体0.01m级),避免“任何形状都一样直直往下/往上顶飞”
/// </summary>
[DisallowMultipleComponent]
[RequireComponent(typeof(Rigidbody))]
public class BuoyancyBody : MonoBehaviour
{
[Header("Water")]
[Tooltip("如果不填则使用简单水面y = WaterLevel")]
public MonoBehaviour waterProviderBehaviour;
private IWaterProvider waterProvider;
[Tooltip("简单水面模式水面高度y")]
public float waterLevel = 0f;
[Header("Buoyancy (Physical)")]
[Tooltip("水密度 kg/m^3。淡水约1000海水约1025")]
public float waterDensity = 1000f;
[Tooltip("物体密度 kg/m^3。决定浮沉< waterDensity 更容易浮,> waterDensity 更容易沉")]
public float objectDensity = 300f;
[Tooltip("如果 > 0 则强制使用该体积(m^3),否则按 mass/objectDensity 推体积")]
public float overrideVolume = 0f;
[Tooltip("浮力强度缩放调手感用1为物理值")]
public float buoyancyScale = 1f;
[Header("Stabilization / Damping")]
[Tooltip("水中线性阻尼(越大越不弹、越“粘水”)")]
public float linearDamping = 2.5f;
[Tooltip("水中角阻尼(越大越稳定立漂)")]
public float angularDamping = 2.0f;
[Tooltip("入水/出水过渡平滑厚度m。越大越不弹但边界更“软”】【0.01~0.05常用】")]
public float surfaceSmoothing = 0.02f;
[Tooltip("限制单个采样点最大浮力加速度m/s^2防止轻小物体从高处落下冲天")]
public float maxBuoyantAccelPerPoint = 50f;
[Header("Center of Mass")]
[Tooltip("本地空间重心偏移m。可用来模拟胶囊体配重让浮漂能站起来")]
public Vector3 centerOfMassOffset = Vector3.zero;
[Header("Sampling (Shape matters!)")]
[Tooltip("每个Collider生成的采样点数量越大越精确越耗性能。浮漂建议 12~40")]
[Range(4, 200)] public int pointsPerCollider = 24;
[Tooltip("采样点是否只保留在Collider内部对非凸MeshCollider不可靠浮漂建议用Capsule/Box/Sphere")]
public bool keepPointsInsideCollider = true;
[Tooltip("运行时显示采样点(编辑器Gizmos)")]
public bool drawGizmos = true;
[Tooltip("只对这些Collider算浮力为空则自动抓取子物体所有Collider")]
public Collider[] colliders;
private Rigidbody rb;
private readonly List<SamplePoint> samplePoints = new();
private struct SamplePoint
{
public Collider col;
public Vector3 localPos; // 相对 collider.transform 的本地
}
void Awake()
{
rb = GetComponent<Rigidbody>();
ApplyCOM();
waterProvider = waterProviderBehaviour as IWaterProvider;
BuildSamplePoints();
}
void OnValidate()
{
if (!rb) rb = GetComponent<Rigidbody>();
ApplyCOM();
// 在编辑器里改参数时重建采样点
if (Application.isPlaying == false)
{
// 避免在Prefab编辑等情况下报错
}
}
void Reset()
{
rb = GetComponent<Rigidbody>();
rb.useGravity = true;
rb.interpolation = RigidbodyInterpolation.Interpolate;
rb.collisionDetectionMode = CollisionDetectionMode.Continuous;
if (colliders == null || colliders.Length == 0)
colliders = GetComponentsInChildren<Collider>();
}
private void ApplyCOM()
{
if (!rb) return;
rb.centerOfMass = centerOfMassOffset;
}
/// <summary>手动调用当你运行时增减Collider或缩放后建议调用一次</summary>
public void Rebuild()
{
waterProvider = waterProviderBehaviour as IWaterProvider;
ApplyCOM();
BuildSamplePoints();
}
private void BuildSamplePoints()
{
samplePoints.Clear();
Collider[] cols = colliders;
if (cols == null || cols.Length == 0)
cols = GetComponentsInChildren<Collider>();
foreach (var col in cols)
{
if (!col || !col.enabled) continue;
// 用 bounds 做快速采样,再可选筛内部点
var b = col.bounds;
int n = Mathf.Max(4, pointsPerCollider);
// 使用“分层网格+抖动”的方式,保证不同形状/尺寸采样分布不同
int dim = Mathf.CeilToInt(Mathf.Pow(n, 1f / 3f));
dim = Mathf.Max(2, dim);
Vector3 size = b.size;
Vector3 step = new Vector3(
size.x / (dim - 1),
size.y / (dim - 1),
size.z / (dim - 1)
);
// 为了让小物体也稳定如果某轴极小step可能很小没关系
int added = 0;
for (int ix = 0; ix < dim && added < n; ix++)
for (int iy = 0; iy < dim && added < n; iy++)
for (int iz = 0; iz < dim && added < n; iz++)
{
// 0..1
float fx = (dim == 1) ? 0.5f : (float)ix / (dim - 1);
float fy = (dim == 1) ? 0.5f : (float)iy / (dim - 1);
float fz = (dim == 1) ? 0.5f : (float)iz / (dim - 1);
// 抖动(避免规则网格导致“锁姿态”)
Vector3 jitter = new Vector3(
(UnityEngine.Random.value - 0.5f) * step.x * 0.25f,
(UnityEngine.Random.value - 0.5f) * step.y * 0.25f,
(UnityEngine.Random.value - 0.5f) * step.z * 0.25f
);
Vector3 world = new Vector3(
b.min.x + size.x * fx,
b.min.y + size.y * fy,
b.min.z + size.z * fz
) + jitter;
if (keepPointsInsideCollider)
{
// 对凸Collider通常可靠如果点在内部ClosestPoint会返回点本身
Vector3 cp = col.ClosestPoint(world);
if ((cp - world).sqrMagnitude > 1e-8f)
continue;
}
// 转到 collider.transform 本地存储
Vector3 local = col.transform.InverseTransformPoint(world);
samplePoints.Add(new SamplePoint { col = col, localPos = local });
added++;
}
// 如果内部点太少,至少补一点:用 collider.transform.position 周围
if (added < 4)
{
for (int i = added; i < 4; i++)
{
Vector3 world = col.bounds.center + UnityEngine.Random.insideUnitSphere * (col.bounds.extents.magnitude * 0.25f);
Vector3 local = col.transform.InverseTransformPoint(world);
samplePoints.Add(new SamplePoint { col = col, localPos = local });
}
}
}
}
void FixedUpdate()
{
if (samplePoints.Count == 0) BuildSamplePoints();
// 体积估计V = m / rho_object
float volume = overrideVolume > 0f ? overrideVolume : (rb.mass / Mathf.Max(1e-6f, objectDensity));
volume = Mathf.Max(1e-9f, volume);
int count = samplePoints.Count;
float volPerPoint = volume / count;
Vector3 gravity = Physics.gravity; // 通常 (0,-9.81,0)
float gMag = gravity.magnitude;
// 如果重力为0就不算了
if (gMag < 1e-6f) return;
// 逐点施加:浮力 + 阻尼
for (int i = 0; i < count; i++)
{
var sp = samplePoints[i];
if (!sp.col || !sp.col.enabled) continue;
Vector3 worldPos = sp.col.transform.TransformPoint(sp.localPos);
float waterH = waterProvider?.GetWaterHeight(worldPos) ?? waterLevel;
float depth = waterH - worldPos.y; // >0 表示点在水下
if (depth <= 0f) continue;
// 入水过渡在水面附近用平滑厚度做0..1
float submergence01 = (surfaceSmoothing <= 1e-6f)
? 1f
: Mathf.Clamp01(depth / surfaceSmoothing);
// 该点产生的“排水质量”m_displaced = rho_water * V_point * submergence
float displacedMass = waterDensity * volPerPoint * submergence01;
// 浮力 = -g * m_displaced
Vector3 buoyancy = -gravity * displacedMass * buoyancyScale;
// 限制单点最大向上加速度,防止小物体落水被“顶飞”
// a = F/m -> Fmax = amax * m
float Fmax = maxBuoyantAccelPerPoint * rb.mass;
if (buoyancy.sqrMagnitude > Fmax * Fmax)
buoyancy = buoyancy.normalized * Fmax;
// 水中阻尼(相对水流速度)
Vector3 waterVel = waterProvider?.GetWaterVelocity(worldPos) ?? Vector3.zero;
Vector3 pointVel = rb.GetPointVelocity(worldPos);
Vector3 relVel = pointVel - waterVel;
// 阻尼随入水程度增强:越深越“粘”
Vector3 dampingForce = -relVel * (linearDamping * displacedMass);
// 把力施加在采样点上 -> 自然产生扭矩(形状/重心不同效果不同)
rb.AddForceAtPosition(buoyancy + dampingForce, worldPos, ForceMode.Force);
// 额外角阻尼(整体),只在“确实有一部分在水里”时施加更合理,
// 这里用 submergence01 做比例:越入水越强
rb.AddTorque(-rb.angularVelocity * (angularDamping * displacedMass), ForceMode.Force);
}
}
#if UNITY_EDITOR
void OnDrawGizmosSelected()
{
if (!drawGizmos) return;
Gizmos.matrix = Matrix4x4.identity;
// 水面
Gizmos.color = new Color(0f, 0.6f, 1f, 0.25f);
Vector3 p = transform.position;
float y = (waterProvider != null) ? waterProvider.GetWaterHeight(p) : waterLevel;
Gizmos.DrawCube(new Vector3(p.x, y, p.z), new Vector3(0.5f, 0.001f, 0.5f));
// 采样点
if (samplePoints == null) return;
Gizmos.color = Color.yellow;
foreach (var sp in samplePoints)
{
if (!sp.col) continue;
Vector3 w = sp.col.transform.TransformPoint(sp.localPos);
Gizmos.DrawSphere(w, 0.005f);
}
// COM
if (TryGetComponent<Rigidbody>(out var r))
{
Gizmos.color = Color.red;
Gizmos.DrawSphere(transform.TransformPoint(r.centerOfMass), 0.01f);
}
}
#endif
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 1687f1bac8ee43f8a5705c076bda18ef
timeCreated: 1772380695

View File

@@ -1,95 +0,0 @@
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(CapsuleCollider))]
public class CapsuleBuoyancy : MonoBehaviour
{
[Header("Water Settings")]
public float waterHeight = 0f; // 水面高度
public float waterDensity = 1000f; // 水密度
[Header("Buoyancy Settings")]
public int samplePoints = 8; // 采样点数量
public float buoyancyMultiplier = 1.0f; // 浮力强度调节
public float waterDrag = 1.5f; // 水阻力
public float waterAngularDrag = 1.5f; // 水角阻力
private Rigidbody rb;
private CapsuleCollider capsule;
private float capsuleVolume;
private float gravity;
void Start()
{
rb = GetComponent<Rigidbody>();
capsule = GetComponent<CapsuleCollider>();
gravity = Physics.gravity.magnitude;
CalculateCapsuleVolume();
}
void FixedUpdate()
{
ApplyBuoyancy();
}
void CalculateCapsuleVolume()
{
float radius = capsule.radius * Mathf.Max(transform.localScale.x, transform.localScale.z);
float height = Mathf.Max(0, capsule.height * transform.localScale.y - 2f * radius);
float cylinderVolume = Mathf.PI * radius * radius * height;
float sphereVolume = (4f / 3f) * Mathf.PI * radius * radius * radius;
capsuleVolume = cylinderVolume + sphereVolume;
}
void ApplyBuoyancy()
{
float submergedRatioTotal = 0f;
for (int i = 0; i < samplePoints; i++)
{
float t = (float)i / (samplePoints - 1);
Vector3 localPoint = Vector3.up * Mathf.Lerp(
-capsule.height * 0.5f + capsule.radius,
capsule.height * 0.5f - capsule.radius,
t);
Vector3 worldPoint = transform.TransformPoint(localPoint);
float depth = waterHeight - worldPoint.y;
if (depth > 0f)
{
float normalizedDepth = Mathf.Clamp01(depth / (capsule.height / samplePoints));
submergedRatioTotal += normalizedDepth;
float forceMagnitude =
waterDensity *
gravity *
(capsuleVolume / samplePoints) *
normalizedDepth *
buoyancyMultiplier;
Vector3 buoyancyForce = Vector3.up * forceMagnitude;
rb.AddForceAtPosition(buoyancyForce, worldPoint, ForceMode.Force);
}
}
// 如果在水中,加阻力
if (submergedRatioTotal > 0f)
{
rb.linearDamping = waterDrag;
rb.angularDamping = waterAngularDrag;
}
else
{
rb.linearDamping = 0f;
rb.angularDamping = 0.05f;
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 3137aa469d6440af8cdeaa03a4c272d1
timeCreated: 1772464119

View File

@@ -0,0 +1,200 @@
using UnityEngine;
[DisallowMultipleComponent]
[RequireComponent(typeof(Rigidbody), typeof(CapsuleCollider))]
public class CapsuleBuoyancyStable : MonoBehaviour
{
[Header("References")]
public MonoBehaviour WaterBehaviour; // 实现 IWaterProvider
private IWaterProvider Water => WaterBehaviour as IWaterProvider;
[Header("Buoyancy")]
[Tooltip("完全浸没时总浮力 = mass*g*buoyancyScale。>1 更浮。")]
public float buoyancyScale = 1.6f;
[Tooltip("沿胶囊轴向采样点数量(建议 7~11。")]
[Range(3, 15)] public int samplePoints = 9;
[Tooltip("浸没比例曲线0=刚碰水, 1=充分在水下)。")]
public AnimationCurve submergenceCurve = AnimationCurve.Linear(0, 0, 1, 1);
[Header("Damping")]
[Tooltip("上浮方向速度阻尼(越大越不弹)。")]
public float verticalDamping = 3.0f;
[Tooltip("整体角速度阻尼(只施加一次,不要太大)。")]
public float angularDamping = 0.6f;
[Header("Optional Upright Stabilizer (Recommended for bobber)")]
[Tooltip("让胶囊轴向更倾向于对齐世界Up。0=关闭。")]
public float uprightSpring = 0.0f;
[Tooltip("upright 的角速度阻尼。")]
public float uprightDamping = 0.5f;
[Tooltip("胶囊轴向0=X,1=Y,2=Z通常 CapsuleCollider.direction 也一样)。")]
public int uprightAxis = 1;
[Header("Water Drag")]
public float extraDragInWater = 0.8f;
public float extraAngularDragInWater = 0.8f;
[Header("Debug")]
public bool drawDebug = false;
Rigidbody _rb;
CapsuleCollider _cap;
float _baseDrag, _baseAngularDrag;
void Awake()
{
_rb = GetComponent<Rigidbody>();
_cap = GetComponent<CapsuleCollider>();
_baseDrag = _rb.linearDamping;
_baseAngularDrag = _rb.angularDamping;
if (WaterBehaviour != null && Water == null)
Debug.LogError($"{name}: WaterBehaviour 没有实现 IWaterProvider。", this);
}
void FixedUpdate()
{
if (Water == null) return;
GetWorldCapsule(out Vector3 a, out Vector3 b, out float radius);
int n = Mathf.Max(3, samplePoints);
float fullBuoyancy = _rb.mass * Physics.gravity.magnitude * buoyancyScale;
float perPointMax = fullBuoyancy / n;
float subSum = 0f;
int wetCount = 0;
for (int i = 0; i < n; i++)
{
float t = (float)i / (n - 1);
Vector3 p = Vector3.Lerp(a, b, t);
float waterH = Water.GetWaterHeight(p);
float depth = waterH - p.y; // >0 在水下
float sub = Mathf.InverseLerp(-radius, radius, depth); // 0..1
if (sub <= 0f) continue;
sub = Mathf.Clamp01(submergenceCurve.Evaluate(sub));
subSum += sub;
wetCount++;
Vector3 buoyDir = Vector3.up;
Vector3 waterVel = Water.GetWaterVelocity(p);
Vector3 pointVel = _rb.GetPointVelocity(p);
Vector3 relVel = pointVel - waterVel;
// 浮力
Vector3 buoyForce = buoyDir * (perPointMax * sub);
// 只阻尼上浮方向速度分量(防弹跳)
float vUp = Vector3.Dot(relVel, buoyDir);
Vector3 dampForce = -buoyDir * (vUp * verticalDamping * _rb.mass * sub);
_rb.AddForceAtPosition(buoyForce + dampForce, p, ForceMode.Force);
if (drawDebug)
{
Debug.DrawLine(p, p + buoyForce / (_rb.mass * 10f), Color.cyan, 0f, false);
Debug.DrawLine(p, p + dampForce / (_rb.mass * 10f), Color.yellow, 0f, false);
}
}
float subAvg = (wetCount > 0) ? (subSum / wetCount) : 0f;
// 角阻尼:只加一次(关键修复点)
if (subAvg > 0f)
{
_rb.AddTorque(-_rb.angularVelocity * (angularDamping * _rb.mass * subAvg), ForceMode.Force);
}
// 可选upright 稳定器(更像“浮漂自动立起来”)
if (subAvg > 0f && uprightSpring > 0f)
{
Vector3 axisWorld = GetAxisWorld(uprightAxis);
Vector3 targetUp = Vector3.up;
// 误差轴axisWorld 需要对齐 targetUp也可反过来按你浮漂模型选
Vector3 errorAxis = Vector3.Cross(axisWorld, targetUp);
float errorMag = errorAxis.magnitude;
if (errorMag > 1e-6f)
{
errorAxis /= errorMag;
// “弹簧”力矩 + 阻尼(防止在两个角度间抽动)
Vector3 springTorque = errorAxis * (uprightSpring * errorMag * _rb.mass);
Vector3 dampTorque = -_rb.angularVelocity * (uprightDamping * _rb.mass);
_rb.AddTorque((springTorque + dampTorque) * subAvg, ForceMode.Force);
}
}
// 入水整体 drag
if (subAvg > 0.001f)
{
_rb.linearDamping = _baseDrag + extraDragInWater * subAvg;
_rb.angularDamping = _baseAngularDrag + extraAngularDragInWater * subAvg;
}
else
{
_rb.linearDamping = _baseDrag;
_rb.angularDamping = _baseAngularDrag;
}
}
Vector3 GetAxisWorld(int axis)
{
return axis switch
{
0 => transform.right,
2 => transform.forward,
_ => transform.up,
};
}
void GetWorldCapsule(out Vector3 a, out Vector3 b, out float radius)
{
Vector3 lossy = transform.lossyScale;
int dir = _cap.direction; // 0=X,1=Y,2=Z
float scaleAlong = (dir == 0) ? Mathf.Abs(lossy.x) : (dir == 1) ? Mathf.Abs(lossy.y) : Mathf.Abs(lossy.z);
float scaleR;
if (dir == 0) scaleR = Mathf.Max(Mathf.Abs(lossy.y), Mathf.Abs(lossy.z));
else if (dir == 1) scaleR = Mathf.Max(Mathf.Abs(lossy.x), Mathf.Abs(lossy.z));
else scaleR = Mathf.Max(Mathf.Abs(lossy.x), Mathf.Abs(lossy.y));
radius = _cap.radius * scaleR;
Vector3 center = transform.TransformPoint(_cap.center);
Vector3 axisWorld = (dir == 0) ? transform.right : (dir == 1) ? transform.up : transform.forward;
float heightWorld = Mathf.Max(0f, _cap.height * scaleAlong);
float cylinderLen = Mathf.Max(0f, heightWorld - 2f * radius);
Vector3 half = axisWorld * (cylinderLen * 0.5f);
a = center - half;
b = center + half;
}
private void OnDrawGizmosSelected()
{
if (drawDebug)
{
// 绘制 Rigidbody 的重心点位
Vector3 centerOfMassWorld = transform.TransformPoint(_rb != null ? _rb.centerOfMass : Vector3.zero);
Gizmos.color = Color.cyan;
Gizmos.DrawSphere(centerOfMassWorld, 0.1f);
Gizmos.DrawLine(centerOfMassWorld, centerOfMassWorld + Vector3.up * 0.5f);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f91c9d873c83492ca6d5e3e3a67c1760
timeCreated: 1772522093

View File

@@ -1,317 +0,0 @@
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(Collider))]
public class CentimeterBuoyancy : MonoBehaviour
{
[Header("浮力设置")]
[SerializeField] private float waterDensity = 1000f; // 水的密度 kg/m³
[SerializeField] private float buoyancyScale = 1f; // 浮力缩放系数
[SerializeField] private float dragScale = 1f; // 水中阻力系数
[Header("水面设置")]
[SerializeField] private float waterLevel = 0f; // 水面高度
[SerializeField] private LayerMask waterLayer; // 水体层级
[Header("调试")]
[SerializeField] private bool showDebugInfo = true;
[SerializeField] private Color debugColor = Color.cyan;
private Rigidbody rb;
private Collider objCollider;
private float volumeInCm;
private Vector3[] samplePoints;
private Bounds localBounds;
void Start()
{
rb = GetComponent<Rigidbody>();
objCollider = GetComponent<Collider>();
// 计算物体体积(立方厘米)
volumeInCm = CalculateVolumeInCm();
// 计算局部空间的包围盒
CalculateLocalBounds();
if (showDebugInfo)
{
Debug.Log($"物体体积: {volumeInCm:F2} cm³质量: {rb.mass * 1000:F2} g");
Debug.Log($"局部包围盒: 中心 {localBounds.center}, 大小 {localBounds.size}");
}
// 初始化采样点
InitializeSamplePoints();
}
void FixedUpdate()
{
ApplyBuoyancy();
ApplyWaterDrag();
}
// 计算局部空间的包围盒
void CalculateLocalBounds()
{
// 获取碰撞器的局部空间边界
if (objCollider is BoxCollider box)
{
localBounds = new Bounds(box.center, box.size);
}
else if (objCollider is SphereCollider sphere)
{
Vector3 size = Vector3.one * sphere.radius * 2;
localBounds = new Bounds(sphere.center, size);
}
else if (objCollider is CapsuleCollider capsule)
{
float radius = capsule.radius;
float height = capsule.height;
Vector3 size;
// 根据胶囊方向确定尺寸
switch (capsule.direction)
{
case 0: // X轴
size = new Vector3(height, radius * 2, radius * 2);
break;
case 1: // Y轴
size = new Vector3(radius * 2, height, radius * 2);
break;
case 2: // Z轴
size = new Vector3(radius * 2, radius * 2, height);
break;
default:
size = new Vector3(radius * 2, height, radius * 2);
break;
}
localBounds = new Bounds(capsule.center, size);
}
else
{
// 对于复杂碰撞器,使用世界边界转换到局部空间
Bounds worldBounds = objCollider.bounds;
localBounds = new Bounds(
transform.InverseTransformPoint(worldBounds.center),
transform.InverseTransformVector(worldBounds.size)
);
}
}
// 计算体积(立方厘米)
float CalculateVolumeInCm()
{
Bounds bounds = objCollider.bounds;
Vector3 sizeInCm = bounds.size * 100f; // 转换为厘米
// 根据不同碰撞器类型估算体积
if (objCollider is BoxCollider)
{
return sizeInCm.x * sizeInCm.y * sizeInCm.z;
}
else if (objCollider is SphereCollider)
{
float radiusInCm = bounds.extents.x * 100f;
return (4f / 3f) * Mathf.PI * Mathf.Pow(radiusInCm, 3);
}
else if (objCollider is CapsuleCollider)
{
CapsuleCollider capsule = objCollider as CapsuleCollider;
float radiusInCm = capsule.radius * 100f;
float heightInCm = capsule.height * 100f;
return Mathf.PI * radiusInCm * radiusInCm * heightInCm;
}
else
{
// 对于复杂碰撞器,使用边界框估算
return sizeInCm.x * sizeInCm.y * sizeInCm.z * 0.5f;
}
}
// 初始化采样点
void InitializeSamplePoints()
{
int sampleCount = 3; // 每个维度采样点数(减少采样点以提高性能)
// 创建网格采样点用于检测浸入深度
samplePoints = new Vector3[sampleCount * sampleCount * sampleCount];
int index = 0;
for (int x = 0; x < sampleCount; x++)
{
for (int y = 0; y < sampleCount; y++)
{
for (int z = 0; z < sampleCount; z++)
{
// 生成 -0.5 到 0.5 范围内的归一化坐标
float nx = (float)x / (sampleCount - 1) - 0.5f;
float ny = (float)y / (sampleCount - 1) - 0.5f;
float nz = (float)z / (sampleCount - 1) - 0.5f;
samplePoints[index] = new Vector3(nx, ny, nz);
index++;
}
}
}
}
void ApplyBuoyancy()
{
float immersedVolume = 0f;
float totalImmersedDepth = 0f;
int immersedPoints = 0;
Vector3 buoyancyCenter = Vector3.zero;
// 计算浸入体积
foreach (Vector3 normalizedPoint in samplePoints)
{
// 将归一化坐标转换为局部空间坐标
Vector3 localPoint = new Vector3(
localBounds.center.x + normalizedPoint.x * localBounds.size.x,
localBounds.center.y + normalizedPoint.y * localBounds.size.y,
localBounds.center.z + normalizedPoint.z * localBounds.size.z
);
// 转换到世界空间
Vector3 worldPoint = transform.TransformPoint(localPoint);
// 调试:打印第一个点的坐标
if (showDebugInfo && immersedPoints == 0 && samplePoints.Length > 0)
{
Debug.DrawLine(worldPoint, worldPoint + Vector3.up * 0.01f, Color.red, 0.1f);
}
if (worldPoint.y < waterLevel)
{
immersedVolume += 1f;
totalImmersedDepth += (waterLevel - worldPoint.y);
buoyancyCenter += worldPoint;
immersedPoints++;
}
}
// 计算浸入比例
float immersedRatio = (float)immersedPoints / samplePoints.Length;
if (immersedRatio < 0.01f)
return; // 几乎没有浸入,不施加浮力
// 计算平均浸入深度
float averageImmersedDepth = immersedPoints > 0 ? totalImmersedDepth / immersedPoints : 0;
// 计算浮力中心
if (immersedPoints > 0)
{
buoyancyCenter /= immersedPoints;
}
// 计算实际浸入体积(立方厘米)
float actualImmersedVolumeCm = volumeInCm * immersedRatio;
// 转换为立方米1 m³ = 1,000,000 cm³
float actualImmersedVolumeM = actualImmersedVolumeCm / 1000000f;
// 计算浮力 = 水的密度 * 重力加速度 * 浸入体积
// 水的密度默认 1000 kg/m³重力加速度 9.8 m/s²
float buoyancyForceMagnitude = waterDensity * Physics.gravity.magnitude * actualImmersedVolumeM * buoyancyScale;
// 根据浸入深度调整浮力(更深的地方浮力更大)
float depthFactor = Mathf.Clamp01(averageImmersedDepth * 10f); // 每10cm深度增加一倍
buoyancyForceMagnitude *= (1f + depthFactor);
// 施加浮力
Vector3 buoyancyForce = Vector3.up * buoyancyForceMagnitude;
rb.AddForceAtPosition(buoyancyForce, buoyancyCenter, ForceMode.Force);
// 施加浮力产生的扭矩(使物体自然上浮)
Vector3 torque = Vector3.Cross(buoyancyCenter - rb.worldCenterOfMass, buoyancyForce);
rb.AddTorque(torque * 0.1f);
if (showDebugInfo)
{
// 绘制浮力线
Debug.DrawLine(buoyancyCenter, buoyancyCenter + buoyancyForce * 0.001f, debugColor, 0.1f);
// 绘制浸没点
foreach (Vector3 normalizedPoint in samplePoints)
{
Vector3 localPoint = new Vector3(
localBounds.center.x + normalizedPoint.x * localBounds.size.x,
localBounds.center.y + normalizedPoint.y * localBounds.size.y,
localBounds.center.z + normalizedPoint.z * localBounds.size.z
);
Vector3 worldPoint = transform.TransformPoint(localPoint);
if (worldPoint.y < waterLevel)
{
Debug.DrawLine(worldPoint, worldPoint + Vector3.up * 0.005f, debugColor, 0.1f);
}
}
}
}
void ApplyWaterDrag()
{
float immersedRatio = CalculateImmersedRatio();
if (immersedRatio > 0.01f)
{
// 线性阻力
Vector3 dragForce = -rb.linearVelocity * dragScale * immersedRatio * 10f;
rb.AddForce(dragForce, ForceMode.Force);
// 角阻力
Vector3 angularDrag = -rb.angularVelocity * dragScale * immersedRatio * 5f;
rb.AddTorque(angularDrag, ForceMode.Force);
}
}
float CalculateImmersedRatio()
{
int immersedPoints = 0;
foreach (Vector3 normalizedPoint in samplePoints)
{
Vector3 localPoint = new Vector3(
localBounds.center.x + normalizedPoint.x * localBounds.size.x,
localBounds.center.y + normalizedPoint.y * localBounds.size.y,
localBounds.center.z + normalizedPoint.z * localBounds.size.z
);
Vector3 worldPoint = transform.TransformPoint(localPoint);
if (worldPoint.y < waterLevel)
{
immersedPoints++;
}
}
return (float)immersedPoints / samplePoints.Length;
}
void OnDrawGizmosSelected()
{
if (!showDebugInfo || !Application.isPlaying) return;
// 绘制局部包围盒
Gizmos.color = Color.yellow;
Vector3[] corners = new Vector3[8];
// 计算局部包围盒的8个角在世界空间的位置
for (int i = 0; i < 8; i++)
{
Vector3 localCorner = localBounds.center;
localCorner.x += (i & 1) == 0 ? -localBounds.extents.x : localBounds.extents.x;
localCorner.y += (i & 2) == 0 ? -localBounds.extents.y : localBounds.extents.y;
localCorner.z += (i & 4) == 0 ? -localBounds.extents.z : localBounds.extents.z;
corners[i] = transform.TransformPoint(localCorner);
}
// 绘制包围盒的边
int[] edges = new int[] { 0,1, 1,3, 3,2, 2,0, 4,5, 5,7, 7,6, 6,4, 0,4, 1,5, 2,6, 3,7 };
for (int i = 0; i < edges.Length; i += 2)
{
Gizmos.DrawLine(corners[edges[i]], corners[edges[i + 1]]);
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 3481fc2889d14a00a9de205cc39d9c33
timeCreated: 1772262032

View File

@@ -1,105 +0,0 @@
using UnityEngine;
public class FloatBobberController : MonoBehaviour
{
[SerializeField] private Rigidbody _rigidbody;
[Header("水属性")] public float waterLevel = 0f;
[Header("浮漂最大浮力")] public float bobberVolume = 30f; // 浮漂最大浮力 (cm³)
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()
{
if (!_rigidbody.isKinematic) return;
float totalDownwardWeight = 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;
}
else
{
// 净浮力为负 → 说明浮漂整体被拉下,沉入水中
float sinkDistance = Mathf.Abs(netBuoyancy) * 0.03f;
targetY = waterLevel - sinkDistance;
}
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);
}
// ----------------------------------------
// 外部控制接口
// ----------------------------------------
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;
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: f32660b4549141fbb754dfef9a523eda
timeCreated: 1772264277

View File

@@ -30,14 +30,14 @@ EditorUserSettings:
value: 0508070250545c58585e0924437b5d444f4e4b7f7d7a71627f794c64b2e5633a value: 0508070250545c58585e0924437b5d444f4e4b7f7d7a71627f794c64b2e5633a
flags: 0 flags: 0
RecentlyUsedSceneGuid-7: RecentlyUsedSceneGuid-7:
value: 0054045155060d5a5c575f7045270d44474f4e7c7f7924637e2a1832b1b5636d
flags: 0
RecentlyUsedSceneGuid-8:
value: 5409030052070d0d095a5c7412745e444216417c2e7a23642b7e1832bab9363e value: 5409030052070d0d095a5c7412745e444216417c2e7a23642b7e1832bab9363e
flags: 0 flags: 0
RecentlyUsedSceneGuid-9: RecentlyUsedSceneGuid-8:
value: 5606515f5605500b0e5c5c2615760a444615487c2a2a2467297d1932b7e4673a value: 5606515f5605500b0e5c5c2615760a444615487c2a2a2467297d1932b7e4673a
flags: 0 flags: 0
RecentlyUsedSceneGuid-9:
value: 0054045155060d5a5c575f7045270d44474f4e7c7f7924637e2a1832b1b5636d
flags: 0
UnityEditor.ShaderGraph.Blackboard: UnityEditor.ShaderGraph.Blackboard:
value: 18135939215a0a5004000b0e15254b524c030a3f2964643d120d1230e9e93a3fd6e826abbd2e2d293c4ead313b08042de6030a0afa240c0d020be94c4ba75e435d8715fa32c70d15d11612dacc11fee5d3c5d1fe9ab1bf968e93e2ffcbc3e7e2f0b3ffe0e8b0be9afeffa9ffff8e85dd8390e2969e8899daa7 value: 18135939215a0a5004000b0e15254b524c030a3f2964643d120d1230e9e93a3fd6e826abbd2e2d293c4ead313b08042de6030a0afa240c0d020be94c4ba75e435d8715fa32c70d15d11612dacc11fee5d3c5d1fe9ab1bf968e93e2ffcbc3e7e2f0b3ffe0e8b0be9afeffa9ffff8e85dd8390e2969e8899daa7
flags: 0 flags: 0