feat(fix-system): 优化线缆收起动画
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -128,10 +128,10 @@ public class PhysicCable : MonoBehaviour
|
||||
private Vector3 _insertImpactDir;
|
||||
private Vector3 _insertImpactNormal;
|
||||
|
||||
// 收拢(Collapsing)状态:从当前形状纯插值收缩到目标点,时长由外部(如强制收起指令)指定
|
||||
// 收拢(Collapsing)状态:沿当前线径从尾端逐段吸回出线口
|
||||
private Vector3[] _collapseStart;
|
||||
private Vector3 _collapseTarget;
|
||||
private float _collapseStartLength;
|
||||
private float[] _collapseDistances;
|
||||
private float _collapsePathLength;
|
||||
private float _collapseDuration;
|
||||
private float _collapseElapsed;
|
||||
|
||||
@@ -239,7 +239,7 @@ public class PhysicCable : MonoBehaviour
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 强制收起:不走拖拽物理,从当前形状纯运动学插值收缩到 target(出线口收纳位)。
|
||||
/// 强制收起:不走拖拽物理,保留当前曲线路径并从尾端逐段缩短到 target(出线口)。
|
||||
/// 用于指令强制收起等需要在极短、精确可控时长内完成的场景,避免拖拽跟随参数(为秒级手动拖拽标定)
|
||||
/// 在几帧内来不及收敛而产生的甩鞭/抖动。
|
||||
/// </summary>
|
||||
@@ -248,10 +248,18 @@ public class PhysicCable : MonoBehaviour
|
||||
EnsureInit();
|
||||
|
||||
_collapseStart ??= new Vector3[_n];
|
||||
for (int i = 0; i < _n; i++) _collapseStart[i] = _pts[i].pos;
|
||||
_collapseDistances ??= new float[_n];
|
||||
|
||||
_collapsePathLength = 0f;
|
||||
_collapseStart[0] = FlattenZ(target);
|
||||
_collapseDistances[0] = 0f;
|
||||
for (int i = 1; i < _n; i++)
|
||||
{
|
||||
_collapseStart[i] = _pts[i].pos;
|
||||
_collapsePathLength += Vector3.Distance(_collapseStart[i - 1], _collapseStart[i]);
|
||||
_collapseDistances[i] = _collapsePathLength;
|
||||
}
|
||||
|
||||
_collapseTarget = FlattenZ(target);
|
||||
_collapseStartLength = _length;
|
||||
_collapseDuration = Mathf.Max(0.0001f, duration);
|
||||
_collapseElapsed = 0f;
|
||||
|
||||
@@ -492,20 +500,38 @@ public class PhysicCable : MonoBehaviour
|
||||
UpdatePlugAngle(plugged);
|
||||
}
|
||||
|
||||
/// <summary>Collapsing 状态的纯运动学插值:所有点从收拢起点直接 Lerp 到目标点,不经过 Verlet/约束求解。</summary>
|
||||
/// <summary>
|
||||
/// Collapsing 状态沿收线前的曲线逐段截短。线身形状不做整体缩放,
|
||||
/// 只有尾端沿原路径倒退,形成被线盘吸入的观感。
|
||||
/// </summary>
|
||||
private void SimulateCollapse(float dt)
|
||||
{
|
||||
_collapseElapsed += dt;
|
||||
float t = Mathf.Clamp01(_collapseElapsed / _collapseDuration);
|
||||
float eased = t * t; // ease-in:起步慢、收尾快,贴近"被吸回线盘"的观感
|
||||
float eased = t * t * (3f - 2f * t);
|
||||
float remainingLength = _collapsePathLength * (1f - eased);
|
||||
int segment = 1;
|
||||
|
||||
for (int i = 0; i < _n; i++)
|
||||
{
|
||||
Vector3 pos = Vector3.Lerp(_collapseStart[i], _collapseTarget, eased);
|
||||
float distance = remainingLength * (i / (float)(_n - 1));
|
||||
while (segment < _n - 1 && _collapseDistances[segment] < distance)
|
||||
{
|
||||
segment++;
|
||||
}
|
||||
|
||||
int previous = segment - 1;
|
||||
float segmentLength = _collapseDistances[segment] - _collapseDistances[previous];
|
||||
float segmentT = segmentLength > 1e-5f
|
||||
? (distance - _collapseDistances[previous]) / segmentLength
|
||||
: 0f;
|
||||
Vector3 pos = Vector3.Lerp(_collapseStart[previous], _collapseStart[segment], segmentT);
|
||||
_pts[i].pos = pos;
|
||||
_pts[i].prev = pos;
|
||||
}
|
||||
_length = Mathf.Lerp(_collapseStartLength, 0f, eased);
|
||||
|
||||
_length = remainingLength;
|
||||
SyncPlugAngleToTail();
|
||||
}
|
||||
|
||||
private Vector3 ApplyUnplugResistance(Vector3 rawPointer)
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace AibisDream.FixSystem
|
||||
public class CablePanel : MonoBehaviour, IOperatorPanel
|
||||
{
|
||||
private const int SortingOrderRetracted = 41;
|
||||
private const float RetractDuration = 0.18f; // 强制收起:插头 DOMove 与绳收拢共用时长
|
||||
private const float RetractDuration = 0.22f;
|
||||
|
||||
#region 组件索引
|
||||
|
||||
@@ -44,8 +44,9 @@ namespace AibisDream.FixSystem
|
||||
|
||||
private ISocket curSocket;
|
||||
|
||||
// 收线动画期间插头由 DOTween 驱动,暂停"插头跟随绳末端"
|
||||
// 放线动画期间插头由 DOTween 驱动,暂停"插头跟随绳末端"
|
||||
private bool suppressPlugFollow;
|
||||
private Tween retractTween;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -152,6 +153,8 @@ namespace AibisDream.FixSystem
|
||||
public void DropDownCableImmediate()
|
||||
{
|
||||
targetCableRetracted = false;
|
||||
retractTween?.Kill(false);
|
||||
retractTween = null;
|
||||
PlugRef.transform.DOKill(false);
|
||||
PlugRef.PullUpSocketSilent();
|
||||
CableReelRef.ResetRotation();
|
||||
@@ -202,7 +205,8 @@ namespace AibisDream.FixSystem
|
||||
&& physicCableRef.State != PhysicCable.CableState.Hidden)
|
||||
{
|
||||
bool active = physicCableRef.State == PhysicCable.CableState.Dragging
|
||||
|| physicCableRef.State == PhysicCable.CableState.Plugged;
|
||||
|| physicCableRef.State == PhysicCable.CableState.Plugged
|
||||
|| physicCableRef.State == PhysicCable.CableState.Collapsing;
|
||||
CableReelRef.UpdateRotation(active);
|
||||
}
|
||||
}
|
||||
@@ -213,7 +217,9 @@ namespace AibisDream.FixSystem
|
||||
|
||||
// 闲置/拖拽时插头挂在绳末端,位置与朝向都由绳给出
|
||||
var state = physicCableRef.State;
|
||||
if (state == PhysicCable.CableState.Free || state == PhysicCable.CableState.Dragging)
|
||||
if (state == PhysicCable.CableState.Free
|
||||
|| state == PhysicCable.CableState.Dragging
|
||||
|| state == PhysicCable.CableState.Collapsing)
|
||||
{
|
||||
physicCableRef.ApplyPlugPose(plugRef.transform, plugRef.PlugRoot);
|
||||
plugRef.ApplyFeedbackOffset();
|
||||
@@ -355,7 +361,10 @@ namespace AibisDream.FixSystem
|
||||
targetCableRetracted = true;
|
||||
isCableRetracted = true;
|
||||
|
||||
retractTween?.Kill(false);
|
||||
retractTween = null;
|
||||
PlugRef.transform.DOKill(false);
|
||||
PlugRef.CancelFeedbackTween();
|
||||
|
||||
if (physicCableRef.State == PhysicCable.CableState.Hidden)
|
||||
{
|
||||
@@ -363,11 +372,17 @@ namespace AibisDream.FixSystem
|
||||
return;
|
||||
}
|
||||
|
||||
suppressPlugFollow = true;
|
||||
physicCableRef.CollapseTo(retractedPos.position, RetractDuration);
|
||||
PlugRef.transform.DOMove(retractedPos.position, RetractDuration)
|
||||
.OnComplete(CompleteRetractIfCurrent)
|
||||
.OnKill(CompleteRetractIfCurrent);
|
||||
suppressPlugFollow = false;
|
||||
physicCableRef.CollapseTo(physicCableRef.startTransform.position, RetractDuration);
|
||||
retractTween = DOVirtual.DelayedCall(RetractDuration, CompleteRetractIfCurrent)
|
||||
.OnKill(() =>
|
||||
{
|
||||
// DelayedCall 正常完成后会自动 Kill;此时回调已经把字段清空,不重复结算。
|
||||
if (retractTween != null)
|
||||
{
|
||||
CompleteRetractIfCurrent();
|
||||
}
|
||||
});
|
||||
|
||||
AudioManager.Instance.PlaySfx("event:/ActionFB/mods_close");
|
||||
AudioManager.Instance.PlaySfx("event:/FollowInput/plugdrop");
|
||||
@@ -376,6 +391,7 @@ namespace AibisDream.FixSystem
|
||||
private void CompleteRetractIfCurrent()
|
||||
{
|
||||
if (!targetCableRetracted) return;
|
||||
retractTween = null;
|
||||
ApplyRetractedState();
|
||||
isCableRetracted = true;
|
||||
}
|
||||
@@ -385,6 +401,8 @@ namespace AibisDream.FixSystem
|
||||
if (!isCableRetracted && !targetCableRetracted) return;
|
||||
|
||||
targetCableRetracted = false;
|
||||
retractTween?.Kill(false);
|
||||
retractTween = null;
|
||||
PlugRef.PullUpSocket();
|
||||
PlugRef.transform.DOKill(false);
|
||||
CableReelRef.ResetRotation();
|
||||
@@ -421,6 +439,8 @@ namespace AibisDream.FixSystem
|
||||
{
|
||||
targetCableRetracted = true;
|
||||
isCableRetracted = true;
|
||||
retractTween?.Kill(false);
|
||||
retractTween = null;
|
||||
PlugRef.transform.DOKill(false);
|
||||
ApplyRetractedState();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user