From bc7e3d3c4453c4c81b496bf32983dc1d6e83ea84 Mon Sep 17 00:00:00 2001 From: "Bob.Song" Date: Tue, 3 Mar 2026 15:50:04 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9bobber=E6=B5=AE=E5=8A=9B?= =?UTF-8?q?=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Scenes/BobberTest.unity | 604 ++++++++++++++---- Assets/Scripts/Test/BobberBuoyancyStable.cs | 148 ----- .../Scripts/Test/BobberBuoyancyStable.cs.meta | 3 - .../Test/BobberBuoyancyStable_MultiPoint.cs | 155 ----- .../BobberBuoyancyStable_MultiPoint.cs.meta | 3 - Assets/Scripts/Test/BobberFloatingTest.cs | 9 - .../Scripts/Test/BobberFloatingTest.cs.meta | 3 - Assets/Scripts/Test/BobberTest.cs | 24 - Assets/Scripts/Test/BobberTest.cs.meta | 3 - Assets/Scripts/Test/Buoyancy.cs | 65 +- Assets/Scripts/Test/Buoyancy2.cs | 388 ----------- Assets/Scripts/Test/Buoyancy2.cs.meta | 3 - Assets/Scripts/Test/BuoyancyBody.cs | 294 --------- Assets/Scripts/Test/BuoyancyBody.cs.meta | 3 - Assets/Scripts/Test/CapsuleBuoyancy.cs | 95 --- Assets/Scripts/Test/CapsuleBuoyancy.cs.meta | 3 - Assets/Scripts/Test/CapsuleBuoyancyStable.cs | 200 ++++++ .../Test/CapsuleBuoyancyStable.cs.meta | 3 + Assets/Scripts/Test/CentimeterBuoyancy.cs | 317 --------- .../Scripts/Test/CentimeterBuoyancy.cs.meta | 3 - Assets/Scripts/Test/FloatBobberController.cs | 105 --- .../Test/FloatBobberController.cs.meta | 3 - UserSettings/EditorUserSettings.asset | 8 +- 23 files changed, 702 insertions(+), 1740 deletions(-) delete mode 100644 Assets/Scripts/Test/BobberBuoyancyStable.cs delete mode 100644 Assets/Scripts/Test/BobberBuoyancyStable.cs.meta delete mode 100644 Assets/Scripts/Test/BobberBuoyancyStable_MultiPoint.cs delete mode 100644 Assets/Scripts/Test/BobberBuoyancyStable_MultiPoint.cs.meta delete mode 100644 Assets/Scripts/Test/BobberFloatingTest.cs delete mode 100644 Assets/Scripts/Test/BobberFloatingTest.cs.meta delete mode 100644 Assets/Scripts/Test/BobberTest.cs delete mode 100644 Assets/Scripts/Test/BobberTest.cs.meta delete mode 100644 Assets/Scripts/Test/Buoyancy2.cs delete mode 100644 Assets/Scripts/Test/Buoyancy2.cs.meta delete mode 100644 Assets/Scripts/Test/BuoyancyBody.cs delete mode 100644 Assets/Scripts/Test/BuoyancyBody.cs.meta delete mode 100644 Assets/Scripts/Test/CapsuleBuoyancy.cs delete mode 100644 Assets/Scripts/Test/CapsuleBuoyancy.cs.meta create mode 100644 Assets/Scripts/Test/CapsuleBuoyancyStable.cs create mode 100644 Assets/Scripts/Test/CapsuleBuoyancyStable.cs.meta delete mode 100644 Assets/Scripts/Test/CentimeterBuoyancy.cs delete mode 100644 Assets/Scripts/Test/CentimeterBuoyancy.cs.meta delete mode 100644 Assets/Scripts/Test/FloatBobberController.cs delete mode 100644 Assets/Scripts/Test/FloatBobberController.cs.meta diff --git a/Assets/Scenes/BobberTest.unity b/Assets/Scenes/BobberTest.unity index 190dd263e..ac37e88ad 100644 --- a/Assets/Scenes/BobberTest.unity +++ b/Assets/Scenes/BobberTest.unity @@ -391,11 +391,6 @@ Transform: m_Children: [] m_Father: {fileID: 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 GameObject: m_ObjectHideFlags: 0 @@ -714,7 +709,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!64 &668361901 MeshCollider: m_ObjectHideFlags: 0 @@ -1050,7 +1045,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 2147483647 - m_IsActive: 0 + m_IsActive: 1 --- !u!154 &909052970 TerrainCollider: m_ObjectHideFlags: 0 @@ -1120,8 +1115,8 @@ Transform: m_GameObject: {fileID: 909052969} serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: -23.7, y: -0.46, z: -26.45} - m_LocalScale: {x: 1, y: 1, z: 1} + m_LocalPosition: {x: -53, y: -1.75, z: -55.52} + m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} @@ -1900,63 +1895,6 @@ Transform: m_CorrespondingSourceObject: {fileID: 8356280719142672529, guid: 84f17dfc7c7a7485296643a4e64d6200, type: 3} m_PrefabInstance: {fileID: 5499094441789200052} 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 PrefabInstance: m_ObjectHideFlags: 0 @@ -2185,7 +2123,7 @@ Transform: m_GameObject: {fileID: 1518432884} serializedVersion: 2 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_ConstrainProportionsScale: 0 m_Children: [] @@ -2624,6 +2562,205 @@ Transform: m_Children: [] m_Father: {fileID: 2058458420} 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 Transform: m_CorrespondingSourceObject: {fileID: 2452750316707852748, guid: c26fe2b4fef6c484089497b549dd6b04, type: 3} @@ -2720,6 +2857,147 @@ Transform: m_CorrespondingSourceObject: {fileID: 8356280719142672529, guid: 84f17dfc7c7a7485296643a4e64d6200, type: 3} m_PrefabInstance: {fileID: 7347139586361227608} 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 GameObject: m_ObjectHideFlags: 0 @@ -3852,8 +4130,8 @@ Rigidbody: serializedVersion: 5 m_Mass: 0.1 m_LinearDamping: 0.1 - m_AngularDamping: 0.1 - m_CenterOfMass: {x: 0, y: 0.03, z: 0} + m_AngularDamping: 0.01 + m_CenterOfMass: {x: 0, y: 0.04, 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: @@ -3863,7 +4141,7 @@ Rigidbody: serializedVersion: 2 m_Bits: 0 m_ImplicitCom: 0 - m_ImplicitTensor: 1 + m_ImplicitTensor: 0 m_UseGravity: 0 m_IsKinematic: 0 m_Interpolate: 1 @@ -4066,9 +4344,9 @@ CapsuleCollider: m_Enabled: 1 serializedVersion: 2 m_Radius: 0.004 - m_Height: 0.025 + m_Height: 0.035 m_Direction: 1 - m_Center: {x: 0, y: 0.04, z: 0} + m_Center: {x: 0, y: 0.03, z: 0} --- !u!114 &2358608343594615569 MonoBehaviour: m_ObjectHideFlags: 0 @@ -5464,7 +5742,6 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: Assembly-CSharp::Buoyancy _water: {fileID: 2531380344179187550} - includeDeformation: 1 sphereRadiusApproximation: 0.1 waveForceMultiplier: 0.01 currentSpeedMultiplier: 0.01 @@ -6414,20 +6691,43 @@ MonoBehaviour: m_GameObject: {fileID: 2095571565886628079} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 6f273ba33ebe49cd8b31b92db8288a90, type: 3} + m_Script: {fileID: 11500000, guid: f91c9d873c83492ca6d5e3e3a67c1760, 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 + 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!114 &7647511515837834160 MonoBehaviour: @@ -6528,9 +6828,9 @@ Rigidbody: m_ImplicitTensor: 0 m_UseGravity: 0 m_IsKinematic: 0 - m_Interpolate: 0 + m_Interpolate: 1 m_Constraints: 0 - m_CollisionDetection: 2 + m_CollisionDetection: 1 --- !u!54 &8094040829892155629 Rigidbody: m_ObjectHideFlags: 0 @@ -6551,8 +6851,8 @@ Rigidbody: m_ExcludeLayers: serializedVersion: 2 m_Bits: 0 - m_ImplicitCom: 0 - m_ImplicitTensor: 1 + m_ImplicitCom: 1 + m_ImplicitTensor: 0 m_UseGravity: 1 m_IsKinematic: 0 m_Interpolate: 1 @@ -6619,8 +6919,8 @@ GameObject: - component: {fileID: 8114758377478902151} - component: {fileID: 8093361435069273870} - component: {fileID: 8264839114692736908} - - component: {fileID: 8174519546569408267} - component: {fileID: 7647511515837834160} + - component: {fileID: 8264839114692736909} m_Layer: 7 m_Name: Lure m_TagString: Untagged @@ -6659,7 +6959,7 @@ GameObject: - component: {fileID: 8264424693587136968} - component: {fileID: 8264424693587136966} - component: {fileID: 6135364979465448005} - - component: {fileID: 8264424693587136967} + - component: {fileID: 8264424693587136969} m_Layer: 16 m_Name: Float m_TagString: Untagged @@ -6729,7 +7029,7 @@ Transform: m_GameObject: {fileID: 8113175342759564802} serializedVersion: 2 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_ConstrainProportionsScale: 0 m_Children: @@ -6767,8 +7067,7 @@ Transform: m_LocalPosition: {x: 0, y: -1.5, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 - m_Children: - - {fileID: 207925356} + m_Children: [] m_Father: {fileID: 8114378222086924161} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!4 &8114850716269546499 @@ -7030,27 +7329,6 @@ LineRenderer: m_UseWorldSpace: 1 m_Loop: 0 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 GameObject: m_ObjectHideFlags: 0 @@ -7192,30 +7470,6 @@ ConfigurableJoint: m_EnablePreprocessing: 0 m_MassScale: 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 CapsuleCollider: m_ObjectHideFlags: 0 @@ -7239,6 +7493,53 @@ CapsuleCollider: m_Height: 0.025 m_Direction: 1 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 ConfigurableJoint: m_ObjectHideFlags: 0 @@ -7341,6 +7642,27 @@ ConfigurableJoint: m_EnablePreprocessing: 0 m_MassScale: 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 Rigidbody: m_ObjectHideFlags: 0 @@ -7831,6 +8153,7 @@ SceneRoots: - {fileID: 3065509872725565573} - {fileID: 8114378222086924161} - {fileID: 1518432889} + - {fileID: 1738432958} - {fileID: 909052972} - {fileID: 668361904} - {fileID: 154764977} @@ -7838,3 +8161,4 @@ SceneRoots: - {fileID: 7542518911993633412} - {fileID: 5388330205210097294} - {fileID: 820800910} + - {fileID: 1876288045} diff --git a/Assets/Scripts/Test/BobberBuoyancyStable.cs b/Assets/Scripts/Test/BobberBuoyancyStable.cs deleted file mode 100644 index 4a5269a5a..000000000 --- a/Assets/Scripts/Test/BobberBuoyancyStable.cs +++ /dev/null @@ -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(); - capsule = GetComponent(); - 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); - } -} \ No newline at end of file diff --git a/Assets/Scripts/Test/BobberBuoyancyStable.cs.meta b/Assets/Scripts/Test/BobberBuoyancyStable.cs.meta deleted file mode 100644 index dadfb928d..000000000 --- a/Assets/Scripts/Test/BobberBuoyancyStable.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 976f4f103cc04f34a8a81bd4abba2244 -timeCreated: 1772269304 \ No newline at end of file diff --git a/Assets/Scripts/Test/BobberBuoyancyStable_MultiPoint.cs b/Assets/Scripts/Test/BobberBuoyancyStable_MultiPoint.cs deleted file mode 100644 index a4580161f..000000000 --- a/Assets/Scripts/Test/BobberBuoyancyStable_MultiPoint.cs +++ /dev/null @@ -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(); - cap = GetComponent(); - - 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); - } -} \ No newline at end of file diff --git a/Assets/Scripts/Test/BobberBuoyancyStable_MultiPoint.cs.meta b/Assets/Scripts/Test/BobberBuoyancyStable_MultiPoint.cs.meta deleted file mode 100644 index 324f2b202..000000000 --- a/Assets/Scripts/Test/BobberBuoyancyStable_MultiPoint.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: d18347d6a7a44aa8be1ea374a3d9067d -timeCreated: 1772295188 \ No newline at end of file diff --git a/Assets/Scripts/Test/BobberFloatingTest.cs b/Assets/Scripts/Test/BobberFloatingTest.cs deleted file mode 100644 index 3c4375949..000000000 --- a/Assets/Scripts/Test/BobberFloatingTest.cs +++ /dev/null @@ -1,9 +0,0 @@ -using UnityEngine; - -namespace NBF -{ - public class BobberFloatingTest : MonoBehaviour - { - - } -} \ No newline at end of file diff --git a/Assets/Scripts/Test/BobberFloatingTest.cs.meta b/Assets/Scripts/Test/BobberFloatingTest.cs.meta deleted file mode 100644 index 154f8301d..000000000 --- a/Assets/Scripts/Test/BobberFloatingTest.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 75d9ef57b5894116b65988fda7760fc6 -timeCreated: 1772260103 \ No newline at end of file diff --git a/Assets/Scripts/Test/BobberTest.cs b/Assets/Scripts/Test/BobberTest.cs deleted file mode 100644 index 179e13a1e..000000000 --- a/Assets/Scripts/Test/BobberTest.cs +++ /dev/null @@ -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); - } - } -} \ No newline at end of file diff --git a/Assets/Scripts/Test/BobberTest.cs.meta b/Assets/Scripts/Test/BobberTest.cs.meta deleted file mode 100644 index 3edd47aff..000000000 --- a/Assets/Scripts/Test/BobberTest.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 5e21f6b9b3c2483e92fdf2f3dfdcce62 -timeCreated: 1772097978 \ No newline at end of file diff --git a/Assets/Scripts/Test/Buoyancy.cs b/Assets/Scripts/Test/Buoyancy.cs index 5f3461699..cec46119c 100644 --- a/Assets/Scripts/Test/Buoyancy.cs +++ b/Assets/Scripts/Test/Buoyancy.cs @@ -8,20 +8,14 @@ public class Buoyancy : MonoBehaviour { // public WaterSurface targetSurface; 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.")] - public float sphereRadiusApproximation = 0.25f; + [Tooltip("指定由水面水流引起的移动的乘数。")] public float currentSpeedMultiplier = 1f; - [Tooltip("Specifies the multiplier for the movement induced by other deformation (waves, swell... etc).")] - 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; + [Tooltip("指定由介质黏性所引起的阻力的乘数。")] public float dragMultiplier = 1f; public float defaultRigidbodyDrag = 0.1f; @@ -29,15 +23,13 @@ public class Buoyancy : MonoBehaviour 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; - [Tooltip("When enabled, the net force is applied with a random offset to create an angular velocity.")] + [Tooltip("启用后,净力会以随机偏移量施加出来,从而产生角速度。")] public bool applyForceWithRandomOffset; - [Tooltip( - "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; + [Tooltip("启用调试绘制")] public bool drawDebug; private Vector3 currentDirection; @@ -65,8 +57,7 @@ public class Buoyancy : MonoBehaviour rigidbodyComponent.linearDamping = defaultRigidbodyDrag; if (_water == null) { - Debug.LogWarning( - "The variable '_water' needs a valid Water Surface to be assigned for the script to work properly."); + Debug.LogWarning("没有找到水组件"); } } @@ -90,59 +81,59 @@ public class Buoyancy : MonoBehaviour rigidbodyComponent.useGravity = false; - // Cache frequently used values + // 缓存常用值 Vector3 pos = transform.position; Vector3 velocity = rigidbodyComponent.linearVelocity; float radius = sphereRadiusApproximation; float diameter = 2f * radius; - // 1) Sample water surface at current position + // 1) 当前位置的水面示例图 FetchWaterSurfaceData(pos, out waterPosition, out normal, out currentDirection); - // Horizontal “wave push” direction derived from surface normal + // 水平“波浪推动”方向源自表面法线 deformationDirection = Vector3.ProjectOnPlane(normal, Vector3.up); - // 2) Submersion depth of the approximated sphere (0..2R) + // 2) 近似球体的浸没深度(0 至 2R) float bottomY = pos.y - radius; h = Mathf.Clamp(waterPosition.y - bottomY, 0f, diameter); 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); - // 4) Angular damping transitions from air to water + // 4)角向阻尼从空气状态转变为水状态 rigidbodyComponent.angularDamping = Mathf.Lerp( overwaterRigidbodyAngularDrag, underwaterRigidbodyAngularDrag, hNormalized ); - // 5) Forces (kept identical to your original math/units) + // 5) 力(保持与您原始的数学计算/单位一致) Vector3 gravityAcceleration = Physics.gravity; - // NOTE: this is kept as-is (original mixes accel & force then uses Acceleration mode) + // 注意:保持原样(先将加速度和力相加,然后使用加速度模式) Vector3 weightVector = rigidbodyComponent.mass * gravityAcceleration; Vector3 gravityTerm = Vector3.Lerp(gravityAcceleration, weightVector, hNormalized); - // Physical constants (as in original) + // 物理常数(与原文相同) const float rhoWater = 997f; // kg/m^3 const float rhoAir = 0.001293f; // kg/m^3 const float muAir = 0.0000181f; // Pa·s const float muWater = 0.001f; // Pa·s const float dragCoefficient = 0.47f; - // Buoyancy: -ρ * V * g + // 浮力: -ρ * V * g 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 dragWater = MathF.PI * 6f * radius * muWater * (-velocity); Vector3 viscousDragTerm = Vector3.Lerp(dragAir, dragWater, hNormalized) * dragMultiplier; - // Net force (still applied as Acceleration) + // 合力(仍被视为加速度) Vector3 netAcceleration = gravityTerm + buoyancyTerm + viscousDragTerm; - // Optional random offset to induce angular velocity + // 可选的随机偏移量,用于产生角速度 Vector3 randomOffset = Vector3.zero; if (applyForceWithRandomOffset) { @@ -159,12 +150,12 @@ public class Buoyancy : MonoBehaviour ForceMode.Acceleration ); - // 6) Extra forces only around the surface transition (0 0f && hNormalized < 1f) { Vector3 gravityDir = gravityAcceleration.normalized; - // Surface tension damping: cancels velocity along gravity direction + // 表面张力阻尼:抵消沿重力方向的速度 Vector3 verticalVelocity = Vector3.Dot(velocity, gravityDir) * gravityDir; Vector3 surfaceTensionAccel = -verticalVelocity * surfaceTensionDamping; @@ -173,7 +164,7 @@ public class Buoyancy : MonoBehaviour rigidbodyComponent.AddForce(currentDirection * currentSpeedMultiplier, ForceMode.Acceleration); } - // 7) Terminal velocity clamp (same formula, just named) + // 7) 终端速度夹具(公式相同,只是命名不同) float area = MathF.PI * radius * radius; float g = -gravityAcceleration.y; // positive value @@ -221,6 +212,12 @@ public class Buoyancy : MonoBehaviour Gizmos.DrawLine(base.transform.position, base.transform.position + currentDirection); Gizmos.color = Color.yellow; 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); } } } \ No newline at end of file diff --git a/Assets/Scripts/Test/Buoyancy2.cs b/Assets/Scripts/Test/Buoyancy2.cs deleted file mode 100644 index 973061e05..000000000 --- a/Assets/Scripts/Test/Buoyancy2.cs +++ /dev/null @@ -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(); - objCollider = GetComponent(); - - 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); - } -} \ No newline at end of file diff --git a/Assets/Scripts/Test/Buoyancy2.cs.meta b/Assets/Scripts/Test/Buoyancy2.cs.meta deleted file mode 100644 index ad9d6072b..000000000 --- a/Assets/Scripts/Test/Buoyancy2.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 8fb7a174acde428b84360b349d8ca2e3 -timeCreated: 1772376865 \ No newline at end of file diff --git a/Assets/Scripts/Test/BuoyancyBody.cs b/Assets/Scripts/Test/BuoyancyBody.cs deleted file mode 100644 index 990222bb3..000000000 --- a/Assets/Scripts/Test/BuoyancyBody.cs +++ /dev/null @@ -1,294 +0,0 @@ -using System; -using System.Collections.Generic; -using UnityEngine; - - - -/// -/// 多点采样浮力:考虑形状(采样点分布)、重心(Rigidbody.centerOfMass)、扭矩(在点上施力)。 -/// 适合小物体(0.01m级),避免“任何形状都一样直直往下/往上顶飞” -/// -[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 samplePoints = new(); - - private struct SamplePoint - { - public Collider col; - public Vector3 localPos; // 相对 collider.transform 的本地 - } - - void Awake() - { - rb = GetComponent(); - ApplyCOM(); - - waterProvider = waterProviderBehaviour as IWaterProvider; - BuildSamplePoints(); - } - - void OnValidate() - { - if (!rb) rb = GetComponent(); - ApplyCOM(); - - // 在编辑器里改参数时重建采样点 - if (Application.isPlaying == false) - { - // 避免在Prefab编辑等情况下报错 - } - } - - void Reset() - { - rb = GetComponent(); - rb.useGravity = true; - rb.interpolation = RigidbodyInterpolation.Interpolate; - rb.collisionDetectionMode = CollisionDetectionMode.Continuous; - - if (colliders == null || colliders.Length == 0) - colliders = GetComponentsInChildren(); - } - - private void ApplyCOM() - { - if (!rb) return; - rb.centerOfMass = centerOfMassOffset; - } - - /// 手动调用:当你运行时增减Collider或缩放后,建议调用一次 - 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(); - - 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(out var r)) - { - Gizmos.color = Color.red; - Gizmos.DrawSphere(transform.TransformPoint(r.centerOfMass), 0.01f); - } - } -#endif -} \ No newline at end of file diff --git a/Assets/Scripts/Test/BuoyancyBody.cs.meta b/Assets/Scripts/Test/BuoyancyBody.cs.meta deleted file mode 100644 index a0d342bae..000000000 --- a/Assets/Scripts/Test/BuoyancyBody.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 1687f1bac8ee43f8a5705c076bda18ef -timeCreated: 1772380695 \ No newline at end of file diff --git a/Assets/Scripts/Test/CapsuleBuoyancy.cs b/Assets/Scripts/Test/CapsuleBuoyancy.cs deleted file mode 100644 index 3fe7fdb32..000000000 --- a/Assets/Scripts/Test/CapsuleBuoyancy.cs +++ /dev/null @@ -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(); - capsule = GetComponent(); - - 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; - } - } -} \ No newline at end of file diff --git a/Assets/Scripts/Test/CapsuleBuoyancy.cs.meta b/Assets/Scripts/Test/CapsuleBuoyancy.cs.meta deleted file mode 100644 index 08709b72f..000000000 --- a/Assets/Scripts/Test/CapsuleBuoyancy.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 3137aa469d6440af8cdeaa03a4c272d1 -timeCreated: 1772464119 \ No newline at end of file diff --git a/Assets/Scripts/Test/CapsuleBuoyancyStable.cs b/Assets/Scripts/Test/CapsuleBuoyancyStable.cs new file mode 100644 index 000000000..5f8cb469c --- /dev/null +++ b/Assets/Scripts/Test/CapsuleBuoyancyStable.cs @@ -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(); + _cap = GetComponent(); + _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); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Test/CapsuleBuoyancyStable.cs.meta b/Assets/Scripts/Test/CapsuleBuoyancyStable.cs.meta new file mode 100644 index 000000000..1baa145b5 --- /dev/null +++ b/Assets/Scripts/Test/CapsuleBuoyancyStable.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f91c9d873c83492ca6d5e3e3a67c1760 +timeCreated: 1772522093 \ No newline at end of file diff --git a/Assets/Scripts/Test/CentimeterBuoyancy.cs b/Assets/Scripts/Test/CentimeterBuoyancy.cs deleted file mode 100644 index aef9a69a4..000000000 --- a/Assets/Scripts/Test/CentimeterBuoyancy.cs +++ /dev/null @@ -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(); - objCollider = GetComponent(); - - // 计算物体体积(立方厘米) - 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]]); - } - } -} \ No newline at end of file diff --git a/Assets/Scripts/Test/CentimeterBuoyancy.cs.meta b/Assets/Scripts/Test/CentimeterBuoyancy.cs.meta deleted file mode 100644 index bc89d66f7..000000000 --- a/Assets/Scripts/Test/CentimeterBuoyancy.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 3481fc2889d14a00a9de205cc39d9c33 -timeCreated: 1772262032 \ No newline at end of file diff --git a/Assets/Scripts/Test/FloatBobberController.cs b/Assets/Scripts/Test/FloatBobberController.cs deleted file mode 100644 index 3c93e240b..000000000 --- a/Assets/Scripts/Test/FloatBobberController.cs +++ /dev/null @@ -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; - } -} \ No newline at end of file diff --git a/Assets/Scripts/Test/FloatBobberController.cs.meta b/Assets/Scripts/Test/FloatBobberController.cs.meta deleted file mode 100644 index 727132752..000000000 --- a/Assets/Scripts/Test/FloatBobberController.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: f32660b4549141fbb754dfef9a523eda -timeCreated: 1772264277 \ No newline at end of file diff --git a/UserSettings/EditorUserSettings.asset b/UserSettings/EditorUserSettings.asset index 2e1ad56f1..0a7faed54 100644 --- a/UserSettings/EditorUserSettings.asset +++ b/UserSettings/EditorUserSettings.asset @@ -30,14 +30,14 @@ EditorUserSettings: value: 0508070250545c58585e0924437b5d444f4e4b7f7d7a71627f794c64b2e5633a flags: 0 RecentlyUsedSceneGuid-7: - value: 0054045155060d5a5c575f7045270d44474f4e7c7f7924637e2a1832b1b5636d - flags: 0 - RecentlyUsedSceneGuid-8: value: 5409030052070d0d095a5c7412745e444216417c2e7a23642b7e1832bab9363e flags: 0 - RecentlyUsedSceneGuid-9: + RecentlyUsedSceneGuid-8: value: 5606515f5605500b0e5c5c2615760a444615487c2a2a2467297d1932b7e4673a flags: 0 + RecentlyUsedSceneGuid-9: + value: 0054045155060d5a5c575f7045270d44474f4e7c7f7924637e2a1832b1b5636d + flags: 0 UnityEditor.ShaderGraph.Blackboard: value: 18135939215a0a5004000b0e15254b524c030a3f2964643d120d1230e9e93a3fd6e826abbd2e2d293c4ead313b08042de6030a0afa240c0d020be94c4ba75e435d8715fa32c70d15d11612dacc11fee5d3c5d1fe9ab1bf968e93e2ffcbc3e7e2f0b3ffe0e8b0be9afeffa9ffff8e85dd8390e2969e8899daa7 flags: 0