内容提交

This commit is contained in:
Bob.Song
2026-02-28 17:45:08 +08:00
parent 47be63d324
commit 7ea3a7868c
26 changed files with 2395 additions and 553 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9d3a120cd69656f468963a7b399a2f3b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,35 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: e251860fcd970734c9f14ebd98a146c3, type: 3}
m_Name: GS-20260228 - 171840
m_EditorClassIdentifier: GaiaCore::Gaia.GaiaSession
m_name: Session 20260228-171840
m_description: 'Rocking out at Creativity Central!
If you like Gaia please
consider rating it :)'
m_previewImage: {fileID: 0}
m_dateCreated: 2026/2/28 17:18:40
m_terrainWidth: 1024
m_terrainDepth: 1024
m_terrainHeight: 1024
m_seaLevel: 25
m_spawnDensity: 0.8
m_isLocked: 0
m_previewImageBytes:
m_previewImageWidth: 0
m_previewImageHeight: 0
m_operations: []
m_terrainMinMaxCache: []
m_bakedMaskCacheEntries: []
m_worldBiomeMaskSettings: {fileID: 0}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a8eda7ecde0aa9d4994993b532ba21ae
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 743e8db08f1a57f49b2057d04785e337
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,63 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!850595691 &4890085278179872738
LightingSettings:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Gaia Lighting Settings
serializedVersion: 9
m_EnableBakedLightmaps: 0
m_EnableRealtimeLightmaps: 0
m_RealtimeEnvironmentLighting: 1
m_BounceScale: 1
m_AlbedoBoost: 1
m_IndirectOutputScale: 1
m_UsingShadowmask: 1
m_BakeBackend: 1
m_LightmapMaxSize: 1024
m_LightmapSizeFixed: 0
m_UseMipmapLimits: 1
m_BakeResolution: 40
m_Padding: 2
m_LightmapCompression: 3
m_AO: 0
m_AOMaxDistance: 1
m_CompAOExponent: 1
m_CompAOExponentDirect: 0
m_ExtractAO: 0
m_MixedBakeMode: 2
m_LightmapsBakeMode: 1
m_FilterMode: 1
m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0}
m_ExportTrainingData: 0
m_EnableWorkerProcessBaking: 1
m_TrainingDataDestination: TrainingData
m_RealtimeResolution: 2
m_ForceWhiteAlbedo: 0
m_ForceUpdates: 0
m_PVRCulling: 1
m_PVRSampling: 1
m_PVRDirectSampleCount: 32
m_PVRSampleCount: 512
m_PVREnvironmentSampleCount: 256
m_PVREnvironmentReferencePointCount: 2048
m_LightProbeSampleCountMultiplier: 4
m_PVRBounces: 2
m_PVRMinBounces: 2
m_PVREnvironmentImportanceSampling: 1
m_PVRFilteringMode: 1
m_PVRDenoiserTypeDirect: 1
m_PVRDenoiserTypeIndirect: 1
m_PVRDenoiserTypeAO: 1
m_PVRFilterTypeDirect: 0
m_PVRFilterTypeIndirect: 0
m_PVRFilterTypeAO: 0
m_PVRFilteringGaussRadiusDirect: 1
m_PVRFilteringGaussRadiusIndirect: 1
m_PVRFilteringGaussRadiusAO: 1
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
m_PVRFilteringAtrousPositionSigmaIndirect: 2
m_PVRFilteringAtrousPositionSigmaAO: 1
m_RespectSceneVisibilityWhenBakingGI: 0

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6f2a358d6ae67644f8c199d940dadd27
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 4890085278179872738
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7f56f90b57af5a9448a3fb8ca3f1f2a6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,38 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: da50733ca13d24f45a2980dcd5a56ed5, type: 3}
m_Name: TerrainScenes
m_EditorClassIdentifier: GaiaCore::Gaia.TerrainSceneStorage
m_terrainLoadingEnabled: 1
m_showTerrainLoadingDisabledWarning: 1
m_useAddressables: 0
m_preloadAddressablesWithImpostors: 1
m_colliderOnlyLoading: 0
m_terrainTilesX: 1
m_terrainTilesZ: 1
m_terrainTilesSize: 0
m_useFloatingPointFix: 0
m_hasWorldMap: 0
m_worldMaprelativeSize: 0.5
m_worldMapRelativeHeightmapPixels: 1
m_worldMapPreviewHeightmapResolution: 2049
m_worldMapPreviewRange: 1024
m_worldMapPreviewTerrainHeight: 1024
m_terrainScenes: []
m_deactivateRuntimePlayer: 0
m_deactivateRuntimeLighting: 0
m_deactivateRuntimeAudio: 0
m_deactivateRuntimeWeather: 0
m_deactivateRuntimeWater: 0
m_deactivateRuntimeScreenShotter: 0
m_pos00X: -1.7976931348623157e+308
m_pos00Z: -1.7976931348623157e+308

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2e2428c1e05a106448aeb4c7e59def5d
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ed3d8d94bfd55054cbdd0c355641f6ee
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -26,7 +26,7 @@ MonoBehaviour:
m_unloadTerrainScenes: 0
m_floatingPointFix: 0
m_targeSizePreset: 3
m_creationWorkflow: 2
m_creationWorkflow: 1
m_defaultStampSpawnSettings: {fileID: 11400000, guid: 0e1586b8863570048942e9493106d5e8, type: 2}
m_defaultBiomeMaskSettings: {fileID: 0}
m_spawnSimulateComputeShader: {fileID: 7200000, guid: ab31bdb5aecea3744951fb814c0d268b, type: 3}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,391 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!29 &1
OcclusionCullingSettings:
m_ObjectHideFlags: 0
serializedVersion: 2
m_OcclusionBakeSettings:
smallestOccluder: 5
smallestHole: 0.25
backfaceThreshold: 100
m_SceneGUID: 00000000000000000000000000000000
m_OcclusionCullingData: {fileID: 0}
--- !u!104 &2
RenderSettings:
m_ObjectHideFlags: 0
serializedVersion: 10
m_Fog: 0
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
m_FogMode: 3
m_FogDensity: 0.01
m_LinearFogStart: 0
m_LinearFogEnd: 300
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
m_AmbientIntensity: 1
m_AmbientMode: 0
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
m_HaloStrength: 0.5
m_FlareStrength: 1
m_FlareFadeSpeed: 3
m_HaloTexture: {fileID: 0}
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
m_DefaultReflectionMode: 0
m_DefaultReflectionResolution: 128
m_ReflectionBounces: 1
m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0}
m_UseRadianceAmbientProbe: 0
--- !u!157 &3
LightmapSettings:
m_ObjectHideFlags: 0
serializedVersion: 13
m_BakeOnSceneLoad: 0
m_GISettings:
serializedVersion: 2
m_BounceScale: 1
m_IndirectOutputScale: 1
m_AlbedoBoost: 1
m_EnvironmentLightingMode: 0
m_EnableBakedLightmaps: 1
m_EnableRealtimeLightmaps: 0
m_LightmapEditorSettings:
serializedVersion: 12
m_Resolution: 2
m_BakeResolution: 40
m_AtlasSize: 1024
m_AO: 0
m_AOMaxDistance: 1
m_CompAOExponent: 1
m_CompAOExponentDirect: 0
m_ExtractAmbientOcclusion: 0
m_Padding: 2
m_LightmapParameters: {fileID: 0}
m_LightmapsBakeMode: 1
m_TextureCompression: 1
m_ReflectionCompression: 2
m_MixedBakeMode: 2
m_BakeBackend: 1
m_PVRSampling: 1
m_PVRDirectSampleCount: 32
m_PVRSampleCount: 512
m_PVRBounces: 2
m_PVREnvironmentSampleCount: 256
m_PVREnvironmentReferencePointCount: 2048
m_PVRFilteringMode: 1
m_PVRDenoiserTypeDirect: 1
m_PVRDenoiserTypeIndirect: 1
m_PVRDenoiserTypeAO: 1
m_PVRFilterTypeDirect: 0
m_PVRFilterTypeIndirect: 0
m_PVRFilterTypeAO: 0
m_PVREnvironmentMIS: 1
m_PVRCulling: 1
m_PVRFilteringGaussRadiusDirect: 1
m_PVRFilteringGaussRadiusIndirect: 5
m_PVRFilteringGaussRadiusAO: 2
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
m_PVRFilteringAtrousPositionSigmaIndirect: 2
m_PVRFilteringAtrousPositionSigmaAO: 1
m_ExportTrainingData: 0
m_TrainingDataDestination: TrainingData
m_LightProbeSampleCountMultiplier: 4
m_LightingDataAsset: {fileID: 20201, guid: 0000000000000000f000000000000000, type: 0}
m_LightingSettings: {fileID: 0}
--- !u!196 &4
NavMeshSettings:
serializedVersion: 2
m_ObjectHideFlags: 0
m_BuildSettings:
serializedVersion: 3
agentTypeID: 0
agentRadius: 0.5
agentHeight: 2
agentSlope: 45
agentClimb: 0.4
ledgeDropHeight: 0
maxJumpAcrossDistance: 0
minRegionArea: 2
manualCellSize: 0
cellSize: 0.16666667
manualTileSize: 0
tileSize: 256
buildHeightMesh: 0
maxJobWorkers: 0
preserveTilesOutsideBounds: 0
debug:
m_Flags: 0
m_NavMeshData: {fileID: 0}
--- !u!1 &203844586
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 203844589}
- component: {fileID: 203844588}
- component: {fileID: 203844587}
m_Layer: 0
m_Name: Directional Light
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &203844587
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 203844586}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3}
m_Name:
m_EditorClassIdentifier:
m_UsePipelineSettings: 1
m_AdditionalLightsShadowResolutionTier: 2
m_CustomShadowLayers: 0
m_LightCookieSize: {x: 1, y: 1}
m_LightCookieOffset: {x: 0, y: 0}
m_SoftShadowQuality: 0
m_RenderingLayersMask:
serializedVersion: 0
m_Bits: 1
m_ShadowRenderingLayersMask:
serializedVersion: 0
m_Bits: 1
m_Version: 4
m_LightLayerMask: 1
m_ShadowLayerMask: 1
m_RenderingLayers: 1
m_ShadowRenderingLayers: 1
--- !u!108 &203844588
Light:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 203844586}
m_Enabled: 1
serializedVersion: 11
m_Type: 1
m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1}
m_Intensity: 1
m_Range: 10
m_SpotAngle: 30
m_InnerSpotAngle: 21.80208
m_CookieSize: 10
m_Shadows:
m_Type: 2
m_Resolution: -1
m_CustomResolution: -1
m_Strength: 1
m_Bias: 0.05
m_NormalBias: 0.4
m_NearPlane: 0.2
m_CullingMatrixOverride:
e00: 1
e01: 0
e02: 0
e03: 0
e10: 0
e11: 1
e12: 0
e13: 0
e20: 0
e21: 0
e22: 1
e23: 0
e30: 0
e31: 0
e32: 0
e33: 1
m_UseCullingMatrixOverride: 0
m_Cookie: {fileID: 0}
m_DrawHalo: 0
m_Flare: {fileID: 0}
m_RenderMode: 0
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_RenderingLayerMask: 1
m_Lightmapping: 4
m_LightShadowCasterMode: 0
m_AreaSize: {x: 1, y: 1}
m_BounceIntensity: 1
m_ColorTemperature: 6570
m_UseColorTemperature: 0
m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
m_UseBoundingSphereOverride: 0
m_UseViewFrustumForShadowCasterCull: 1
m_ForceVisible: 0
m_ShadowRadius: 0
m_ShadowAngle: 0
m_LightUnit: 1
m_LuxAtDistance: 1
m_EnableSpotReflector: 1
--- !u!4 &203844589
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 203844586}
serializedVersion: 2
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
m_LocalPosition: {x: 0, y: 3, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
--- !u!1 &961739749
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 961739753}
- component: {fileID: 961739752}
- component: {fileID: 961739751}
- component: {fileID: 961739750}
m_Layer: 0
m_Name: Main Camera
m_TagString: MainCamera
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &961739750
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 961739749}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3}
m_Name:
m_EditorClassIdentifier:
m_RenderShadows: 1
m_RequiresDepthTextureOption: 2
m_RequiresOpaqueTextureOption: 2
m_CameraType: 0
m_Cameras: []
m_RendererIndex: -1
m_VolumeLayerMask:
serializedVersion: 2
m_Bits: 1
m_VolumeTrigger: {fileID: 0}
m_VolumeFrameworkUpdateModeOption: 2
m_RenderPostProcessing: 0
m_Antialiasing: 0
m_AntialiasingQuality: 2
m_StopNaN: 0
m_Dithering: 0
m_ClearDepth: 1
m_AllowXRRendering: 1
m_AllowHDROutput: 1
m_UseScreenCoordOverride: 0
m_ScreenSizeOverride: {x: 0, y: 0, z: 0, w: 0}
m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0}
m_RequiresDepthTexture: 0
m_RequiresColorTexture: 0
m_TaaSettings:
m_Quality: 3
m_FrameInfluence: 0.1
m_JitterScale: 1
m_MipBias: 0
m_VarianceClampScale: 0.9
m_ContrastAdaptiveSharpening: 0
m_Version: 2
--- !u!81 &961739751
AudioListener:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 961739749}
m_Enabled: 1
--- !u!20 &961739752
Camera:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 961739749}
m_Enabled: 1
serializedVersion: 2
m_ClearFlags: 1
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
m_projectionMatrixMode: 1
m_GateFitMode: 2
m_FOVAxisMode: 0
m_Iso: 200
m_ShutterSpeed: 0.005
m_Aperture: 16
m_FocusDistance: 10
m_FocalLength: 50
m_BladeCount: 5
m_Curvature: {x: 2, y: 11}
m_BarrelClipping: 0.25
m_Anamorphism: 0
m_SensorSize: {x: 36, y: 24}
m_LensShift: {x: 0, y: 0}
m_NormalizedViewPortRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
near clip plane: 0.3
far clip plane: 1000
field of view: 60
orthographic: 0
orthographic size: 5
m_Depth: -1
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_RenderingPath: -1
m_TargetTexture: {fileID: 0}
m_TargetDisplay: 0
m_TargetEye: 3
m_HDR: 1
m_AllowMSAA: 1
m_AllowDynamicResolution: 0
m_ForceIntoRT: 0
m_OcclusionCulling: 1
m_StereoConvergence: 10
m_StereoSeparation: 0.022
--- !u!4 &961739753
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 961739749}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 1, z: -10}
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!1660057539 &9223372036854775807
SceneRoots:
m_ObjectHideFlags: 0
m_Roots:
- {fileID: 961739753}
- {fileID: 203844589}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 57e93090c20de5540b06fbb7d4da5d3b
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,130 @@
using UnityEngine;
[DisallowMultipleComponent]
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(CapsuleCollider))]
public class BobberBuoyancyStable : MonoBehaviour
{
[Header("Water")]
public float waterLevelY = 0f;
[Tooltip("必须至少浸入这么深才开始产生浮力(防止还没入水就被顶)")]
public float enterWaterDepth = 0.003f; // 3mm按你的尺度改
[Tooltip("在这个深度范围内做平滑过渡(越大越软)")]
public float smoothDepth = 0.02f;
[Header("Buoyancy Spring")]
public float buoyancySpring = 30f;
public float buoyancyDamping = 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;
public Vector3 extraCenterOfMassOffset = new Vector3(0f, -0.01f, 0f);
[Header("Righting")]
public float rightingTorque = 1.5f;
public float rightingDamping = 0.5f;
Rigidbody rb;
CapsuleCollider cap;
float airLinearDamping;
float airAngularDamping;
void Awake()
{
rb = GetComponent<Rigidbody>();
cap = GetComponent<CapsuleCollider>();
rb.useGravity = true;
airLinearDamping = rb.linearDamping;
airAngularDamping = rb.angularDamping;
ApplyCenterOfMass();
rb.maxAngularVelocity = 50f;
}
void FixedUpdate()
{
ApplyCenterOfMass();
Bounds b = cap.bounds;
float bottomY = b.min.y;
float topY = b.max.y;
// 用“底部点”判定是否真正入水(必须超过阈值)
float bottomSubmersion = waterLevelY - bottomY; // >0 表示底部在水下
if (bottomSubmersion <= enterWaterDepth)
{
// 认为未入水:不施加浮力,恢复空气阻尼
rb.linearDamping = airLinearDamping;
rb.angularDamping = airAngularDamping;
return;
}
// 进入水中:阻尼随浸入增强
// 这里用一个0~1的平滑权重避免刚入水就“猛顶”
float w = Smooth01((bottomSubmersion - enterWaterDepth) / Mathf.Max(1e-4f, smoothDepth));
rb.linearDamping = airLinearDamping + extraLinearDampingInWater * w;
rb.angularDamping = airAngularDamping + extraAngularDampingInWater * w;
// 垂直速度(用刚体自身速度就够稳定)
float vY = rb.linearVelocity.y;
// 弹簧+阻尼浮力(仅向上)
float forceY = buoyancySpring * bottomSubmersion - buoyancyDamping * vY;
if (forceY < 0f) forceY = 0f;
// 平滑权重:刚入水时逐渐接管
forceY *= w;
// 限制最大上浮加速度(可选)
if (maxUpAcceleration > 0f)
{
float maxForce = rb.mass * maxUpAcceleration;
if (forceY > maxForce) forceY = maxForce;
}
// 浮力作用点:必须放在水面下(否则会出现奇怪力矩)
float buoyY = Mathf.Min(waterLevelY - 0.001f, topY); // 强制在水面下1mm
buoyY = Mathf.Max(buoyY, bottomY); // 不低于底部
Vector3 buoyPoint = new Vector3(b.center.x, buoyY, b.center.z);
rb.AddForceAtPosition(Vector3.up * forceY, buoyPoint, ForceMode.Force);
// 归正扭矩(只在水里生效)
Vector3 up = transform.up;
Vector3 axis = Vector3.Cross(up, Vector3.up);
float mag = axis.magnitude;
if (mag > 1e-4f)
{
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);
// smoothstep
return t * t * (3f - 2f * t);
}
}

