6 Commits
dev_new ... dev

Author SHA1 Message Date
caa260b53b 修改线求解器 2026-04-13 20:55:07 +08:00
6e918bb1a6 取消注释 2026-04-12 23:40:14 +08:00
de0b0558d6 修改提交 2026-04-12 22:42:06 +08:00
c96255aaa5 修改线渲染逻辑 2026-04-12 21:45:36 +08:00
b83dfd47b1 修改鱼线相关逻辑 2026-04-12 21:17:29 +08:00
8fbb21a66c 修改鱼线节点逻辑 2026-04-12 18:37:24 +08:00
29 changed files with 2074 additions and 874 deletions

View File

@@ -18553,21 +18553,6 @@ MonoBehaviour:
- {fileID: 102900000, guid: 60ac19fbbe20cec48add96b79332c113, type: 3}
FilterEnum: 0
Filter: '*'
- Path: Assets/ResRaw/Prefabs/Line/fishing line float set 1.prefab
Address: Plyaer/fishing line float set 1
Type: GameObject
Bundle: main/plyaer.bundle
Tags:
Group:
Name: Plyaer
Enable: 1
BundleMode: 0
AddressMode: 2
Tags:
Collectors:
- {fileID: 102900000, guid: aa3f5467c0c153642ac320466aee0ec1, type: 3}
FilterEnum: 0
Filter: '*'
- Path: Assets/ResRaw/Prefabs/Line/fishing line float set.prefab
Address: Plyaer/fishing line float set
Type: GameObject
@@ -18598,6 +18583,21 @@ MonoBehaviour:
- {fileID: 102900000, guid: aa3f5467c0c153642ac320466aee0ec1, type: 3}
FilterEnum: 0
Filter: '*'
- Path: Assets/ResRaw/Prefabs/Line/FishingLine1.prefab
Address: Plyaer/FishingLine1
Type: GameObject
Bundle: main/plyaer.bundle
Tags:
Group:
Name: Plyaer
Enable: 1
BundleMode: 0
AddressMode: 2
Tags:
Collectors:
- {fileID: 102900000, guid: aa3f5467c0c153642ac320466aee0ec1, type: 3}
FilterEnum: 0
Filter: '*'
- Path: Assets/ResRaw/Prefabs/Line/FishingRopeLong.asset
Address: Plyaer/FishingRopeLong
Type: Missing

View File

@@ -0,0 +1,589 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &2696931885206049402
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6213026895670800501}
- component: {fileID: 7362979975150531515}
- component: {fileID: 5572865435543895569}
m_Layer: 15
m_Name: Start
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &6213026895670800501
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2696931885206049402}
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: 98221710317492190}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!54 &7362979975150531515
Rigidbody:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2696931885206049402}
serializedVersion: 5
m_Mass: 1
m_LinearDamping: 1
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!114 &5572865435543895569
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2696931885206049402}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 610df5569209e4b4997cb2dbf3b94cdc, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::NBF.FishingLineNode
nodeType: 0
body: {fileID: 7362979975150531515}
interaction: {fileID: 0}
segmentLengthToNext: 0.5
fixedVirtualNodesToNext: 2
runtimeVirtualNode: 0
runtimeChainIndex: -1
drawDebugGizmo: 1
debugGizmoRadius: 0.03
logicalNodeColor: {r: 0.2, g: 0.9, b: 0.2, a: 1}
virtualNodeColor: {r: 1, g: 0.6, b: 0.1, a: 1}
features: []
motionFeatures: []
activeMotionFeature: {fileID: 0}
--- !u!1 &5077741257619886775
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 7385546574667729905}
- component: {fileID: 5707703654405666688}
- component: {fileID: 3979683508768218053}
- component: {fileID: 250386986656750139}
- component: {fileID: 6225447558892123241}
m_Layer: 15
m_Name: End
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &7385546574667729905
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5077741257619886775}
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: 98221710317492190}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!54 &5707703654405666688
Rigidbody:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5077741257619886775}
serializedVersion: 5
m_Mass: 0.01
m_LinearDamping: 1
m_AngularDamping: 0.2
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!114 &3979683508768218053
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5077741257619886775}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 610df5569209e4b4997cb2dbf3b94cdc, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::NBF.FishingLineNode
nodeType: 3
body: {fileID: 5707703654405666688}
interaction: {fileID: 0}
segmentLengthToNext: 0.5
fixedVirtualNodesToNext: 2
runtimeVirtualNode: 0
runtimeChainIndex: -1
drawDebugGizmo: 1
debugGizmoRadius: 0.03
logicalNodeColor: {r: 0.2, g: 0.9, b: 0.2, a: 1}
virtualNodeColor: {r: 1, g: 0.6, b: 0.1, a: 1}
features: []
motionFeatures: []
activeMotionFeature: {fileID: 0}
--- !u!65 &250386986656750139
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5077741257619886775}
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.02, y: 0.02, z: 0.02}
m_Center: {x: 0, y: 0, z: 0}
--- !u!114 &6225447558892123241
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5077741257619886775}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1de1bec90e454664a860c5248170ff95, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::NBF.JointPinchController
moveSpeed: 5
snapDistance: 0.1
--- !u!1 &5252216124238432432
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 3116177874895914436}
- component: {fileID: 1033618195002336566}
- component: {fileID: 4152162740525283091}
- component: {fileID: 2513762410452133691}
- component: {fileID: 3049258369283796050}
m_Layer: 15
m_Name: Bobber
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &3116177874895914436
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5252216124238432432}
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: 98221710317492190}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!54 &1033618195002336566
Rigidbody:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5252216124238432432}
serializedVersion: 5
m_Mass: 1
m_LinearDamping: 1
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: 0
m_ImplicitTensor: 0
m_UseGravity: 1
m_IsKinematic: 0
m_Interpolate: 0
m_Constraints: 0
m_CollisionDetection: 0
--- !u!114 &4152162740525283091
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5252216124238432432}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 610df5569209e4b4997cb2dbf3b94cdc, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::NBF.FishingLineNode
nodeType: 1
body: {fileID: 1033618195002336566}
interaction: {fileID: 0}
segmentLengthToNext: 0.5
fixedVirtualNodesToNext: 2
runtimeVirtualNode: 0
runtimeChainIndex: -1
drawDebugGizmo: 1
debugGizmoRadius: 0.03
logicalNodeColor: {r: 0.2, g: 0.9, b: 0.2, a: 1}
virtualNodeColor: {r: 1, g: 0.6, b: 0.1, a: 1}
features: []
motionFeatures: []
activeMotionFeature: {fileID: 0}
--- !u!114 &2513762410452133691
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5252216124238432432}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ca4d5d54d89446b0a10b7ce521fd7d9e, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::NBF.FishingBobberFeature
priorityOffset: 0
waterLevel: 0
enterWaterDepth: 0.002
exitWaterDepth: -0.01
floatHeight: 0.08
bottomOffsetLocalY: 0
ySmoothTime: 0.08
maxYSpeed: 2
yDeadZone: 0.0005
neutralBottomWeight: 1
currentBottomWeight: 1
baseDraftDepth: 0.02
draftDepthPerWeight: 0.01
minDraftDepth: 0.005
maxDraftDepth: 0.08
draftSmoothTime: 0.18
biteSmoothTime: 0.03
tapAmplitude: 0.008
tapDuration: 0.18
slowSinkAmplitude: 0.025
slowSinkDuration: 1.2
liftAmplitude: 0.015
liftDuration: 1.2
blackDriftAmplitude: 0.06
blackDriftDuration: 0.8
enableDebugInput: 1
stopBiteKey: 114
tapKey: 116
slowSinkKey: 103
liftKey: 104
blackDriftKey: 98
lyingWeightThreshold: 0.4
tiltedWeightThreshold: 0.8
uprightWeightThreshold: 1.2
lyingAngle: 88
tiltedAngle: 42
uprightAngle: 0
tiltAxis: 0
invertTiltDirection: 0
rotationLerpSpeed: 8
waterAngularDamping: 999
--- !u!65 &3049258369283796050
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5252216124238432432}
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.02, y: 0.02, z: 0.02}
m_Center: {x: 0, y: 0, z: 0}
--- !u!1 &5438655829551842420
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 98221710317492190}
- component: {fileID: 913314649585263376}
- component: {fileID: 6821754774284524478}
- component: {fileID: 1999186176030474616}
m_Layer: 0
m_Name: FishingLine1
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &98221710317492190
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5438655829551842420}
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:
- {fileID: 6213026895670800501}
- {fileID: 3116177874895914436}
- {fileID: 7385546574667729905}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &913314649585263376
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5438655829551842420}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: dcd0fd8d96f994444b2d8663af6b915d, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::NBF.FishingLineSolver
ConfigId: 0
LineType: 0
PinchController: {fileID: 6225447558892123241}
anchorTransform: {fileID: 0}
logicalNodes:
- {fileID: 5572865435543895569}
- {fileID: 4152162740525283091}
- {fileID: 3979683508768218053}
lineRenderer: {fileID: 1999186176030474616}
firstSegmentLength: 1.2
firstSegmentStep: 0.1
jointSolverIterations: 12
jointProjectionDistance: 0.02
jointProjectionAngle: 1
lengthLimitTolerance: 0.01
breakStretchThreshold: 0.05
breakLimitDuration: 0.15
autoBuildOnStart: 1
--- !u!120 &6821754774284524478
LineRenderer:
serializedVersion: 3
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5438655829551842420}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 0
m_LightProbeUsage: 0
m_ReflectionProbeUsage: 0
m_RayTracingMode: 0
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: 0}
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_MaskInteraction: 0
m_Positions:
- {x: 0, y: 0, z: 0}
- {x: 0, y: 1, z: 0}
m_Parameters:
serializedVersion: 3
widthMultiplier: 1
widthCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0.001
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
colorGradient:
serializedVersion: 2
key0: {r: 1, g: 1, b: 1, a: 1}
key1: {r: 1, g: 1, b: 1, a: 1}
key2: {r: 0, g: 0, b: 0, a: 0}
key3: {r: 0, g: 0, b: 0, a: 0}
key4: {r: 0, g: 0, b: 0, a: 0}
key5: {r: 0, g: 0, b: 0, a: 0}
key6: {r: 0, g: 0, b: 0, a: 0}
key7: {r: 0, g: 0, b: 0, a: 0}
ctime0: 0
ctime1: 65535
ctime2: 0
ctime3: 0
ctime4: 0
ctime5: 0
ctime6: 0
ctime7: 0
atime0: 0
atime1: 65535
atime2: 0
atime3: 0
atime4: 0
atime5: 0
atime6: 0
atime7: 0
m_Mode: 0
m_ColorSpace: -1
m_NumColorKeys: 2
m_NumAlphaKeys: 2
numCornerVertices: 0
numCapVertices: 0
alignment: 0
textureMode: 0
textureScale: {x: 1, y: 1}
shadowBias: 0.5
generateLightingData: 0
m_UseWorldSpace: 1
m_Loop: 0
m_ApplyActiveColorSpace: 1
--- !u!114 &1999186176030474616
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5438655829551842420}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 827786ffede4e7b4781c522e8a4ba9d0, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::NBF.FishingLineRenderer
solver: {fileID: 913314649585263376}
lineRenderer: {fileID: 6821754774284524478}
solverIterations: 8
damping: 0.98
gravityScale: 1
simulationStep: 0.0166667
maxDeltaTime: 0.0333333
constrainToWaterSurface: 1
waterSurfaceTransform: {fileID: 0}
waterSurfaceHeight: 0
ignoreHeadNodeCount: 1
ignoreTailNodeCount: 1
waterSurfaceFollowSpeed: 12
maxSubStepsPerFrame: 2
sleepVelocityThreshold: 0.001
sleepDistanceThreshold: 0.002
stableFramesBeforeSleep: 4
wakeDistanceThreshold: 0.001
smoothCorners: 1
minCornerAngle: 12
maxCornerSmoothDistance: 0.03
cornerSmoothSubdivisions: 3
drawDebugSamples: 0
debugLogicalSampleColor: {r: 0, g: 1, b: 1, a: 1}
debugVirtualSampleColor: {r: 1, g: 0.55, b: 0.15, a: 1}
debugLogicalSampleRadius: 0.018
debugVirtualSampleRadius: 0.012

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 488209094f0c45a41aa6801dd86e6768
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,427 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &1035052809208993
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4026445325167852}
- component: {fileID: 54679398375713381}
- component: {fileID: 153611279189314279}
- component: {fileID: 135844594273256032}
- component: {fileID: 7176287465780574680}
m_Layer: 15
m_Name: Lure
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &4026445325167852
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1035052809208993}
serializedVersion: 2
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: -1.5, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 4283454774123242}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!54 &54679398375713381
Rigidbody:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1035052809208993}
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: 2
--- !u!153 &153611279189314279
ConfigurableJoint:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1035052809208993}
serializedVersion: 4
m_ConnectedBody: {fileID: 54298866000586118}
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!135 &135844594273256032
SphereCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1035052809208993}
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!114 &7176287465780574680
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1035052809208993}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ee9704c2e1594f4cab270bfd4ca2210b, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::NBF.FishingLineNode
nodeType: 3
body: {fileID: 54679398375713381}
--- !u!1 &1387836627839849
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4283454774123242}
- component: {fileID: 7859179875146676465}
m_Layer: 0
m_Name: fishing line float set 1
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &4283454774123242
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1387836627839849}
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:
- {fileID: 4530253318796540}
- {fileID: 4026445325167852}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &7859179875146676465
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1387836627839849}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fd39a2e024a0477c9ad5698d80d9a63a, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::NBF.FishingLineSolver
logicalNodes:
- {fileID: 8342656697567645068}
- {fileID: 7176287465780574680}
--- !u!1 &1858052053854210
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4530253318796540}
- component: {fileID: 54298866000586118}
- component: {fileID: 153691655494134957}
- component: {fileID: 8342656697567645068}
m_Layer: 15
m_Name: Float
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &4530253318796540
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1858052053854210}
serializedVersion: 2
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: -1, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 4283454774123242}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!54 &54298866000586118
Rigidbody:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1858052053854210}
serializedVersion: 5
m_Mass: 0.1
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: 1
m_IsKinematic: 0
m_Interpolate: 0
m_Constraints: 0
m_CollisionDetection: 2
--- !u!153 &153691655494134957
ConfigurableJoint:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1858052053854210}
serializedVersion: 4
m_ConnectedBody: {fileID: 0}
m_ConnectedArticulationBody: {fileID: 0}
m_Anchor: {x: 0, y: -0.01, 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!114 &8342656697567645068
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1858052053854210}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ee9704c2e1594f4cab270bfd4ca2210b, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::NBF.FishingLineNode
nodeType: 1
body: {fileID: 54298866000586118}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 449040ff936513f4ba4b9715db2f4bf7
timeCreated: 1762387921
licenseType: Free
PrefabImporter:
externalObjects: {}
addedObjectFileIDs:
isPrefabVariant: 0
variantParentGUID: 00000000000000000000000000000000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -675,7 +675,7 @@ MonoBehaviour:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1858052053854210}
m_Enabled: 0
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2dedfafdc2d747d98c682cde3e28e513, type: 3}
m_Name:

