feat(cable): 线缆优化

This commit is contained in:
2026-07-08 22:31:21 +08:00
parent 68837581af
commit f273569e7c
3 changed files with 142 additions and 11 deletions
@@ -68,7 +68,9 @@ namespace AibisDream.FixSystem
/// </summary>
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;
@@ -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<PhysicLineSegment>();
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<PhysicLineSegment>();
}
}
}
@@ -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<VerletParticle> particles = new();
private List<VerletStick> 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<LineRenderer>();
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;
}
}