diff --git a/Assets/Scripts/FixSystemNew/BodyModule/Cable.cs b/Assets/Scripts/FixSystemNew/BodyModule/Cable.cs index 9cec05583..d15bcdc9c 100644 --- a/Assets/Scripts/FixSystemNew/BodyModule/Cable.cs +++ b/Assets/Scripts/FixSystemNew/BodyModule/Cable.cs @@ -68,7 +68,9 @@ namespace AibisDream.FixSystem /// private void ResolveConfig() { - _headStiffLength = config.headStiffLength <= 0f ? 0.055f : config.headStiffLength; + _headStiffLength = config.headStiffLength <= 0f + ? (config.stiffLength > 0f ? config.stiffLength : 0.055f) + : config.headStiffLength; _headRenderBlend = config.headRenderBlend <= 0f ? 0.55f : Mathf.Clamp01(config.headRenderBlend); _tailStiffCount = config.tailStiffCount < 2 ? 2 : config.tailStiffCount; diff --git a/Assets/Scripts/FixSystemNew/Cable/PhysicCable.cs b/Assets/Scripts/FixSystemNew/Cable/PhysicCable.cs index 16a840e4c..13684eda2 100644 --- a/Assets/Scripts/FixSystemNew/Cable/PhysicCable.cs +++ b/Assets/Scripts/FixSystemNew/Cable/PhysicCable.cs @@ -1,6 +1,7 @@ using UnityEngine; using UnityEngine.Serialization; +[RequireComponent(typeof(PhysicLineSegment))] public class PhysicCable : MonoBehaviour { [FormerlySerializedAs("StartTranform")] public Transform startTransform; @@ -13,12 +14,15 @@ public class PhysicCable : MonoBehaviour // Start is called before the first frame update void Start() { - physicLine = transform.GetComponent(); + EnsurePhysicLine(); } public void Init(Vector3 endPos) { - physicLine.Initialize(startTransform.position, endPos); + EnsurePhysicLine(); + if (physicLine == null || startTransform == null) return; + + physicLine.Initialize(startTransform.position, endPos, GetOutletDirection(endPos)); } public void HideCable() @@ -46,9 +50,14 @@ public class PhysicCable : MonoBehaviour { if (isEnabled) { + EnsurePhysicLine(); + if (physicLine == null) return; + // 更新起点位置 if (startTransform != null) { + Vector3 fallbackTarget = targetTransform != null ? targetTransform.position : startTransform.position + startTransform.right; + physicLine.UpdateOutletDirection(GetOutletDirection(fallbackTarget)); physicLine.UpdateStart(startTransform.position); } @@ -59,4 +68,37 @@ public class PhysicCable : MonoBehaviour } } } + + private Vector3 GetOutletDirection(Vector3 fallbackTarget) + { + if (startTransform == null) + { + return Vector3.right; + } + + if (startTransform.parent != null) + { + Vector3 radialDirection = startTransform.position - startTransform.parent.position; + if (radialDirection.sqrMagnitude > 1e-6f) + { + return radialDirection.normalized; + } + } + + Vector3 fallbackDirection = fallbackTarget - startTransform.position; + if (fallbackDirection.sqrMagnitude > 1e-6f) + { + return fallbackDirection.normalized; + } + + return startTransform.right; + } + + private void EnsurePhysicLine() + { + if (physicLine == null) + { + physicLine = transform.GetComponent(); + } + } } diff --git a/Assets/Scripts/FixSystemNew/Cable/PhysicLineSegment.cs b/Assets/Scripts/FixSystemNew/Cable/PhysicLineSegment.cs index ab99be7fd..ed55d2f55 100644 --- a/Assets/Scripts/FixSystemNew/Cable/PhysicLineSegment.cs +++ b/Assets/Scripts/FixSystemNew/Cable/PhysicLineSegment.cs @@ -6,6 +6,9 @@ public class PhysicLineSegment : MonoBehaviour { public float segmentSpacing = 0.1f; public int k = 30; + [Header("出线口保护段")] + [SerializeField] private float outletGuardLength = 0.15f; + [SerializeField] private int outletGuardParticleCount = 4; public List particles = new(); private List sticks = new(); @@ -15,8 +18,16 @@ public class PhysicLineSegment : MonoBehaviour public float gravity = -9.8f; private float originalLength; + private Vector3 outletStart; + private Vector3 outletDirection = Vector3.right; + private int protectedParticleCount = 1; public void Initialize(Vector3 start, Vector3 end) + { + Initialize(start, end, end - start); + } + + public void Initialize(Vector3 start, Vector3 end, Vector3 startDirection) { particles.Clear(); sticks.Clear(); @@ -26,25 +37,37 @@ public class PhysicLineSegment : MonoBehaviour } originalLength = Vector3.Distance(start, end); + outletStart = start; + outletDirection = ResolveDirection(startDirection, end - start); - segmentCount = Mathf.RoundToInt(originalLength / segmentSpacing); + float spacing = Mathf.Max(0.001f, segmentSpacing); + segmentCount = Mathf.Max(3, Mathf.CeilToInt(originalLength / spacing) + 1); + protectedParticleCount = ResolveProtectedParticleCount(segmentCount); lineRenderer = GetComponent(); + float guardLength = ResolveGuardLength(); + Vector3 guardEnd = start + outletDirection * guardLength; // Initialize particles for (int i = 0; i < segmentCount; i++) { - var particle = new VerletParticle(Vector3.Lerp(start, end, i / (float)segmentCount)); + Vector3 position = GetInitialParticlePosition(start, guardEnd, end, i); + var particle = new VerletParticle(position) + { + isLocked = i < protectedParticleCount + }; particles.Add(particle); } // Initialize sticks for (int i = 0; i < segmentCount - 1; i++) { - sticks.Add(new VerletStick(particles[i], particles[i + 1], segmentSpacing)); + sticks.Add(new VerletStick( + particles[i], + particles[i + 1], + Vector3.Distance(particles[i].position, particles[i + 1].position))); } - // Lock the first particle - particles[0].isLocked = true; + ApplyOutletGuard(); lineRenderer.positionCount = segmentCount; } @@ -63,8 +86,10 @@ public class PhysicLineSegment : MonoBehaviour void UpdateLine() { + ApplyOutletGuard(); + // Apply gravity to particles - for (int i = 1; i < particles.Count; i++) + for (int i = 0; i < particles.Count; i++) { particles[i].ApplyForce(Vector3.up * gravity * Time.deltaTime); particles[i].UpdatePosition(); @@ -78,6 +103,8 @@ public class PhysicLineSegment : MonoBehaviour stick.UpdateStick(); } } + + ApplyOutletGuard(); } public void UpdatePlug(Transform plug) @@ -95,9 +122,69 @@ public class PhysicLineSegment : MonoBehaviour public void UpdateStart(Vector3 newStartPosition) { - if (particles.Count > 0) + outletStart = newStartPosition; + ApplyOutletGuard(); + } + + public void UpdateOutletDirection(Vector3 newOutletDirection) + { + outletDirection = ResolveDirection(newOutletDirection, outletDirection); + ApplyOutletGuard(); + } + + private Vector3 GetInitialParticlePosition(Vector3 start, Vector3 guardEnd, Vector3 end, int index) + { + if (index < protectedParticleCount) { - particles[0].position = newStartPosition; + float t = protectedParticleCount <= 1 ? 0f : index / (float)(protectedParticleCount - 1); + return Vector3.Lerp(start, guardEnd, t); + } + + int flexibleCount = segmentCount - protectedParticleCount; + float flexT = (index - protectedParticleCount + 1) / (float)flexibleCount; + return Vector3.Lerp(guardEnd, end, flexT); + } + + private void ApplyOutletGuard() + { + if (particles.Count == 0) return; + + int count = Mathf.Min(protectedParticleCount, particles.Count); + float guardLength = ResolveGuardLength(); + for (int i = 0; i < count; i++) + { + float t = count <= 1 ? 0f : i / (float)(count - 1); + Vector3 lockedPosition = outletStart + outletDirection * (guardLength * t); + particles[i].position = lockedPosition; + particles[i].previousPosition = lockedPosition; + particles[i].isLocked = true; } } + + private int ResolveProtectedParticleCount(int count) + { + int requestedCount = Mathf.Max(1, outletGuardParticleCount); + return Mathf.Clamp(requestedCount, 1, Mathf.Max(1, count - 1)); + } + + private float ResolveGuardLength() + { + float configuredLength = Mathf.Max(0f, outletGuardLength); + return Mathf.Min(configuredLength, originalLength * 0.45f); + } + + private static Vector3 ResolveDirection(Vector3 direction, Vector3 fallback) + { + if (direction.sqrMagnitude > 1e-6f) + { + return direction.normalized; + } + + if (fallback.sqrMagnitude > 1e-6f) + { + return fallback.normalized; + } + + return Vector3.right; + } }