View File

@@ -1,29 +1,29 @@
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(Rope))]
public class RopeFishLineEditor : Editor
{
private Rope _target;
void OnEnable()
{
_target = target as Rope;
// lookAtPoint = serializedObject.FindProperty("lookAtPoint");
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (GUILayout.Button("打印总长度"))
{
_target.DebugLength();
// Debug.Log($"总长度={_target.GetCurrentLength()} 目标长度={_target.GetTargetLength()} smoot={_target.GetLengthSmoothVel()} relLen={_target.GetLengthByPoints()} PolylineLength={_target.GetPhysicsPolylineLength()}");
}
// serializedObject.Update();
// EditorGUILayout.PropertyField(lookAtPoint);
// serializedObject.ApplyModifiedProperties();
}
}
// using UnityEditor;
// using UnityEngine;
//
// [CustomEditor(typeof(Rope))]
// public class RopeFishLineEditor : Editor
// {
// private Rope _target;
//
// void OnEnable()
// {
// _target = target as Rope;
// // lookAtPoint = serializedObject.FindProperty("lookAtPoint");
// }
//
// public override void OnInspectorGUI()
// {
// base.OnInspectorGUI();
//
//
// if (GUILayout.Button("打印总长度"))
// {
// _target.DebugLength();
// // Debug.Log($"总长度={_target.GetCurrentLength()} 目标长度={_target.GetTargetLength()} smoot={_target.GetLengthSmoothVel()} relLen={_target.GetLengthByPoints()} PolylineLength={_target.GetPhysicsPolylineLength()}");
// }
// // serializedObject.Update();
// // EditorGUILayout.PropertyField(lookAtPoint);
// // serializedObject.ApplyModifiedProperties();
// }
// }

View File

@@ -1,59 +1,59 @@
using System;
using UnityEngine;
namespace NBF
{
public class LureController : MonoBehaviour
{
[SerializeField] private Rigidbody rBody;
[SerializeField] private ConfigurableJoint joint;
public Rigidbody RBody => rBody;
public ConfigurableJoint Joint => joint;
private void Start()
{
RBody.detectCollisions = true;
RBody.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
RBody.interpolation = RigidbodyInterpolation.Interpolate;
}
public void SetJoint(Rigidbody rb)
{
joint.connectedBody = rb;
}
public void EnableCollision(bool enable)
{
if (rBody == null)
{
rBody = GetComponent<Rigidbody>();
}
// rBody.detectCollisions = enable;
}
public void SetKinematic(bool value)
{
rBody.isKinematic = value;
}
public void SetJointDistance(float limit)
{
joint.linearLimit = new SoftJointLimit
{
limit = limit
};
}
private void OnCollisionEnter(Collision other)
{
Debug.Log($"OnCollisionEnter:{other.gameObject.name}");
}
private void OnCollisionExit(Collision other)
{
Debug.Log($"OnCollisionExit:{other.gameObject.name}");
}
}
}
// using System;
// using UnityEngine;
//
// namespace NBF
// {
// public class LureController : MonoBehaviour
// {
// [SerializeField] private Rigidbody rBody;
// [SerializeField] private ConfigurableJoint joint;
// public Rigidbody RBody => rBody;
//
// public ConfigurableJoint Joint => joint;
//
// private void Start()
// {
// RBody.detectCollisions = true;
// RBody.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
// RBody.interpolation = RigidbodyInterpolation.Interpolate;
// }
//
// public void SetJoint(Rigidbody rb)
// {
// joint.connectedBody = rb;
// }
//
//
// public void EnableCollision(bool enable)
// {
// if (rBody == null)
// {
// rBody = GetComponent<Rigidbody>();
// }
//
// // rBody.detectCollisions = enable;
// }
//
// public void SetKinematic(bool value)
// {
// rBody.isKinematic = value;
// }
//
// public void SetJointDistance(float limit)
// {
// joint.linearLimit = new SoftJointLimit
// {
// limit = limit
// };
// }
//
// private void OnCollisionEnter(Collision other)
// {
// Debug.Log($"OnCollisionEnter:{other.gameObject.name}");
// }
// private void OnCollisionExit(Collision other)
// {
// Debug.Log($"OnCollisionExit:{other.gameObject.name}");
// }
// }
// }

View File

@@ -1,3 +1,8 @@
fileFormatVersion: 2
guid: 11c32e9f078948c880079b4903f7c8bd
timeCreated: 1776008844
guid: 6901448ac9466974791a863c357f6579
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,8 +1,3 @@
fileFormatVersion: 2
guid: 2ff164cb3132445289d22c7b3a0f4fab
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
timeCreated: 1775957622

View File