View File

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

View File

@@ -1,126 +1,68 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine;
using WaveHarmonic.Crest;
namespace NBF
{
/// <summary>
/// 小型浮漂/小物体:基于 Crest 的浮力(自动从多个 CapsuleCollider 计算尺寸与探针)
/// - 自动收集自身与子物体上的 CapsuleCollider可多个
/// - 计算整体长轴、长度、最大直径、底部高度
/// - 自动设置 Crest 查询尺度 _ObjectWidth与直径同量级
/// - 自动生成 5 个探针:底部四周 + 底部中心(更稳)
/// - 浮力:弹簧(k) + 阻尼(c)ForceMode.Force质量参与
/// </summary>
public sealed class BobberFloating : MonoBehaviour
{
[Header("Crest")]
public WaterRenderer _water;
[SerializeField] Rigidbody _RigidBody;
[Tooltip("要瞄准哪一层水的碰撞层。")]
[SerializeField] CollisionLayer _Layer = CollisionLayer.AfterAnimatedWaves;
[Tooltip("要瞄准哪一层水的碰撞层。")] [SerializeField]
CollisionLayer _Layer = CollisionLayer.AfterAnimatedWaves;
// -----------------------------
// 自动从 CapsuleCollider 计算
// -----------------------------
[Header("Auto from CapsuleColliders")]
[Tooltip("自动扫描自己与子物体的 CapsuleCollider并计算尺寸/探针/采样尺度。")]
[SerializeField] bool _AutoFromCapsules = true;
[Header("浮力")]
[Header("力强度")]
[Tooltip("对于探测器而言,大致为 100 比 1 的质量与力的比例,以使质心保持在表面附近。对于“对齐法线”,默认值适用于具有默认刚体的默认球体。")]
[SerializeField]
float _BuoyancyForceStrength = 10f;
[Tooltip("仅使用 enabled 的 CapsuleCollider。")]
[SerializeField] bool _OnlyEnabledCapsules = true;
[Header("扭矩强度")] [Tooltip("使船体方向与水的法线方向一致时所施加扭矩的大小。")] [SerializeField]
float _BuoyancyTorqueStrength = 8f;
[Tooltip("运行时也会每隔一定时间重建(应对浮漂缩放/更换碰撞体。0 表示仅 Start/OnValidate 时重建。")]
[SerializeField] float _RuntimeRebuildInterval = 0f;
[Header("最大力矩")] [Tooltip("将浮力值固定在此数值上。\n\n适用于处理完全浸没的物体。")] [SerializeField]
float _MaximumBuoyancyForce = 100f;
[Tooltip("给估算出来的直径乘一个系数,用于更稳的 Crest 查询与阻尼。通常 1.0~2.0。")]
[Range(0.5f, 3f)]
[SerializeField] float _WidthMultiplier = 1.2f;
[Header("高度偏移")] [Tooltip("从变换中心到船体底部的高度偏移(如果存在)。\n\n默认值适用于默认球体。该值无需精确测量从中心到底部的距离。")] [SerializeField]
float _CenterToBottomOffset = -1f;
[Tooltip("探针半径比例(相对估算半径)。越大越“撑开”,越抗翻滚,但也更容易被浪抬。建议 0.6~0.9")]
[Range(0.1f, 1.2f)]
[SerializeField] float _ProbeRadiusRatio = 0.75f;
[Tooltip("顺着波浪 “冲浪” 的近似流体动力学效果。")] [Range(0, 1)] [SerializeField]
float _AccelerateDownhill;
[Tooltip("探针在底部之上的抬高(米)。用于避免探针刚好在最底点导致抖动。建议:半径的 5%~20%。")]
[SerializeField] float _ProbeLiftMeters = 0.0f;
[Tooltip("探针权重分配:四周总权重(其余给中心)。建议 0.6~0.9")]
[Range(0f, 1f)]
[SerializeField] float _RingWeight = 0.75f;
[Header("拖拽")] [Tooltip("在水中时使用拖拽功能。\n将此属性添加到刚体所声明的拖拽力上。")] [SerializeField]
Vector3 _Drag = new(2f, 3f, 1f);
// -----------------------------
// 物理参数(吃水可控)
// -----------------------------
[Header("Buoyancy (controllable submergence)")]
[Tooltip("目标吃水比例:以“直径”为尺度。\n例0.30 表示目标吃水深度约 = 直径*0.30。\n对小浮漂非常好调想更浮露更多-> 降低;想更沉 -> 提高。")]
[Range(0.05f, 1.2f)]
[SerializeField] float _TargetSubmergenceRatio = 0.30f;
[Tooltip("在水中会产生旋转阻力。\n\n将此阻力添加到刚体上已声明的旋转阻力值之上。")] [SerializeField]
float _AngularDrag = 0.2f;
[Tooltip("浮力阻尼比0~2 常用)。越大越不抖,但会显得黏。推荐 0.7~1.2")]
[Range(0f, 2f)]
[SerializeField] float _DampingRatio = 0.95f;
[Tooltip("施加拉力的位置的垂直偏移量。")] [SerializeField]
float _ForceHeightOffset;
[Tooltip("最大总浮力N保护。若为 Infinity 则不限制。")]
[SerializeField] float _MaximumBuoyancyForce = 50f;
[Tooltip("吃水偏移(米)。用于校准 pivot/碰撞体中心误差。\n>0 更容易浮起(等效更浅),<0 更沉。")]
[SerializeField] float _SubmergenceOffset = 0f;
[Header("波响应")] [Tooltip("用于物理计算的物体宽度。\n\n此值越大波响应的滤波效果和平滑程度就越高。如果无法对较大波长进行滤波则应增加 LOD 级别。")] [SerializeField]
float _ObjectWidth = 3f;
[Header("Water drag (relative to water surface velocity/flow)")]
[Tooltip("相对水的线性阻力N per m/s。小浮漂建议从 (0.2, 0.6, 0.2) 起。")]
[SerializeField] Vector3 _Drag = new(0.2f, 0.6f, 0.2f);
[Tooltip("角阻力N*m per rad/s。小浮漂建议 0.01~0.05。")]
[SerializeField] float _AngularDrag = 0.03f;
[Header("Optional downhill acceleration")]
[Range(0, 1)]
[SerializeField] float _AccelerateDownhill = 0f;
[Header("Force application height offset")]
[SerializeField] float _ForceHeightOffset = 0f;
// -----------------------------
// 自动生成的“几何结果”
// -----------------------------
[Header("Computed (read-only)")]
[SerializeField] float _ComputedLength = 0.1f;
[SerializeField] float _ComputedDiameter = 0.04f;
[SerializeField] Vector3 _ComputedAxisLocal = Vector3.up; // 刚体局部坐标中的长轴(单位向量)
[SerializeField] float _ComputedBottomLocalY = -0.02f; // 刚体局部坐标中“整体最低点”的 y沿长轴方向投影不是y这里是按 rb local Y仅用于Debug显示
[Header("Wave response / query scale")]
[Tooltip("用于 Crest 查询的物体宽度(米)。会自动从 Capsule 直径推算。")]
[SerializeField] float _ObjectWidth = 0.04f;
// -----------------------------
// Debug
// -----------------------------
[Space(10)]
[SerializeField] DebugFields _Debug = new();
[Space(10)] [SerializeField] DebugFields _Debug = new();
[Serializable]
[System.Serializable]
sealed class DebugFields
{
[SerializeField] internal bool _DrawProbes = false;
[SerializeField] internal bool _DrawForces = false;
[SerializeField] internal bool _DrawQueries = false;
}
/// <summary>是否有任意探针入水</summary>
internal const string k_FixedUpdateMarker = "Crest.FloatingObject.FixedUpdate";
static Unity.Profiling.ProfilerMarker s_FixedUpdateMarker = new(k_FixedUpdateMarker);
/// <summary>
/// 这个物体的任何部分是否浸泡在水中?
/// </summary>
public bool InWater { get; private set; }
// 探针(刚体局部空间,相对 worldCenterOfMass
struct Probe
{
public Vector3 localOffsetFromCOM;
public float weight;
}
Probe[] _Probes = Array.Empty<Probe>();
readonly SampleFlowHelper _SampleFlowHelper = new();
Vector3[] _QueryPoints;
@@ -128,436 +70,152 @@ namespace NBF
Vector3[] _QueryResultVelocities;
Vector3[] _QueryResultNormal;
float _NextRebuildTime = 0f;
// internal FloatingObjectProbe[] _Probe = new FloatingObjectProbe[] { new() { _Weight = 1f } };
void Reset()
{
if (_RigidBody == null) TryGetComponent(out _RigidBody);
}
public FloatingObjectProbe[] Probe = new FloatingObjectProbe[] { new() { _Weight = 1f } };
void OnValidate()
{
_ObjectWidth = Mathf.Max(0.001f, _ObjectWidth);
_RuntimeRebuildInterval = Mathf.Max(0f, _RuntimeRebuildInterval);
_ProbeLiftMeters = Mathf.Max(0f, _ProbeLiftMeters);
if (!Application.isPlaying)
{
if (_AutoFromCapsules)
{
TryRebuildFromCapsules(editor: true);
}
AllocateQueryArrays();
}
}
void Start()
private void Start()
{
if (_RigidBody == null) TryGetComponent(out _RigidBody);
if (_AutoFromCapsules)
{
TryRebuildFromCapsules(editor: false);
}
AllocateQueryArrays();
var points = Probe;
// Advanced 还需要为中心增设一个位置。
var length = points.Length;
_QueryPoints = new Vector3[length];
_QueryResultDisplacements = new Vector3[length];
_QueryResultVelocities = new Vector3[length];
_QueryResultNormal = new Vector3[length];
}
void FixedUpdate()
private void FixedUpdate()
{
if (_water == null || _RigidBody == null) return;
s_FixedUpdateMarker.Begin(this);
if (_AutoFromCapsules && _RuntimeRebuildInterval > 0f && Time.time >= _NextRebuildTime)
{
_NextRebuildTime = Time.time + _RuntimeRebuildInterval;
TryRebuildFromCapsules(editor: false);
AllocateQueryArrays();
}
if (_Probes == null || _Probes.Length == 0) return;
// Crest provider
var points = Probe;
// 查询
var collisions = _water.AnimatedWavesLod.Provider;
int probeCount = _Probes.Length;
int centerIndex = probeCount; // 最后一个点用于中心采样(表面速度/法线)
Vector3 comWorld = _RigidBody.worldCenterOfMass;
// Build query points
for (int i = 0; i < probeCount; i++)
// 更新查询点。
for (var i = 0; i < points.Length; i++)
{
_QueryPoints[i] = comWorld + transform.TransformVector(_Probes[i].localOffsetFromCOM);
var point = points[i];
_QueryPoints[i] =
transform.TransformPoint(point._Position + new Vector3(0, _RigidBody.centerOfMass.y, 0));
}
_QueryPoints[centerIndex] = comWorld;
// Query waves
collisions.Query(
GetHashCode(),
_ObjectWidth,
_QueryPoints,
_QueryResultDisplacements,
_QueryResultNormal,
_QueryResultVelocities,
_Layer
);
// Surface velocity + flow
Vector3 surfaceVelocity = _QueryResultVelocities[centerIndex];
_QueryPoints[^1] = transform.position + new Vector3(0, _RigidBody.centerOfMass.y, 0);
collisions.Query(GetHashCode(), _ObjectWidth, _QueryPoints, _QueryResultDisplacements,
_QueryResultNormal, _QueryResultVelocities, _Layer);
//我们可以将表面速度过滤为最近两帧中的较小值。
//存在一种极端情况:当波长被开启 / 关闭时,会产生单帧速度尖峰——
//因为此时水面确实会发生极快的运动。
var surfaceVelocity = _QueryResultVelocities[^1];
_SampleFlowHelper.Sample(transform.position, out var surfaceFlow, minimumLength: _ObjectWidth);
surfaceVelocity += new Vector3(surfaceFlow.x, 0f, surfaceFlow.y);
// Compute buoyancy coefficients
float g = Mathf.Abs(Physics.gravity.y);
// 目标吃水深度(米):按直径比例
float targetSubmergence = Mathf.Max(0.0005f, _ComputedDiameter * _TargetSubmergenceRatio);
// 总弹簧系数k ≈ m*g / targetSubmergence
float kTotal = (_RigidBody.mass * g) / targetSubmergence;
// 总阻尼c = 2*sqrt(k*m)*dampingRatio
float cTotal = 2f * Mathf.Sqrt(Mathf.Max(0.0001f, kTotal * _RigidBody.mass)) * _DampingRatio;
InWater = false;
float totalBuoyMag = 0f;
// Apply distributed buoyancy
for (int i = 0; i < probeCount; i++)
surfaceVelocity += new Vector3(surfaceFlow.x, 0, surfaceFlow.y);
if (_Debug._DrawQueries)
{
Vector3 p = _QueryPoints[i];
float waterHeight = _QueryResultDisplacements[i].y + _water.SeaLevel;
// depth: positive means submerged
float depth = (waterHeight - p.y) + _SubmergenceOffset;
if (depth <= 0f) continue;
InWater = true;
float w = Mathf.Max(0.0001f, _Probes[i].weight);
// spring
float FiSpring = (kTotal * w) * depth;
// damping (vertical relative velocity)
Vector3 pointVel = _RigidBody.GetPointVelocity(p);
float vRel = Vector3.Dot(pointVel - surfaceVelocity, Vector3.up);
float FiDamp = -(cTotal * w) * vRel;
float Fi = FiSpring + FiDamp;
if (Fi < 0f) Fi = 0f;
totalBuoyMag += Fi;
Vector3 force = Fi * Vector3.up;
_RigidBody.AddForceAtPosition(force, p, ForceMode.Force);
if (_Debug._DrawForces)
{
Debug.DrawLine(p, p + force * 0.02f, Color.cyan);
}
Debug.DrawLine(transform.position + 5f * Vector3.up,
transform.position + 5f * Vector3.up + surfaceVelocity, new(1, 1, 1, 0.6f));
}
if (!InWater) return;
// Clamp buoyancy (simple safety)
if (_MaximumBuoyancyForce < Mathf.Infinity && totalBuoyMag > _MaximumBuoyancyForce)
var height = _QueryResultDisplacements[0].y + _water.SeaLevel;
var bottomDepth = height - transform.position.y - _CenterToBottomOffset;
var normal = _QueryResultNormal[0];
if (_Debug._DrawQueries)
{
float excess = totalBuoyMag - _MaximumBuoyancyForce;
_RigidBody.AddForce(-excess * Vector3.up, ForceMode.Force);
var surfPos = transform.position;
surfPos.y = height;
DebugUtility.DrawCross(Debug.DrawLine, surfPos, normal, 1f, Color.red);
}
// Optional downhill acceleration (usually 0 for bobber)
if (_AccelerateDownhill > 0f)
InWater = bottomDepth > 0f;
if (!InWater)
{
Vector3 normal = _QueryResultNormal[centerIndex];
_RigidBody.AddForce(_AccelerateDownhill * g * new Vector3(normal.x, 0f, normal.z), ForceMode.Force);
s_FixedUpdateMarker.End();
return;
}
// Angular drag
if (_AngularDrag > 0f)
var buoyancy = _BuoyancyForceStrength * bottomDepth * bottomDepth * bottomDepth *
-Physics.gravity.normalized;
if (_MaximumBuoyancyForce < Mathf.Infinity)
{
_RigidBody.AddTorque(-_AngularDrag * _RigidBody.angularVelocity, ForceMode.Force);
buoyancy = Vector3.ClampMagnitude(buoyancy, _MaximumBuoyancyForce);
}
// Linear drag relative to water
_RigidBody.AddForce(buoyancy, ForceMode.Acceleration);
// // 在水面上滑行的近似流体动力学
// if (_AccelerateDownhill > 0f)
// {
// _RigidBody.AddForce(_AccelerateDownhill * -Physics.gravity.y * new Vector3(normal.x, 0f, normal.z),
// ForceMode.Acceleration);
// }
//
//
// // 朝向
// // 与水面垂直。默认使用一个垂直方向,但也可以使用单独的垂直方向。
// // 根据船的长度与宽度的比例。这会根据船只的不同而产生不同的旋转效果。
// // dimensions.
// {
// var normalLatitudinal = normal;
//
// if (_Debug._DrawQueries)
// Debug.DrawLine(transform.position, transform.position + 5f * normalLatitudinal, Color.green);
//
// var torqueWidth = Vector3.Cross(transform.up, normalLatitudinal);
// _RigidBody.AddTorque(torqueWidth * _BuoyancyTorqueStrength, ForceMode.Acceleration);
// _RigidBody.AddTorque(-_AngularDrag * _RigidBody.angularVelocity);
// }
//
//
// 相对于水进行拖拽操作
if (_Drag != Vector3.zero)
{
Vector3 velocityRelativeToWater = _RigidBody.linearVelocity - surfaceVelocity;
Vector3 forcePosition = _RigidBody.worldCenterOfMass + _ForceHeightOffset * Vector3.up;
var velocityRelativeToWater = _RigidBody.linearVelocity - surfaceVelocity;
var forcePosition = _RigidBody.worldCenterOfMass + _ForceHeightOffset * Vector3.up;
_RigidBody.AddForceAtPosition(
_Drag.x * Vector3.Dot(transform.right, -velocityRelativeToWater) * transform.right,
forcePosition,
ForceMode.Force);
_RigidBody.AddForceAtPosition(
_Drag.y * Vector3.Dot(Vector3.up, -velocityRelativeToWater) * Vector3.up,
forcePosition,
ForceMode.Force);
_Drag.x * Vector3.Dot(transform.right, -velocityRelativeToWater) * transform.right, forcePosition,
ForceMode.Acceleration);
_RigidBody.AddForceAtPosition(_Drag.y * Vector3.Dot(Vector3.up, -velocityRelativeToWater) * Vector3.up,
forcePosition, ForceMode.Acceleration);
_RigidBody.AddForceAtPosition(
_Drag.z * Vector3.Dot(transform.forward, -velocityRelativeToWater) * transform.forward,
forcePosition,
ForceMode.Force);
forcePosition, ForceMode.Acceleration);
}
if (_Debug._DrawProbes)
{
for (int i = 0; i < probeCount; i++)
{
Debug.DrawLine(_QueryPoints[i], _QueryPoints[i] + Vector3.up * 0.05f, Color.yellow);
}
}
s_FixedUpdateMarker.End();
}
}
static class DebugUtility
{
public delegate void DrawLine(Vector3 position, Vector3 up, Color color, float duration);
public static void DrawCross(DrawLine draw, Vector3 position, float r, Color color, float duration = 0f)
{
draw(position - Vector3.up * r, position + Vector3.up * r, color, duration);
draw(position - Vector3.right * r, position + Vector3.right * r, color, duration);
draw(position - Vector3.forward * r, position + Vector3.forward * r, color, duration);
}
void AllocateQueryArrays()
public static void DrawCross(DrawLine draw, Vector3 position, Vector3 up, float r, Color color,
float duration = 0f)
{
int probeCount = _Probes?.Length ?? 0;
int n = probeCount + 1; // +1 center sample
if (n <= 1) n = 2;
if (_QueryPoints != null && _QueryPoints.Length == n) return;
_QueryPoints = new Vector3[n];
_QueryResultDisplacements = new Vector3[n];
_QueryResultVelocities = new Vector3[n];
_QueryResultNormal = new Vector3[n];
}
/// <summary>
/// 公开按钮:手动重建(你也可以在 Inspector 右键脚本 -> Reset然后 Play
/// </summary>
[ContextMenu("Rebuild From CapsuleColliders")]
public void RebuildFromCapsules()
{
TryRebuildFromCapsules(editor: Application.isEditor && !Application.isPlaying);
AllocateQueryArrays();
}
bool TryRebuildFromCapsules(bool editor)
{
var capsules = GetComponentsInChildren<CapsuleCollider>(includeInactive: true);
if (capsules == null || capsules.Length == 0)
{
// 没胶囊就不改
return false;
}
// 收集点(在刚体局部空间)
List<Vector3> ptsLocal = new List<Vector3>(capsules.Length * 8);
// 同时估算“长轴”:取所有胶囊在 rb local 的端点云,计算 AABB 最大跨度轴
// 这比“看某个 Capsule.direction”更鲁棒因为你说可能多个、分布复杂
foreach (var cap in capsules)
{
if (cap == null) continue;
if (_OnlyEnabledCapsules && !cap.enabled) continue;
AddCapsuleSamplePointsInRbLocal(cap, ptsLocal);
}
if (ptsLocal.Count < 2) return false;
// AABB in rb local
Vector3 min = ptsLocal[0];
Vector3 max = ptsLocal[0];
for (int i = 1; i < ptsLocal.Count; i++)
{
min = Vector3.Min(min, ptsLocal[i]);
max = Vector3.Max(max, ptsLocal[i]);
}
Vector3 size = max - min;
// 估算长轴:取跨度最大的轴
// 注意:这是 rb local 坐标的轴x/y/z足够用于“尺寸”和“探针布局”
int axis = 0;
float spanX = size.x;
float spanY = size.y;
float spanZ = size.z;
if (spanY >= spanX && spanY >= spanZ) axis = 1;
else if (spanZ >= spanX && spanZ >= spanY) axis = 2;
else axis = 0;
Vector3 axisLocal = axis == 0 ? Vector3.right : axis == 1 ? Vector3.up : Vector3.forward;
// length along that axis
float length = axis == 0 ? spanX : axis == 1 ? spanY : spanZ;
// diameter取另外两轴的最大跨度更保守
float dia;
if (axis == 0) dia = Mathf.Max(spanY, spanZ);
else if (axis == 1) dia = Mathf.Max(spanX, spanZ);
else dia = Mathf.Max(spanX, spanY);
dia = Mathf.Max(0.001f, dia);
_ComputedLength = length;
_ComputedDiameter = dia;
_ComputedAxisLocal = axisLocal;
// Crest 查询尺度:直径同量级 * multiplier且给下限
_ObjectWidth = Mathf.Max(0.002f, _ComputedDiameter * _WidthMultiplier);
// 估算“底部位置”(用于探针 y
// 我们按“长轴”方向找最小投影的点作为底部
float minProj = float.PositiveInfinity;
Vector3 bottom = ptsLocal[0];
for (int i = 0; i < ptsLocal.Count; i++)
{
float proj = Vector3.Dot(ptsLocal[i], axisLocal);
if (proj < minProj)
{
minProj = proj;
bottom = ptsLocal[i];
}
}
// 这个值只是给你在 Inspector 看一眼,不参与计算
_ComputedBottomLocalY = bottom.y;
// 生成探针:以“底部附近”一圈 + 中心
BuildDefaultProbes(axisLocal, bottom, _ComputedDiameter);
if (!editor)
{
// 运行时也确保 query arrays 充足
AllocateQueryArrays();
}
return true;
}
void BuildDefaultProbes(Vector3 axisLocal, Vector3 bottomLocal, float diameter)
{
// 以刚体局部空间为准,探针 offset 是“相对 COM 的局部偏移”
// 我们把探针放在 “底部投影点附近”,并沿“横向平面”撑开
float radius = diameter * 0.5f;
float ringR = radius * _ProbeRadiusRatio;
// 底部沿长轴方向略抬高,避免探针刚好在最底点导致 jitter
// 这里用“长轴”方向抬高,而不是简单的 localY
float lift = _ProbeLiftMeters;
if (lift <= 0f)
{
// 默认:半径的 10%
lift = radius * 0.10f;
}
Vector3 basePoint = bottomLocal + axisLocal * lift;
// 需要在“横向平面”找两条正交方向(在 rb local 坐标中)
// 任意取一个与 axisLocal 不平行的向量,构造正交基
Vector3 t1 = Vector3.Cross(axisLocal, Vector3.up);
if (t1.sqrMagnitude < 1e-6f) t1 = Vector3.Cross(axisLocal, Vector3.right);
t1.Normalize();
Vector3 t2 = Vector3.Cross(axisLocal, t1).normalized;
// 权重分配
float ringTotal = Mathf.Clamp01(_RingWeight);
float centerW = 1f - ringTotal;
float eachRingW = ringTotal / 4f;
// 探针点rb local最后转成 “相对 COM 的 local offset”
// 注意:我们使用 worldCenterOfMass 作为基准,所以这里要把 basePointrb local转换成 offset-from-COM
Vector3 comLocal = transform.InverseTransformPoint(_RigidBody.worldCenterOfMass);
var probes = new Probe[5];
// 四周
probes[0] = new Probe { localOffsetFromCOM = (basePoint + t1 * ringR) - comLocal, weight = eachRingW };
probes[1] = new Probe { localOffsetFromCOM = (basePoint - t1 * ringR) - comLocal, weight = eachRingW };
probes[2] = new Probe { localOffsetFromCOM = (basePoint + t2 * ringR) - comLocal, weight = eachRingW };
probes[3] = new Probe { localOffsetFromCOM = (basePoint - t2 * ringR) - comLocal, weight = eachRingW };
// 底部中心
probes[4] = new Probe { localOffsetFromCOM = basePoint - comLocal, weight = Mathf.Max(0.0001f, centerW) };
_Probes = probes;
}
void AddCapsuleSamplePointsInRbLocal(CapsuleCollider cap, List<Vector3> outPtsRbLocal)
{
// 计算胶囊端点(世界) + 若干“半径点”,再转换到 rb local
// 胶囊在 cap.transform localcenter, direction(0=x 1=y 2=z), height, radius
Transform ct = cap.transform;
Vector3 centerW = ct.TransformPoint(cap.center);
// direction axis in world
Vector3 axisW =
cap.direction == 0 ? ct.right :
cap.direction == 1 ? ct.up :
ct.forward;
axisW.Normalize();
// 处理缩放radius 取垂直于轴的最大缩放分量(保守)
Vector3 s = ct.lossyScale;
float sx = Mathf.Abs(s.x);
float sy = Mathf.Abs(s.y);
float sz = Mathf.Abs(s.z);
float radiusScale;
float heightScale;
if (cap.direction == 0)
{
heightScale = sx;
radiusScale = Mathf.Max(sy, sz);
}
else if (cap.direction == 1)
{
heightScale = sy;
radiusScale = Mathf.Max(sx, sz);
}
else
{
heightScale = sz;
radiusScale = Mathf.Max(sx, sy);
}
float r = Mathf.Max(0.0005f, cap.radius * radiusScale);
float h = Mathf.Max(0.001f, cap.height * heightScale);
// 圆柱部分半长(端点到端点的距离为 h-2r
float half = Mathf.Max(0f, (h * 0.5f) - r);
Vector3 p0W = centerW + axisW * half;
Vector3 p1W = centerW - axisW * half;
// 在世界空间构造两个与 axisW 正交的方向
Vector3 n1 = Vector3.Cross(axisW, Vector3.up);
if (n1.sqrMagnitude < 1e-6f) n1 = Vector3.Cross(axisW, Vector3.right);
n1.Normalize();
Vector3 n2 = Vector3.Cross(axisW, n1).normalized;
// 采样点:两个端点 + 端点的四周半径点 + 中心四周半径点(足够稳定估算整体 AABB
AddWorldPoint(p0W, outPtsRbLocal);
AddWorldPoint(p1W, outPtsRbLocal);
AddWorldPoint(p0W + n1 * r, outPtsRbLocal);
AddWorldPoint(p0W - n1 * r, outPtsRbLocal);
AddWorldPoint(p0W + n2 * r, outPtsRbLocal);
AddWorldPoint(p0W - n2 * r, outPtsRbLocal);
AddWorldPoint(p1W + n1 * r, outPtsRbLocal);
AddWorldPoint(p1W - n1 * r, outPtsRbLocal);
AddWorldPoint(p1W + n2 * r, outPtsRbLocal);
AddWorldPoint(p1W - n2 * r, outPtsRbLocal);
AddWorldPoint(centerW + n1 * r, outPtsRbLocal);
AddWorldPoint(centerW - n1 * r, outPtsRbLocal);
AddWorldPoint(centerW + n2 * r, outPtsRbLocal);
AddWorldPoint(centerW - n2 * r, outPtsRbLocal);
}
void AddWorldPoint(Vector3 world, List<Vector3> outPtsRbLocal)
{
// rb local即本脚本 transform 的 local
// 注意:这里假设脚本挂在 Rigidbody 所在物体上(通常是这样)
outPtsRbLocal.Add(transform.InverseTransformPoint(world));
up.Normalize();
var right = Vector3.Normalize(Vector3.Cross(up, Vector3.forward));
var forward = Vector3.Cross(up, right);
draw(position - up * r, position + up * r, color, duration);
draw(position - right * r, position + right * r, color, duration);
draw(position - forward * r, position + forward * r, color, duration);
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,317 @@
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

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

View File

@@ -0,0 +1,105 @@
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

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

View File

@@ -52,6 +52,7 @@
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AObject_002Ecs_002Fl_003AC_0021_003FUsers_003Fbob_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F021f30a9a92b48ce98ae6b39956dd76a1df600_003F35_003F4c34802c_003FObject_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AObject_002Ecs_002Fl_003AC_0021_003FUsers_003Fbob_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F314938d17f3848e8ac683e11b27f62ee46ae00_003F0d_003F47ff7f51_003FObject_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AObject_002Ecs_002Fl_003AC_0021_003FUsers_003FFIREBAT_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb4c8c45fec274213bfac03ee0e9a3d621f5a00_003F87_003F6890e286_003FObject_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AObject_002Ecs_002Fl_003AC_0021_003FUsers_003FFIREBAT_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ff27f375161264267910e5cbe7a0862ec1f7600_003Fdf_003F621ab50f_003FObject_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APhysicMaterial_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F21f01dcabaec42e5804351a26927d8a329c00_003F62_003F56756319_003FPhysicMaterial_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APhysics_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F03ef825315384b1cab81c4b53eb03d922ac00_003Fc6_003F12ad2f00_003FPhysics_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APreviewSceneStage_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F1acad9deef3549c4b9617dfa169c66599f7e00_003Fa0_003F28490672_003FPreviewSceneStage_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>

View File

@@ -9,35 +9,35 @@ EditorUserSettings:
value: 18134705175a055722080a3115371d4a0d55006876786860616b0471b8b07a68ffab74f9ee2a3a30300cea1a11320d0beb1a0c25f7060f494b4cc80018eb09361fc211cb1f862d19c51d19dcc413d6ade0d8ddfcddf9f4d9d29195fcfde6ebeae6f0a9c9afa6f8c5b89ff7a1aacececac4eba4d7c9d28bda
flags: 0
RecentlyUsedSceneGuid-0:
value: 5a09065056055c0e595d0a20447b0b4415164e2b297c75682f7c1866b0b3656c
flags: 0
RecentlyUsedSceneGuid-1:
value: 0750065f5d57580c5c0b5d7741275e44464e1d297b7e77342e784a64b3e13539
flags: 0
RecentlyUsedSceneGuid-2:
value: 01085257040c5f0e0c0d5f27457b0f444e4e192c7b2d7e6428794d67e3b36668
flags: 0
RecentlyUsedSceneGuid-3:
RecentlyUsedSceneGuid-1:
value: 52530c5601535f020f565a2043770d1612154d2f747975692c7b4e66b5e3303b
flags: 0
RecentlyUsedSceneGuid-4:
RecentlyUsedSceneGuid-2:
value: 5302035e5c530f0b5c0c557416270d44134e4d28787c76332f7e1f6bb1b76169
flags: 0
RecentlyUsedSceneGuid-5:
value: 0508070250545c58585e0924437b5d444f4e4b7f7d7a71627f794c64b2e5633a
flags: 0
RecentlyUsedSceneGuid-6:
RecentlyUsedSceneGuid-3:
value: 5050570401015d0a545d087047710e44154e1c2e7f787368782c4e60e1e1636b
flags: 0
RecentlyUsedSceneGuid-7:
RecentlyUsedSceneGuid-4:
value: 520004535d5751085c595a7047730e4440161e7d787022342f2d486bb4b6626a
flags: 0
RecentlyUsedSceneGuid-8:
RecentlyUsedSceneGuid-5:
value: 5a035755520650595b5b5f2345740e4447154a73742d70632b7d4b65e4e66d69
flags: 0
RecentlyUsedSceneGuid-9:
RecentlyUsedSceneGuid-6:
value: 0508070250545c58585e0924437b5d444f4e4b7f7d7a71627f794c64b2e5633a
flags: 0
RecentlyUsedSceneGuid-7:
value: 0054045155060d5a5c575f7045270d44474f4e7c7f7924637e2a1832b1b5636d
flags: 0
RecentlyUsedSceneGuid-8:
value: 5409030052070d0d095a5c7412745e444216417c2e7a23642b7e1832bab9363e
flags: 0
RecentlyUsedSceneGuid-9:
value: 5606515f5605500b0e5c5c2615760a444615487c2a2a2467297d1932b7e4673a
flags: 0
UnityEditor.ShaderGraph.Blackboard:
value: 18135939215a0a5004000b0e15254b524c030a3f2964643d120d1230e9e93a3fd6e826abbd2e2d293c4ead313b08042de6030a0afa240c0d020be94c4ba75e435d8715fa32c70d15d11612dacc11fee5d3c5d1fe9ab1bf968e93e2ffcbc3e7e2f0b3ffe0e8b0be9afeffa9ffff8e85dd8390e2969e8899daa7
flags: 0