@@ -13,7 +13,9 @@ namespace NBF
public override bool IsSupportedNode(FishingLineNode node)
{
return node != null && node.Type != FishingLineNode.NodeType.Start;
return node != null
&& !node.IsRuntimeVirtualNode
&& node.Type != FishingLineNode.NodeType.Start;
}
public override bool CanControl()

View File

@@ -2,12 +2,18 @@
namespace NBF
{
public interface IWaterSurfaceProvider
{
float GetWaterHeight(Vector3 worldPos);
Vector3 GetWaterNormal(Vector3 worldPos);
}
public enum BobberControlMode
{
AirPhysics,
WaterPresentation,
}
public enum BobberBiteType
{
None,
@@ -30,102 +36,139 @@ namespace NBF
{
protected override int DefaultPriority => 100;
[Header("Water")] [Tooltip("没有水提供器时使用固定水位")]
[Header("Water")]
[Tooltip("没有水提供器时使用固定水位")]
public float fallbackWaterLevel;
[Tooltip("可选:挂实现了 IWaterSurfaceProvider 的组件")]
public MonoBehaviour waterProviderBehaviour;
[Header("Enter Water")] [Tooltip("底部进入水面多少米后切换为漂像控制")]
[Header("Enter Water")]
[Tooltip("底部进入水面多少米后切换为漂像控制")]
public float enterWaterDepth = 0.002f;
[Tooltip("离开水面多少米后回到空中物理。一般给负值做滞回")] public float exitWaterDepth = -0.01f;
[Tooltip("离开水面多少米后回到空中物理。一般给负值做滞回")]
public float exitWaterDepth = -0.01f;
[Header("Geometry")] [Tooltip("浮漂总高度(米)")]
[Header("Geometry")]
[Tooltip("浮漂总高度(米)")]
public float floatHeight = 0.08f;
[Tooltip("如果 Pivot 在浮漂底部,这里填 0如果 Pivot 在模型中心,就填底部相对 Pivot 的本地 Y")]
public float bottomOffsetLocalY;
[Header("Base Float")] [Tooltip("基础吃铅比例,决定静止时有多少在水下")] [Range(0.05f, 0.95f)]
[Header("Base Float")]
[Tooltip("基础吃铅比例,决定静止时有多少在水下")]
[Range(0.05f, 0.95f)]
public float baseSubmergeRatio = 0.28f;
[Tooltip("Y 轴平滑时间,越小响应越快")] public float ySmoothTime = 0.08f;
[Tooltip("Y 轴平滑时间,越小响应越快")]
public float ySmoothTime = 0.08f;
[Tooltip("最大竖直速度限制(用于 SmoothDamp")] public float maxYSpeed = 2f;
[Tooltip("最大竖直速度限制(用于 SmoothDamp")]
public float maxYSpeed = 2f;
[Tooltip("静止小死区,减少微抖")] public float yDeadZone = 0.0005f;
[Tooltip("静止小死区,减少微抖")]
public float yDeadZone = 0.0005f;
[Header("Surface Motion")] [Tooltip("是否启用轻微水面起伏")]
[Header("Surface Motion")]
[Tooltip("是否启用轻微水面起伏")]
public bool enableSurfaceBobbing = true;
[Tooltip("水面轻微起伏振幅(米)")] public float surfaceBobAmplitude = 0.0015f;
[Tooltip("水面轻微起伏振幅(米)")]
public float surfaceBobAmplitude = 0.0015f;
[Tooltip("水面轻微起伏频率")] public float surfaceBobFrequency = 1.2f;
[Tooltip("水面轻微起伏频率")]
public float surfaceBobFrequency = 1.2f;
[Header("XZ Motion")] [Tooltip("入水后是否锁定 XZ 到入水点附近")]
[Header("XZ Motion")]
[Tooltip("入水后是否锁定 XZ 到入水点附近")]
public bool lockXZAroundAnchor = true;
[Tooltip("XZ 跟随平滑时间")] public float xzSmoothTime = 0.15f;
[Tooltip("XZ 跟随平滑时间")]
public float xzSmoothTime = 0.15f;
[Tooltip("水流/拖拽带来的额外平面偏移最大值")] public float maxPlanarOffset = 0.15f;
[Tooltip("水流/拖拽带来的额外平面偏移最大值")]
public float maxPlanarOffset = 0.15f;
[Header("Sink By Weight / Tension")] [Tooltip("外部向下拉力映射为下沉量的系数。你可以把钩/铅/线组的等效向下拉力喂进来")]
[Header("Sink By Weight / Tension")]
[Tooltip("外部向下拉力映射为下沉量的系数。你可以把钩/铅/线组的等效向下拉力喂进来")]
public float downForceToSink = 0.0025f;
[Tooltip("向下拉力下沉的最大附加量")] public float maxExtraSink = 0.08f;
[Tooltip("向下拉力下沉的最大附加量")]
public float maxExtraSink = 0.08f;
[Header("Bottom Touch")] [Tooltip("触底时是否启用修正")]
[Header("Bottom Touch")]
[Tooltip("触底时是否启用修正")]
public bool enableBottomTouchAdjust = true;
[Tooltip("触底后减少的下沉量(例如铅坠到底,漂会回升一点)")] public float bottomTouchLift = 0.01f;
[Tooltip("触底后减少的下沉量(例如铅坠到底,漂会回升一点)")]
public float bottomTouchLift = 0.01f;
[Header("Posture Source")] [Tooltip("下方 Lure / 钩组 / 铅坠的刚体。姿态主要根据它和浮漂的相对位置判断")]
[Header("Posture Source")]
[Tooltip("下方 Lure / 钩组 / 铅坠的刚体。姿态主要根据它和浮漂的相对位置判断")]
public Rigidbody lureBody;
[Tooltip("用于归一化的参考长度。一般填:浮漂到 Lure 在“正常拉直”时的大致长度")]
public float referenceLength = 0.30f;
[Header("Posture Threshold")] [Tooltip("最小入水比例。不够时优先躺漂")]
[Header("Posture Threshold")]
[Tooltip("最小入水比例。不够时优先躺漂")]
public float minSubmergeToStand = 0.16f;
[Tooltip("垂直分量比低于该值时,优先躺漂")] public float verticalLieThreshold = 0.18f;
[Tooltip("垂直分量比低于该值时,优先躺漂")]
public float verticalLieThreshold = 0.18f;
[Tooltip("垂直分量比高于该值,且水平分量较小时,允许立漂")] public float verticalUprightThreshold = 0.75f;
[Tooltip("垂直分量比高于该值,且水平分量较小时,允许立漂")]
public float verticalUprightThreshold = 0.75f;
[Tooltip("水平分量比高于该值时,不允许完全立漂")] public float planarTiltThreshold = 0.30f;
[Tooltip("水平分量比高于该值时,不允许完全立漂")]
public float planarTiltThreshold = 0.30f;
[Tooltip("水平分量明显大于垂直分量时,优先躺漂")] public float planarDominanceMultiplier = 1.20f;
[Tooltip("水平分量明显大于垂直分量时,优先躺漂")]
public float planarDominanceMultiplier = 1.20f;
[Tooltip("姿态切换滞回")] public float postureHysteresis = 0.04f;
[Tooltip("姿态切换滞回")]
public float postureHysteresis = 0.04f;
[Header("Posture Stability")] [Tooltip("候选姿态需持续多久才真正切换")]
[Header("Posture Stability")]
[Tooltip("候选姿态需持续多久才真正切换")]
public float postureConfirmTime = 0.08f;
[Tooltip("姿态切换后的最短冷却时间,避免来回闪烁")] public float postureSwitchCooldown = 0.10f;
[Tooltip("姿态切换后的最短冷却时间,避免来回闪烁")]
public float postureSwitchCooldown = 0.10f;
[Header("Posture Rotation")] [Tooltip("倾斜状态角度")]
[Header("Posture Rotation")]
[Tooltip("倾斜状态角度")]
public float tiltedAngle = 38f;
[Tooltip("躺漂角度")] public float lyingAngle = 88f;
[Tooltip("躺漂角度")]
public float lyingAngle = 88f;
[Tooltip("立漂时允许的最大附加倾角")] public float uprightMaxTiltAngle = 8f;
[Tooltip("立漂时允许的最大附加倾角")]
public float uprightMaxTiltAngle = 8f;
[Tooltip("平面方向对立漂/斜漂附加倾角的影响强度")] public float planarTiltFactor = 120f;
[Tooltip("平面方向对立漂/斜漂附加倾角的影响强度")]
public float planarTiltFactor = 120f;
[Tooltip("平面方向死区,小于该值时保持上一帧方向")] public float planarDirectionDeadZone = 0.01f;
[Tooltip("平面方向死区,小于该值时保持上一帧方向")]
public float planarDirectionDeadZone = 0.01f;
[Tooltip("平面方向平滑速度")] public float planarDirectionLerpSpeed = 10f;
[Tooltip("平面方向平滑速度")]
public float planarDirectionLerpSpeed = 10f;
[Tooltip("姿态平滑速度")] public float rotationLerpSpeed = 8f;
[Tooltip("姿态平滑速度")]
public float rotationLerpSpeed = 8f;
[Header("Debug Input")] public bool debugResetKey = true;
[Header("Debug Input")]
public bool debugResetKey = true;
public bool debugTapKey = true;
public bool debugSlowSinkKey = true;
public bool debugLiftKey = true;
public bool debugBlackDriftKey = true;
[Header("Debug")] public bool drawDebug;
[Header("Debug")]
public bool drawDebug;
public bool UseTestPosture;
public BobberPosture TestPosture;
@@ -140,6 +183,7 @@ namespace NBF
public Vector2 ExternalPlanarOffset { get; set; }
private Rigidbody _rb;
private IWaterSurfaceProvider _waterProvider;
private BobberControlMode _mode = BobberControlMode.AirPhysics;
private BobberPosture _posture = BobberPosture.Lying;
@@ -277,6 +321,12 @@ namespace NBF
{
_rb = Node != null && Node.Body != null ? Node.Body : GetComponent<Rigidbody>();
}
if (_waterProvider == null && waterProviderBehaviour != null)
{
_waterProvider = waterProviderBehaviour as IWaterSurfaceProvider;
}
}
private void InitializeRuntimeState()
@@ -744,7 +794,6 @@ namespace NBF
var k = (t - 0.35f) / 0.65f;
targetOffset = -Mathf.Lerp(_biteAmplitude, 0f, k);
}
break;
case BobberBiteType.SlowSink:
@@ -778,6 +827,11 @@ namespace NBF
private float GetWaterHeight(Vector3 worldPos)
{
if (_waterProvider != null)
{
return _waterProvider.GetWaterHeight(worldPos);
}
return fallbackWaterLevel;
}
@@ -887,4 +941,4 @@ namespace NBF
}
#endif
}
}
}

View File

@@ -1,4 +1,6 @@
using UnityEngine;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace NBF
{
@@ -9,12 +11,40 @@ namespace NBF
Start,
Float,
Weight,
Tail
Tail,
Virtual,
}
private FishingLineSolver _solver;
[Header("Node")] [SerializeField] private NodeType nodeType = NodeType.Tail;
[SerializeField] private Rigidbody body;
[SerializeField] public Rigidbody body;
[SerializeField] private MonoBehaviour interaction;
[Header("Segment To Next Logical Node")] [Min(0f)] [SerializeField]
private float segmentLengthToNext = 0.5f;
[Min(0)] [SerializeField] private int fixedVirtualNodesToNext = 2;
[Header("Runtime")] [SerializeField] private bool runtimeVirtualNode;
[SerializeField] private int runtimeChainIndex = -1;
[Header("Debug")] [SerializeField] private bool drawDebugGizmo = true;
[Min(0.001f)] [SerializeField] private float debugGizmoRadius = 0.03f;
[SerializeField] private Color logicalNodeColor = new(0.2f, 0.9f, 0.2f, 1f);
[SerializeField] private Color virtualNodeColor = new(1f, 0.6f, 0.1f, 1f);
[SerializeField] private List<FishingLineNodeFeature> features = new();
[SerializeField] private List<FishingLineNodeMotionFeature> motionFeatures = new();
private bool featureCacheReady;
[SerializeField] private FishingLineNodeMotionFeature activeMotionFeature;
/// <summary>
/// 当前正在接管节点运动的组件。
/// </summary>
public FishingLineNodeMotionFeature ActiveMotionFeature => activeMotionFeature;
public NodeType Type
{
@@ -24,10 +54,252 @@ namespace NBF
public Rigidbody Body => body;
public MonoBehaviour Interaction => interaction;
public float SegmentLengthToNext
{
get => segmentLengthToNext;
set => segmentLengthToNext = Mathf.Max(0f, value);
}
public int FixedVirtualNodesToNext
{
get => fixedVirtualNodesToNext;
set => fixedVirtualNodesToNext = Mathf.Max(0, value);
}
public bool IsRuntimeVirtualNode => runtimeVirtualNode;
public int RuntimeChainIndex => runtimeChainIndex;
public Vector3 Position => transform.position;
public string GetDebugName()
{
return runtimeVirtualNode
? $"V[{runtimeChainIndex}]"
: $"{nodeType}[{runtimeChainIndex}]";
}
public string GetDebugSummary()
{
var bodySummary = body == null
? "NoBody"
: $"v:{body.linearVelocity.magnitude:F2}";
return $"{GetDebugName()} pos:{Position} next:{segmentLengthToNext:F2} {bodySummary}";
}
private void Reset()
{
TryGetComponent(out body);
}
private void Awake()
{
_solver = GetComponentInParent<FishingLineSolver>();
body = GetComponent<Rigidbody>();
EnsureFeatureCache();
}
private void Start()
{
BindFeatures(_solver);
}
private void FixedUpdate()
{
EnsureFeatureCache();
UpdateMotionControl(Time.fixedDeltaTime);
}
private void OnValidate()
{
if (body == null)
{
TryGetComponent(out body);
}
segmentLengthToNext = Mathf.Max(0f, segmentLengthToNext);
fixedVirtualNodesToNext = Mathf.Max(0, fixedVirtualNodesToNext);
}
public void SetRuntimeVirtual(bool isVirtual, int chainIndex)
{
runtimeVirtualNode = isVirtual;
runtimeChainIndex = chainIndex;
if (isVirtual)
{
nodeType = NodeType.Virtual;
}
}
public void SetVisualPosition(Vector3 position)
{
transform.position = position;
}
#region Feature
/// <summary>
/// 获取节点上的第一个指定类型功能组件。
/// </summary>
public T GetFeature<T>() where T : FishingLineNodeFeature
{
EnsureFeatureCache();
for (var i = 0; i < features.Count; i++)
{
if (features[i] is T result)
{
return result;
}
}
return null;
}
/// <summary>
/// 尝试获取节点上的指定类型功能组件。
/// </summary>
public bool TryGetFeature<T>(out T feature) where T : FishingLineNodeFeature
{
feature = GetFeature<T>();
return feature != null;
}
/// <summary>
/// 刷新并重新绑定当前节点上的功能组件。
/// </summary>
public void BindFeatures(FishingLineSolver solver)
{
EnsureFeatureCache();
foreach (var t in features)
{
t.Bind(this, solver);
}
ResolveMotionFeature(forceRefresh: true);
}
/// <summary>
/// 通知当前节点上的所有功能组件,鱼线已重建完成。
/// </summary>
public void NotifyLineBuilt()
{
EnsureFeatureCache();
foreach (var t in features)
{
t.OnLineBuilt();
}
ResolveMotionFeature(forceRefresh: true);
}
/// <summary>
/// 通知当前节点上的所有功能组件,鱼线已经达到断线条件。
/// </summary>
public void NotifyLineBreakRequested()
{
EnsureFeatureCache();
foreach (var t in features)
{
t.OnLineBreakRequested();
}
}
private void EnsureFeatureCache()
{
if (!featureCacheReady)
{
RefreshFeatures();
}
}
private void RefreshFeatures()
{
features.Clear();
motionFeatures.Clear();
GetComponents(features);
for (var i = 0; i < features.Count; i++)
{
if (features[i] is FishingLineNodeMotionFeature motionFeature)
{
motionFeatures.Add(motionFeature);
}
}
activeMotionFeature = null;
featureCacheReady = true;
}
private void UpdateMotionControl(float deltaTime)
{
var motionFeature = ResolveMotionFeature(forceRefresh: false);
if (motionFeature == null)
{
return;
}
motionFeature.TickMotion(deltaTime);
}
private FishingLineNodeMotionFeature ResolveMotionFeature(bool forceRefresh)
{
EnsureFeatureCache();
var bestMotionFeature = default(FishingLineNodeMotionFeature);
var bestPriority = int.MinValue;
foreach (var motionFeature in motionFeatures)
{
var r = !motionFeature.IsSupportedNode(this);
var n = !motionFeature.CanControl();
if (motionFeature == null || !motionFeature.IsSupportedNode(this) || !motionFeature.CanControl())
{
continue;
}
if (bestMotionFeature != null && motionFeature.Priority <= bestPriority)
{
continue;
}
bestMotionFeature = motionFeature;
bestPriority = motionFeature.Priority;
}
if (!forceRefresh && ReferenceEquals(activeMotionFeature, bestMotionFeature))
{
return activeMotionFeature;
}
if (activeMotionFeature != null && !ReferenceEquals(activeMotionFeature, bestMotionFeature))
{
activeMotionFeature.OnMotionDeactivated();
}
activeMotionFeature = bestMotionFeature;
if (activeMotionFeature != null)
{
activeMotionFeature.OnMotionActivated();
}
return activeMotionFeature;
}
#endregion
private void OnDrawGizmos()
{
if (!drawDebugGizmo)
{
return;
}
Gizmos.color = runtimeVirtualNode ? virtualNodeColor : logicalNodeColor;
Gizmos.DrawSphere(transform.position, debugGizmoRadius);
}
}
}

View File

@@ -1,3 +1,2 @@
fileFormatVersion: 2
guid: ee9704c2e1594f4cab270bfd4ca2210b
timeCreated: 1776008915
guid: 610df5569209e4b4997cb2dbf3b94cdc

View File

@@ -1,19 +1,630 @@
using UnityEngine;
using System.Collections.Generic;
using UnityEngine;
namespace NBF
{
[RequireComponent(typeof(LineRenderer))]
public class FishingLineRenderer : MonoBehaviour
{
[Header("References")] [SerializeField]
private FishingLineSolver solver;
[Header("References")]
[SerializeField] private FishingLineSolver solver;
[SerializeField] private LineRenderer lineRenderer;
[Header("Verlet")]
[Min(1)]
[SerializeField] private int solverIterations = 8;
[Range(0f, 1f)]
[SerializeField] private float damping = 0.98f;
[Min(0f)]
[SerializeField] private float gravityScale = 1f;
[Min(0.001f)]
[SerializeField] private float simulationStep = 0.0166667f;
[Min(0.001f)]
[SerializeField] private float maxDeltaTime = 0.0333333f;
[Header("Water Surface")]
[SerializeField] private bool constrainToWaterSurface = true;
[SerializeField] private Transform waterSurfaceTransform;
[SerializeField] private float waterSurfaceHeight;
[Min(0)]
[SerializeField] private int ignoreHeadNodeCount = 1;
[Min(0)]
[SerializeField] private int ignoreTailNodeCount = 1;
[Min(0f)]
[SerializeField] private float waterSurfaceFollowSpeed = 12f;
[Header("Stability")]
[Min(1)]
[SerializeField] private int maxSubStepsPerFrame = 2;
[Min(0f)]
[SerializeField] private float sleepVelocityThreshold = 0.001f;
[Min(0f)]
[SerializeField] private float sleepDistanceThreshold = 0.002f;
[Min(1)]
[SerializeField] private int stableFramesBeforeSleep = 4;
[Min(0f)]
[SerializeField] private float wakeDistanceThreshold = 0.001f;
[Header("Corner Smoothing")]
[SerializeField] private bool smoothCorners = true;
[Range(0f, 180f)]
[SerializeField] private float minCornerAngle = 12f;
[Min(0f)]
[SerializeField] private float maxCornerSmoothDistance = 0.03f;
[Min(1)]
[SerializeField] private int cornerSmoothSubdivisions = 3;
[Header("Debug")]
[SerializeField] private bool drawDebugSamples;
[SerializeField] private Color debugLogicalSampleColor = Color.cyan;
[SerializeField] private Color debugVirtualSampleColor = new(1f, 0.55f, 0.15f, 1f);
[Min(0.001f)]
[SerializeField] private float debugLogicalSampleRadius = 0.018f;
[Min(0.001f)]
[SerializeField] private float debugVirtualSampleRadius = 0.012f;
private readonly List<Vector3> positions = new();
private readonly List<Vector3> renderPositions = new();
private readonly List<Vector3> previousPositions = new();
private readonly List<long> sampledPointKeys = new();
private readonly List<Vector3> lastPinnedPointPositions = new();
private readonly List<float> lastRestLengths = new();
private bool[] pinnedFlags = System.Array.Empty<bool>();
private float accumulatedTime;
private bool isSleeping;
private int stableFrameCounter;
public int SampleCount => positions.Count;
public float CurrentRenderedLength
{
get
{
var total = 0f;
var source = renderPositions.Count > 0 ? renderPositions : positions;
for (var i = 0; i < source.Count - 1; i++)
{
total += Vector3.Distance(source[i], source[i + 1]);
}
return total;
}
}
private void Reset()
{
TryGetComponent(out lineRenderer);
if (solver == null)
{
TryGetComponent(out solver);
}
}
private void Awake()
{
lineRenderer = GetComponent<LineRenderer>();
solver = GetComponent<FishingLineSolver>();
if (lineRenderer == null)
{
TryGetComponent(out lineRenderer);
}
}
public void Render(FishingLineSolver sourceSolver, float deltaTime)
{
if (lineRenderer == null)
{
return;
}
solver = sourceSolver;
var points = solver.ChainPoints;
var restLengths = solver.RestLengths;
var pinnedIndices = solver.PinnedIndices;
if (points.Count == 0)
{
lineRenderer.positionCount = 0;
return;
}
var topologyChanged = EnsureBuffers(points, pinnedIndices);
if (topologyChanged || ShouldWake(points, restLengths))
{
WakeUp();
}
Simulate(points, restLengths, deltaTime);
ApplyToRenderer();
CacheFrameState(points, restLengths);
}
private bool EnsureBuffers(
IReadOnlyList<FishingLineSolver.ChainPoint> points,
IReadOnlyList<int> pinnedIndices)
{
var topologyChanged = sampledPointKeys.Count != points.Count;
if (!topologyChanged)
{
for (var i = 0; i < points.Count; i++)
{
if (sampledPointKeys[i] == points[i].Key)
{
continue;
}
topologyChanged = true;
break;
}
}
var previousPositionMap = new Dictionary<long, Vector3>(sampledPointKeys.Count);
var previousHistoryMap = new Dictionary<long, Vector3>(sampledPointKeys.Count);
for (var i = 0; i < sampledPointKeys.Count; i++)
{
previousPositionMap[sampledPointKeys[i]] = positions[i];
previousHistoryMap[sampledPointKeys[i]] = previousPositions[i];
}
positions.Clear();
previousPositions.Clear();
sampledPointKeys.Clear();
pinnedFlags = new bool[points.Count];
for (var i = 0; i < points.Count; i++)
{
var point = points[i];
sampledPointKeys.Add(point.Key);
if (previousPositionMap.TryGetValue(point.Key, out var preservedPosition))
{
positions.Add(preservedPosition);
previousPositions.Add(previousHistoryMap[point.Key]);
continue;
}
positions.Add(point.Position);
previousPositions.Add(point.Position);
}
for (var i = 0; i < pinnedIndices.Count; i++)
{
var pinnedIndex = pinnedIndices[i];
if (pinnedIndex >= 0 && pinnedIndex < pinnedFlags.Length)
{
pinnedFlags[pinnedIndex] = true;
}
}
return topologyChanged;
}
private void Simulate(
IReadOnlyList<FishingLineSolver.ChainPoint> points,
IReadOnlyList<float> restLengths,
float deltaTime)
{
if (isSleeping)
{
PinLogicalPoints(points);
return;
}
var clampedDelta = Mathf.Clamp(deltaTime, 0f, maxDeltaTime);
accumulatedTime = Mathf.Min(accumulatedTime + clampedDelta, simulationStep * maxSubStepsPerFrame);
var subStepCount = 0;
while (accumulatedTime >= simulationStep && subStepCount < maxSubStepsPerFrame)
{
SimulateStep(points, restLengths, simulationStep);
accumulatedTime -= simulationStep;
subStepCount++;
}
if (subStepCount == 0)
{
PinLogicalPoints(points);
ApplySleep();
}
EvaluateSleepState(restLengths);
}
private void SimulateStep(
IReadOnlyList<FishingLineSolver.ChainPoint> points,
IReadOnlyList<float> restLengths,
float stepDelta)
{
var gravity = Physics.gravity * gravityScale * stepDelta * stepDelta;
for (var i = 0; i < points.Count; i++)
{
if (pinnedFlags[i])
{
positions[i] = points[i].Position;
previousPositions[i] = points[i].Position;
continue;
}
var current = positions[i];
var velocity = (current - previousPositions[i]) * damping;
previousPositions[i] = current;
positions[i] = current + velocity + gravity;
}
SolveDistanceConstraints(points, restLengths);
ApplyWaterSurfaceConstraint(stepDelta);
SolveDistanceConstraints(points, restLengths);
// Keep a final pure distance solve so the render chain settles back to its rest-length budget
// without reintroducing the old forced-straightening behavior.
SolveDistanceConstraints(points, restLengths);
PinLogicalPoints(points);
ApplySleep();
}
private void SolveDistanceConstraints(
IReadOnlyList<FishingLineSolver.ChainPoint> points,
IReadOnlyList<float> restLengths)
{
for (var iteration = 0; iteration < solverIterations; iteration++)
{
PinLogicalPoints(points);
for (var segmentIndex = 0; segmentIndex < restLengths.Count; segmentIndex++)
{
SatisfyDistanceConstraint(segmentIndex, restLengths[segmentIndex]);
}
}
}
private void PinLogicalPoints(IReadOnlyList<FishingLineSolver.ChainPoint> points)
{
for (var i = 0; i < points.Count; i++)
{
if (!pinnedFlags[i])
{
continue;
}
positions[i] = points[i].Position;
previousPositions[i] = points[i].Position;
}
}
private void SatisfyDistanceConstraint(int segmentIndex, float restLength)
{
var pointA = positions[segmentIndex];
var pointB = positions[segmentIndex + 1];
var delta = pointB - pointA;
var distance = delta.magnitude;
if (distance <= 0.0001f)
{
return;
}
var correctionScale = (distance - restLength) / distance;
if (Mathf.Approximately(correctionScale, 0f))
{
return;
}
var pointAPinned = pinnedFlags[segmentIndex];
var pointBPinned = pinnedFlags[segmentIndex + 1];
if (pointAPinned && pointBPinned)
{
return;
}
if (pointAPinned)
{
positions[segmentIndex + 1] -= delta * correctionScale;
return;
}
if (pointBPinned)
{
positions[segmentIndex] += delta * correctionScale;
return;
}
var correction = delta * (correctionScale * 0.5f);
positions[segmentIndex] += correction;
positions[segmentIndex + 1] -= correction;
}
private void ApplyWaterSurfaceConstraint(float stepDelta)
{
if (!constrainToWaterSurface || positions.Count == 0)
{
return;
}
var surfaceHeight = waterSurfaceTransform != null ? waterSurfaceTransform.position.y : waterSurfaceHeight;
var startIndex = Mathf.Clamp(ignoreHeadNodeCount, 0, positions.Count);
var endExclusive = Mathf.Clamp(positions.Count - ignoreTailNodeCount, startIndex, positions.Count);
var followFactor = Mathf.Clamp01(waterSurfaceFollowSpeed * stepDelta);
for (var i = startIndex; i < endExclusive; i++)
{
if (pinnedFlags[i])
{
continue;
}
var current = positions[i];
if (current.y >= surfaceHeight)
{
continue;
}
var nextY = Mathf.Lerp(current.y, surfaceHeight, followFactor);
positions[i] = new Vector3(current.x, nextY, current.z);
var previous = previousPositions[i];
previousPositions[i] = new Vector3(
previous.x,
Mathf.Lerp(previous.y, nextY, followFactor),
previous.z);
}
}
private void ApplySleep()
{
for (var i = 0; i < positions.Count; i++)
{
if (pinnedFlags[i])
{
continue;
}
var velocityMagnitude = (positions[i] - previousPositions[i]).magnitude;
if (velocityMagnitude <= sleepVelocityThreshold)
{
previousPositions[i] = positions[i];
}
}
}
private void EvaluateSleepState(IReadOnlyList<float> restLengths)
{
var isStable = true;
for (var i = 0; i < positions.Count; i++)
{
if (pinnedFlags[i])
{
continue;
}
if ((positions[i] - previousPositions[i]).magnitude > sleepVelocityThreshold)
{
isStable = false;
break;
}
}
if (isStable)
{
for (var i = 0; i < restLengths.Count; i++)
{
var error = Mathf.Abs(Vector3.Distance(positions[i], positions[i + 1]) - restLengths[i]);
if (error > sleepDistanceThreshold)
{
isStable = false;
break;
}
}
}
if (!isStable)
{
stableFrameCounter = 0;
return;
}
stableFrameCounter++;
if (stableFrameCounter < stableFramesBeforeSleep)
{
return;
}
isSleeping = true;
accumulatedTime = 0f;
for (var i = 0; i < positions.Count; i++)
{
previousPositions[i] = positions[i];
}
}
private bool ShouldWake(
IReadOnlyList<FishingLineSolver.ChainPoint> points,
IReadOnlyList<float> restLengths)
{
if (!isSleeping)
{
return false;
}
if (lastPinnedPointPositions.Count != points.Count || lastRestLengths.Count != restLengths.Count)
{
return true;
}
for (var i = 0; i < points.Count; i++)
{
if (!pinnedFlags[i])
{
continue;
}
if (Vector3.Distance(points[i].Position, lastPinnedPointPositions[i]) > wakeDistanceThreshold)
{
return true;
}
}
for (var i = 0; i < restLengths.Count; i++)
{
if (Mathf.Abs(restLengths[i] - lastRestLengths[i]) > wakeDistanceThreshold)
{
return true;
}
}
return false;
}
private void CacheFrameState(
IReadOnlyList<FishingLineSolver.ChainPoint> points,
IReadOnlyList<float> restLengths)
{
lastPinnedPointPositions.Clear();
for (var i = 0; i < points.Count; i++)
{
lastPinnedPointPositions.Add(points[i].Position);
}
lastRestLengths.Clear();
for (var i = 0; i < restLengths.Count; i++)
{
lastRestLengths.Add(restLengths[i]);
}
}
private void WakeUp()
{
isSleeping = false;
stableFrameCounter = 0;
accumulatedTime = 0f;
}
private void ApplyToRenderer()
{
BuildRenderPositions();
lineRenderer.positionCount = renderPositions.Count;
for (var i = 0; i < renderPositions.Count; i++)
{
lineRenderer.SetPosition(i, renderPositions[i]);
}
}
private void BuildRenderPositions()
{
renderPositions.Clear();
if (positions.Count == 0)
{
return;
}
if (!smoothCorners || positions.Count < 3)
{
renderPositions.AddRange(positions);
return;
}
renderPositions.Add(positions[0]);
for (var i = 1; i < positions.Count - 1; i++)
{
var previous = positions[i - 1];
var current = positions[i];
var next = positions[i + 1];
if (!TryBuildSmoothedCorner(previous, current, next, out var entry, out var exit))
{
AddRenderPointIfDistinct(current);
continue;
}
AddRenderPointIfDistinct(entry);
for (var subdivision = 1; subdivision <= cornerSmoothSubdivisions; subdivision++)
{
var t = subdivision / (cornerSmoothSubdivisions + 1f);
var pointOnCurve = EvaluateQuadraticBezier(entry, current, exit, t);
AddRenderPointIfDistinct(pointOnCurve);
}
AddRenderPointIfDistinct(exit);
}
AddRenderPointIfDistinct(positions[^1]);
}
private bool TryBuildSmoothedCorner(
Vector3 previous,
Vector3 current,
Vector3 next,
out Vector3 entry,
out Vector3 exit)
{
entry = current;
exit = current;
var incoming = current - previous;
var outgoing = next - current;
var incomingLength = incoming.magnitude;
var outgoingLength = outgoing.magnitude;
if (incomingLength <= 0.0001f || outgoingLength <= 0.0001f)
{
return false;
}
var cornerAngle = Vector3.Angle(incoming, outgoing);
if (cornerAngle < minCornerAngle)
{
return false;
}
var trimDistance = Mathf.Min(
maxCornerSmoothDistance,
incomingLength * 0.5f,
outgoingLength * 0.5f);
if (trimDistance <= 0.0001f)
{
return false;
}
entry = current - (incoming / incomingLength) * trimDistance;
exit = current + (outgoing / outgoingLength) * trimDistance;
return true;
}
private void AddRenderPointIfDistinct(Vector3 point)
{
if (renderPositions.Count > 0 && Vector3.Distance(renderPositions[^1], point) <= 0.0001f)
{
return;
}
renderPositions.Add(point);
}
private static Vector3 EvaluateQuadraticBezier(Vector3 start, Vector3 control, Vector3 end, float t)
{
var oneMinusT = 1f - t;
return (oneMinusT * oneMinusT * start)
+ (2f * oneMinusT * t * control)
+ (t * t * end);
}
private void OnDrawGizmosSelected()
{
if (!drawDebugSamples || positions.Count == 0)
{
return;
}
for (var i = 0; i < positions.Count; i++)
{
var isLogicalPoint = solver != null
&& solver.ChainPoints != null
&& i < solver.ChainPoints.Count
&& solver.ChainPoints[i].IsLogical;
Gizmos.color = isLogicalPoint ? debugLogicalSampleColor : debugVirtualSampleColor;
Gizmos.DrawSphere(
positions[i],
isLogicalPoint ? debugLogicalSampleRadius : debugVirtualSampleRadius);
}
}
}
}
}

View File

@@ -1,3 +1,2 @@
fileFormatVersion: 2
guid: 14537bf263784fcd8aaf130eb3c51350
timeCreated: 1776084149
guid: 827786ffede4e7b4781c522e8a4ba9d0

View File

@@ -1,13 +1,30 @@
using System;
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
namespace NBF
{
public class FishingLineSolver : MonoBehaviour
public enum LineType
{
Hand,
HandDouble,
Spinning,
SpinningFloat,
}
public class FishingLineSolver : FGearBase
{
[SerializeField] public LineType LineType;
[Header("References")] [SerializeField]
private Transform anchorTransform;
[SerializeField] private FishingLineNode[] logicalNodes = Array.Empty<FishingLineNode>();
public JointPinchController PinchController;
#region LineNode
@@ -87,6 +104,11 @@ namespace NBF
}
#endregion
protected override void OnInit()
{
var tipRb = Rod.Asset.LineConnectorRigidbody;
anchorTransform = tipRb.transform;
}
}
}

View File

@@ -1,3 +1,2 @@
fileFormatVersion: 2
guid: fd39a2e024a0477c9ad5698d80d9a63a
timeCreated: 1776008869
guid: dcd0fd8d96f994444b2d8663af6b915d

View File

@@ -0,0 +1,103 @@
using UnityEngine;
namespace NBF
{
public class FishingLineTestController : MonoBehaviour
{
[Header("References")] [SerializeField]
private FishingLineSolver solver;
[Header("Length Test")] [Min(0f)] [SerializeField]
private float initialFirstSegmentLength = 1.2f;
[Min(0f)] [SerializeField] private float minFirstSegmentLength = 0.1f;
[Min(0f)] [SerializeField] private float maxFirstSegmentLength = 5f;
[Min(0f)] [SerializeField] private float lineAdjustSpeed = 1f;
[Header("Input")] [SerializeField] private KeyCode extendKey = KeyCode.UpArrow;
[SerializeField] private KeyCode retractKey = KeyCode.DownArrow;
private float targetFirstSegmentLength;
private void Reset()
{
if (solver == null)
{
solver = GetComponent<FishingLineSolver>();
}
}
private void Start()
{
if (solver == null)
{
return;
}
targetFirstSegmentLength =
Mathf.Clamp(initialFirstSegmentLength, minFirstSegmentLength, maxFirstSegmentLength);
solver.SetLenght(targetFirstSegmentLength);
solver.BuildLine();
solver.OnLineBreakRequested += OnLineBreakRequested;
}
private void OnLineBreakRequested(FishingLineSolver lineSolver)
{
Debug.LogError(
$"当前拉力达到极限,切线,极限时间={lineSolver.LimitStateTime} CurrentStretchLength={lineSolver.CurrentStretchLength} CurrentTensionRatio={lineSolver.CurrentTensionRatio}");
var endNode = lineSolver.GetEndNode();
if (endNode != null)
{
endNode.Body.isKinematic = false;
}
}
private void Update()
{
if (solver == null)
{
return;
}
var input = 0f;
if (Input.GetKey(extendKey))
{
input += 1f;
}
if (Input.GetKey(retractKey))
{
input -= 1f;
}
if (!Mathf.Approximately(input, 0f))
{
targetFirstSegmentLength += input * lineAdjustSpeed * Time.deltaTime;
targetFirstSegmentLength =
Mathf.Clamp(targetFirstSegmentLength, minFirstSegmentLength, maxFirstSegmentLength);
solver.SetLenght(targetFirstSegmentLength);
}
if (solver.CurrentBreakStretchPercent > 0)
{
// Debug.LogError(solver.CurrentBreakStretchPercent);
}
// if (solver.IsAtLimit)
// {
// if (solver.CurrentStretchLength > 0.04)
// Debug.LogError($"CurrentStretchLength={solver.CurrentStretchLength}");
// if (solver.CurrentStretchLength > 0.1 && solver.LimitStateTime > 2f)
// {
// Debug.LogError(
// $"当前拉力达到极限,切线,极限时间={solver.LimitStateTime} CurrentStretchLength={solver.CurrentStretchLength} CurrentTensionRatio={solver.CurrentTensionRatio}");
// var endNode = solver.GetEndNode();
// if (endNode != null)
// {
// endNode.Body.isKinematic = false;
// }
// }
// }
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 77af114bb80f3904a83cdeaacd5af508

View File

@@ -13,7 +13,8 @@ namespace NBF
public struct ThrowAnimationRequest
{
public LureController Lure;
// public LureController Lure;
public FishingLineNode EndNode;
public Vector3 ThrowOriginPosition;
public Vector3 StartPosition;
public Vector3 Forward;

View File

@@ -22,7 +22,7 @@ namespace NBF
private float _castElapsedTime;
private Vector3 _castStartPos;
private Vector3 _castTargetPos;
private LureController _castingLure;
private FishingLineNode _castingLure;
public bool IsPlaying => _castingLure != null;
@@ -40,18 +40,18 @@ namespace NBF
public void Play(ThrowAnimationRequest request)
{
if (request.Lure == null)
if (request.EndNode == null)
{
return;
}
Stop(snapToTarget: false);
_castingLure = request.Lure;
_castingLure = request.EndNode;
_chargedProgress = Mathf.Clamp01(request.ChargedProgress);
_castElapsedTime = 0f;
var lureBody = request.Lure.RBody;
var lureBody = request.EndNode.body;
_castStartPos = request.StartPosition;
Vector3 forward = GetHorizontalForward(request.Forward);
@@ -81,7 +81,7 @@ namespace NBF
return;
}
var lureBody = _castingLure.RBody;
var lureBody = _castingLure.body;
if (snapToTarget)
{
_castingLure.transform.position = _castTargetPos;

View File

@@ -61,7 +61,12 @@ namespace NBF
PlayerView.Unity.ModelAsset.PlayerAnimator.StartThrow = false;
var rod = GetRod();
if (rod == null || rod.Line == null || rod.Line.Lure == null)
if (rod == null || rod.Line == null )
{
return;
}
var endNode = rod.Line.GetEndNode();
if (endNode == null)
{
return;
}
@@ -70,9 +75,9 @@ namespace NBF
_throwAnimation.Player = Player;
_throwAnimation?.Play(new ThrowAnimationRequest
{
Lure = rod.Line.Lure,
EndNode = endNode,
ThrowOriginPosition = PlayerView.Unity.transform.position,
StartPosition = rod.Line.Lure.RBody.position,
StartPosition = endNode.body.position,
Forward = PlayerView.Unity.transform.forward,
ChargedProgress = ChargedProgress
});

View File

@@ -7,7 +7,9 @@ namespace NBF
protected override void OnInit()
{
// transform.position = Rod.lineHandler.LineConnector_1.transform.position;
SetParent(Rod.Line.Bobber.transform);
var node = Rod.Line.GetLogicalNode(FishingLineNode.NodeType.Float);
SetParent(node.transform);
transform.localPosition = Vector3.zero;
// var buoyancy = GetComponentInParent<CapsuleBuoyancyStable>();
// buoyancy.InitBobber();

View File

@@ -18,8 +18,10 @@ namespace NBF
// transform.rotation = Rod.lineHandler.LineConnector_2.transform.rotation; // 确保旋转也同步
// SetParent(Rod.lineHandler.LineConnector_2.transform);
var node = Rod.Line.GetLogicalNode(FishingLineNode.NodeType.Tail);
SetParent(node.transform);
SetParent(Rod.Line.Lure.transform);
// SetParent(Rod.Line.Lure.transform);
transform.localPosition = Vector3.zero;
// var target = lineHandler.LineConnector_2.GetComponent<Rigidbody>();

View File

@@ -1,173 +1,173 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using NBC;
// using Obi;
using UnityEngine;
namespace NBF
{
public enum LineType
{
Hand,
HandDouble,
Spinning,
SpinningFloat,
}
public class FLine : FGearBase
{
public LineType LineType;
[SerializeField] private bool isLureConnect;
[SerializeField] private RodLine rodLine;
/// <summary>
/// 主线
/// </summary>
[SerializeField] private Rope fishingRope;
/// <summary>
/// 浮漂和鱼钩线
/// </summary>
[SerializeField] private Rope bobberRope;
public LureController Lure;
public BobberController Bobber;
public JointPinchController PinchController;
public float LinelenghtDiferent;
protected override void OnInit()
{
var tipRb = Rod.Asset.LineConnectorRigidbody;
if (isLureConnect)
{
Lure.SetJoint(tipRb);
Lure.EnableCollision(false);
}
else
{
fishingRope.startAnchor = tipRb;
Bobber.SetJoint(tipRb);
Lure.SetJoint(Bobber.rbody);
Lure.gameObject.SetActive(true);
Lure.EnableCollision(false);
Lure.SetKinematic(false);
}
GetComponentsInChildren<Transform>(includeInactive: true).ToList().ForEach(delegate(Transform i)
{
i.gameObject.SetActive(true);
});
StartCoroutine(LureUseGravity());
if (isLureConnect)
{
fishingRope.Init(Rod);
}
else
{
fishingRope.Init(Rod);
bobberRope.Init(Rod);
}
// rodLine.GenerateLineRendererRope(guides.ToArray(), _LineThickness);
}
public void InitTest(Rigidbody tipRb)
{
if (isLureConnect)
{
Lure.SetJoint(tipRb);
Lure.EnableCollision(false);
}
else
{
fishingRope.startAnchor = tipRb;
Bobber.SetJoint(tipRb);
Lure.SetJoint(Bobber.rbody);
Lure.gameObject.SetActive(true);
Lure.EnableCollision(false);
Lure.SetKinematic(false);
}
GetComponentsInChildren<Transform>(includeInactive: true).ToList().ForEach(delegate(Transform i)
{
i.gameObject.SetActive(true);
});
StartCoroutine(LureUseGravity());
if (isLureConnect)
{
fishingRope.Init(Rod);
}
else
{
fishingRope.Init(Rod);
bobberRope.Init(Rod);
}
}
private IEnumerator LureUseGravity()
{
yield return 1;
Lure.gameObject.SetActive(false);
Lure.gameObject.SetActive(true);
yield return 1;
Lure.RBody.useGravity = true;
}
public void SetTargetLength(float value)
{
Log.Error($"SetObiRopeStretch={value}");
if (value > 3)
{
// value -= 0.2f;
}
fishingRope.SetTargetLength(value);
}
public void SetLureLength(float value)
{
Log.Error($"SetObiRopeStretch={value}");
bobberRope.SetTargetLength(value);
}
private void Update()
{
LinelenghtDiferent = GetLineDistance();
//非钓鱼状态
Rod.PlayerItem.Tension = Mathf.Clamp(LinelenghtDiferent, 0f, 0.05f);
}
#region Tension
private float GetLineDistance()
{
if (!Bobber.JointRb)
{
return 0;
}
// return 0;
//第一个节点到竿稍的位置-第一段鱼线长度
return Vector3.Distance(Bobber.transform.position, Bobber.JointRb.transform.position) -
fishingRope.GetCurrentLength();
}
public float GetTension(float weight)
{
return weight * GetLineDistance();
}
#endregion
}
}
// using System;
// using System.Collections;
// using System.Collections.Generic;
// using System.Linq;
// using NBC;
// // using Obi;
// using UnityEngine;
//
// namespace NBF
// {
// public enum LineType
// {
// Hand,
// HandDouble,
// Spinning,
// SpinningFloat,
// }
//
// public class FLine : FGearBase
// {
// public LineType LineType;
//
// [SerializeField] private bool isLureConnect;
// [SerializeField] private RodLine rodLine;
//
// /// <summary>
// /// 主线
// /// </summary>
// [SerializeField] private Rope fishingRope;
//
// /// <summary>
// /// 浮漂和鱼钩线
// /// </summary>
// [SerializeField] private Rope bobberRope;
//
// public LureController Lure;
// public BobberController Bobber;
//
// public JointPinchController PinchController;
//
//
// public float LinelenghtDiferent;
//
// protected override void OnInit()
// {
// var tipRb = Rod.Asset.LineConnectorRigidbody;
// if (isLureConnect)
// {
// Lure.SetJoint(tipRb);
// Lure.EnableCollision(false);
// }
// else
// {
// fishingRope.startAnchor = tipRb;
// Bobber.SetJoint(tipRb);
// Lure.SetJoint(Bobber.rbody);
// Lure.gameObject.SetActive(true);
// Lure.EnableCollision(false);
// Lure.SetKinematic(false);
// }
//
// GetComponentsInChildren<Transform>(includeInactive: true).ToList().ForEach(delegate(Transform i)
// {
// i.gameObject.SetActive(true);
// });
//
// StartCoroutine(LureUseGravity());
// if (isLureConnect)
// {
// fishingRope.Init(Rod);
// }
// else
// {
// fishingRope.Init(Rod);
// bobberRope.Init(Rod);
// }
//
// // rodLine.GenerateLineRendererRope(guides.ToArray(), _LineThickness);
// }
//
// public void InitTest(Rigidbody tipRb)
// {
// if (isLureConnect)
// {
// Lure.SetJoint(tipRb);
// Lure.EnableCollision(false);
// }
// else
// {
// fishingRope.startAnchor = tipRb;
// Bobber.SetJoint(tipRb);
// Lure.SetJoint(Bobber.rbody);
// Lure.gameObject.SetActive(true);
// Lure.EnableCollision(false);
// Lure.SetKinematic(false);
// }
//
// GetComponentsInChildren<Transform>(includeInactive: true).ToList().ForEach(delegate(Transform i)
// {
// i.gameObject.SetActive(true);
// });
//
// StartCoroutine(LureUseGravity());
// if (isLureConnect)
// {
// fishingRope.Init(Rod);
// }
// else
// {
// fishingRope.Init(Rod);
// bobberRope.Init(Rod);
// }
// }
//
// private IEnumerator LureUseGravity()
// {
// yield return 1;
// Lure.gameObject.SetActive(false);
// Lure.gameObject.SetActive(true);
// yield return 1;
// Lure.RBody.useGravity = true;
// }
//
// public void SetTargetLength(float value)
// {
// Log.Error($"SetObiRopeStretch={value}");
// if (value > 3)
// {
// // value -= 0.2f;
// }
//
// fishingRope.SetTargetLength(value);
// }
//
// public void SetLureLength(float value)
// {
// Log.Error($"SetObiRopeStretch={value}");
// bobberRope.SetTargetLength(value);
// }
//
//
// private void Update()
// {
// LinelenghtDiferent = GetLineDistance();
//
// //非钓鱼状态
// Rod.PlayerItem.Tension = Mathf.Clamp(LinelenghtDiferent, 0f, 0.05f);
// }
//
// #region Tension
//
// private float GetLineDistance()
// {
// if (!Bobber.JointRb)
// {
// return 0;
// }
//
// // return 0;
//
// //第一个节点到竿稍的位置-第一段鱼线长度
// return Vector3.Distance(Bobber.transform.position, Bobber.JointRb.transform.position) -
// fishingRope.GetCurrentLength();
// }
//
// public float GetTension(float weight)
// {
// return weight * GetLineDistance();
// }
//
// #endregion
// }
// }

View File

@@ -14,7 +14,10 @@ namespace NBF
// SetParent(Rod.lineHandler.LineConnector_1.transform);
SetParent(Rod.Line.Lure.transform);
var node = Rod.Line.GetLogicalNode(FishingLineNode.NodeType.Float);
SetParent(node.transform);
// SetParent(Rod.Line.Lure.transform);
transform.localPosition = Vector3.zero;
}
}

View File

@@ -25,7 +25,7 @@ namespace NBF
public FBait Bait;
public FLure Lure;
public FWeight Weight;
public FLine Line;
public FishingLineSolver Line;
public Transform GearRoot;
@@ -72,24 +72,29 @@ namespace NBF
if (Line.LineType == LineType.Spinning)
{
//没有浮漂类型
Line.Lure.SetJointDistance(PlayerItem.LineLength);
if (PlayerItem.StretchRope)
{
// Line.SetTargetLength(PlayerItem.Tension > 0f ? 0f : PlayerItem.LineLength);
Line.SetTargetLength(PlayerItem.LineLength);
}
// Line.Lure.SetJointDistance(PlayerItem.LineLength);
// if (PlayerItem.StretchRope)
// {
// // Line.SetTargetLength(PlayerItem.Tension > 0f ? 0f : PlayerItem.LineLength);
// Line.SetTargetLength(PlayerItem.LineLength);
// }
Line.SetLenght(PlayerItem.LineLength);
}
else
{
//有浮漂
Line.Lure.SetJointDistance(PlayerItem.FloatLength);
Line.Bobber.SetJointDistance(PlayerItem.LineLength - PlayerItem.FloatLength);
if (PlayerItem.StretchRope)
{
// Line.SetTargetLength(PlayerItem.Tension > 0f ? 0f : PlayerItem.LineLength - PlayerItem.FloatLength);
Line.SetTargetLength(PlayerItem.LineLength - PlayerItem.FloatLength);
Line.SetLureLength(PlayerItem.FloatLength);
}
// Line.Lure.SetJointDistance(PlayerItem.FloatLength);
// Line.Bobber.SetJointDistance(PlayerItem.LineLength - PlayerItem.FloatLength);
// if (PlayerItem.StretchRope)
// {
// // Line.SetTargetLength(PlayerItem.Tension > 0f ? 0f : PlayerItem.LineLength - PlayerItem.FloatLength);
// Line.SetTargetLength(PlayerItem.LineLength - PlayerItem.FloatLength);
// Line.SetLureLength(PlayerItem.FloatLength);
// }
Line.SetLenght(PlayerItem.LineLength - PlayerItem.FloatLength);
Line.SetLenght(PlayerItem.FloatLength, 1);
}
}
@@ -251,7 +256,7 @@ namespace NBF
var solver = Instantiate(lineSolverPrefab, GearRoot);
solver.transform.position = Asset.lineConnector.position;
solver.transform.rotation = Asset.lineConnector.rotation;
var indexNames = new[] { "fishing line float set", "fishing line spinning" };
var indexNames = new[] { "FishingLine1", "FishingLine1" };
var path =
$"Assets/ResRaw/Prefabs/Line/{indexNames[currentLineTypeIndex]}.prefab";
var prefab = Assets.Load<GameObject>(path);
@@ -261,7 +266,7 @@ namespace NBF
obj.transform.localScale = Vector3.one;
obj.transform.rotation = Quaternion.identity;
Line = obj.GetComponent<FLine>();
Line = obj.GetComponent<FishingLineSolver>();
Line.transform.position = Asset.lineConnector.position;
Line.Init(this);
}
@@ -339,11 +344,13 @@ namespace NBF
var state = PlayerItem.Owner.State;
Vector3 vector = Line.Lure.transform.position;
var endNode = Line.GetEndNode();
Vector3 vector = endNode.transform.position;
// 当前物体的朝向与指向 Lure 的方向之间的夹角,在 0完全对齐到 1完全相反之间的一个比例值
float headingAlignment = Vector3.Angle(base.transform.forward,
(Line.Lure.transform.position - transform.position).normalized) / 180f;
(endNode.transform.position - transform.position).normalized) / 180f;
// 经过朝向调制后的有效张力
var effectiveTension = Mathf.Clamp(CurrentTension01 * headingAlignment, 0f, 1f);

View File

@@ -31,12 +31,6 @@ public class Rope : MonoBehaviour
[SerializeField, Range(0, 16), Tooltip("主求解后追加的硬长度约束次数。只负责把 poly 拉回到 rest total不改变可变长度逻辑")]
private int hardTightenIterations = 2;
[SerializeField, Range(0, 32), Tooltip("当绳子接近拉直时,按误差自动追加的硬长度约束次数上限")]
private int adaptiveHardTightenMaxIterations = 8;
[SerializeField, Min(0f), Tooltip("单段允许的最大超长误差;超过时继续追加硬长度约束")]
private float hardConstraintTolerance = 0.0005f;
[Header("Length Control (No Min/Max Clamp)")]
[Tooltip("初始总长度(米)。如果为 0则用 physicsSegmentLen*(minPhysicsNodes-1) 作为初始长度")]
[SerializeField, Min(0f)]
@@ -217,8 +211,6 @@ public class Rope : MonoBehaviour
renderSubdivisionsMoving = Mathf.Max(renderSubdivisionsMoving, 1);
iterations = Mathf.Clamp(iterations, 1, 80);
hardTightenIterations = Mathf.Clamp(hardTightenIterations, 0, 16);
adaptiveHardTightenMaxIterations = Mathf.Clamp(adaptiveHardTightenMaxIterations, 0, 32);
hardConstraintTolerance = Mathf.Max(0f, hardConstraintTolerance);
groundCastDistance = Mathf.Max(groundCastDistance, 0.01f);
groundCastHeight = Mathf.Max(groundCastHeight, 0f);
lineWidth = Mathf.Max(lineWidth, 0.0001f);
@@ -540,7 +532,6 @@ public class Rope : MonoBehaviour
}
SolveHardDistanceConstraints(hardTightenIterations);
SolveHardDistanceConstraintsAdaptive();
LockAnchorsHard();
if (constrainToGround)
@@ -581,7 +572,16 @@ public class Rope : MonoBehaviour
EnsureRenderCaches();
int last = _physicsNodes - 1;
DrawHighResLine_Fast(_startTr.position, _endTr.position, last);
Vector3 s = _startTr.position;
Vector3 e = _endTr.position;
_pCurr[0] = s;
_pCurr[last] = e;
// _pPrev[0] = s;
// _pPrev[last] = e;
DrawHighResLine_Fast();
}
private void UpdateLengthSmooth()
@@ -734,21 +734,6 @@ public class Rope : MonoBehaviour
}
}
private void SolveHardDistanceConstraintsAdaptive()
{
if (adaptiveHardTightenMaxIterations <= 0 || hardConstraintTolerance <= 0f)
return;
for (int it = 0; it < adaptiveHardTightenMaxIterations; it++)
{
if (GetMaxPositiveSegmentDelta() <= hardConstraintTolerance)
break;
LockAnchorsHard();
SolveDistanceConstraints_HeadOnly_Hard();
}
}
private void SolveDistanceConstraints_HeadOnly_Hard()
{
SolveDistanceConstraints_HeadOnly_Bidirectional(1f);
@@ -802,21 +787,6 @@ public class Rope : MonoBehaviour
}
}
private float GetMaxPositiveSegmentDelta()
{
float maxDelta = 0f;
for (int i = 1; i < _physicsNodes; i++)
{
float rest = (i == 1) ? _headRestLen : physicsSegmentLen;
float segLen = Vector3.Distance(_pCurr[i - 1], _pCurr[i]);
float delta = segLen - rest;
if (delta > maxDelta)
maxDelta = delta;
}
return maxDelta;
}
private void ConstrainToGround()
{
if (groundMask == 0) return;
@@ -968,7 +938,7 @@ public class Rope : MonoBehaviour
}
}
private void DrawHighResLine_Fast(Vector3 renderStart, Vector3 renderEnd, int last)
private void DrawHighResLine_Fast()
{
if (_pCurr == null || _physicsNodes < 2) return;
@@ -979,9 +949,7 @@ public class Rope : MonoBehaviour
if (!smooth)
{
_lineRenderer.positionCount = _physicsNodes;
for (int i = 0; i <= last; i++)
_rPoints[i] = GetRenderPoint(i, last, renderStart, renderEnd);
_lineRenderer.SetPositions(_rPoints);
_lineRenderer.SetPositions(_pCurr);
return;
}
@@ -996,6 +964,7 @@ public class Rope : MonoBehaviour
}
int idx = 0;
int last = _physicsNodes - 1;
for (int seg = 0; seg < last; seg++)
{
@@ -1006,10 +975,10 @@ public class Rope : MonoBehaviour
int i3 = seg + 2;
if (i3 > last) i3 = last;
Vector3 p0 = GetRenderPoint(i0, last, renderStart, renderEnd);
Vector3 p1 = GetRenderPoint(i1, last, renderStart, renderEnd);
Vector3 p2 = GetRenderPoint(i2, last, renderStart, renderEnd);
Vector3 p3 = GetRenderPoint(i3, last, renderStart, renderEnd);
Vector3 p0 = _pCurr[i0];
Vector3 p1 = _pCurr[i1];
Vector3 p2 = _pCurr[i2];
Vector3 p3 = _pCurr[i3];
for (int s = 0; s < subdiv; s++)
{
@@ -1032,21 +1001,12 @@ public class Rope : MonoBehaviour
}
}
_rPoints[idx++] = renderEnd;
_rPoints[idx++] = _pCurr[last];
_lineRenderer.positionCount = idx;
_lineRenderer.SetPositions(_rPoints);
}
private Vector3 GetRenderPoint(int index, int last, Vector3 renderStart, Vector3 renderEnd)
{
if (index <= 0)
return renderStart;
if (index >= last)
return renderEnd;
return _pCurr[index];
}
private static float ClampMonotonic(float value, float p0, float p1, float p2, float p3)
{
bool rising = p0 <= p1 && p1 <= p2 && p2 <= p3;