fix 1
This commit is contained in:
@@ -64,12 +64,16 @@ public readonly struct ControlledPlayerCorrectionResult
|
||||
Vector3 position,
|
||||
Quaternion rotation,
|
||||
bool usedHardSnap,
|
||||
ControlledPlayerVisualCorrectionState nextState)
|
||||
ControlledPlayerVisualCorrectionState nextState,
|
||||
float positionError,
|
||||
float rotationErrorDegrees)
|
||||
{
|
||||
Position = position;
|
||||
Rotation = rotation;
|
||||
UsedHardSnap = usedHardSnap;
|
||||
NextState = nextState;
|
||||
PositionError = positionError;
|
||||
RotationErrorDegrees = rotationErrorDegrees;
|
||||
}
|
||||
|
||||
public Vector3 Position { get; }
|
||||
@@ -79,6 +83,10 @@ public readonly struct ControlledPlayerCorrectionResult
|
||||
public bool UsedHardSnap { get; }
|
||||
|
||||
public ControlledPlayerVisualCorrectionState NextState { get; }
|
||||
|
||||
public float PositionError { get; }
|
||||
|
||||
public float RotationErrorDegrees { get; }
|
||||
}
|
||||
|
||||
public static class ControlledPlayerCorrection
|
||||
@@ -114,17 +122,17 @@ public static class ControlledPlayerCorrection
|
||||
|
||||
if (positionError <= Mathf.Epsilon && rotationError <= Mathf.Epsilon)
|
||||
{
|
||||
return new ControlledPlayerCorrectionResult(targetPosition, targetRotation, false, ControlledPlayerVisualCorrectionState.None);
|
||||
return new ControlledPlayerCorrectionResult(targetPosition, targetRotation, false, ControlledPlayerVisualCorrectionState.None, positionError, rotationError);
|
||||
}
|
||||
|
||||
if (boundedPositionCorrection <= Mathf.Epsilon && boundedRotationCorrection <= Mathf.Epsilon)
|
||||
{
|
||||
return new ControlledPlayerCorrectionResult(targetPosition, targetRotation, true, ControlledPlayerVisualCorrectionState.None);
|
||||
return new ControlledPlayerCorrectionResult(targetPosition, targetRotation, true, ControlledPlayerVisualCorrectionState.None, positionError, rotationError);
|
||||
}
|
||||
|
||||
if (positionError > settings.SnapPositionThreshold || rotationError > settings.SnapRotationThresholdDegrees)
|
||||
{
|
||||
return new ControlledPlayerCorrectionResult(targetPosition, targetRotation, true, ControlledPlayerVisualCorrectionState.None);
|
||||
return new ControlledPlayerCorrectionResult(targetPosition, targetRotation, true, ControlledPlayerVisualCorrectionState.None, positionError, rotationError);
|
||||
}
|
||||
|
||||
var remainingStepBudget = activeCorrection.IsActive
|
||||
@@ -132,7 +140,7 @@ public static class ControlledPlayerCorrection
|
||||
: settings.MaxCorrectionSteps;
|
||||
if (remainingStepBudget <= 0)
|
||||
{
|
||||
return new ControlledPlayerCorrectionResult(targetPosition, targetRotation, true, ControlledPlayerVisualCorrectionState.None);
|
||||
return new ControlledPlayerCorrectionResult(targetPosition, targetRotation, true, ControlledPlayerVisualCorrectionState.None, positionError, rotationError);
|
||||
}
|
||||
|
||||
var correctedPosition = Vector3.MoveTowards(currentPosition, targetPosition, boundedPositionCorrection);
|
||||
@@ -141,19 +149,21 @@ public static class ControlledPlayerCorrection
|
||||
var nextRotationError = Quaternion.Angle(correctedRotation, targetRotation);
|
||||
if (nextPositionError <= Mathf.Epsilon && nextRotationError <= Mathf.Epsilon)
|
||||
{
|
||||
return new ControlledPlayerCorrectionResult(targetPosition, targetRotation, false, ControlledPlayerVisualCorrectionState.None);
|
||||
return new ControlledPlayerCorrectionResult(targetPosition, targetRotation, false, ControlledPlayerVisualCorrectionState.None, positionError, rotationError);
|
||||
}
|
||||
|
||||
remainingStepBudget--;
|
||||
if (remainingStepBudget <= 0)
|
||||
{
|
||||
return new ControlledPlayerCorrectionResult(targetPosition, targetRotation, true, ControlledPlayerVisualCorrectionState.None);
|
||||
return new ControlledPlayerCorrectionResult(targetPosition, targetRotation, true, ControlledPlayerVisualCorrectionState.None, positionError, rotationError);
|
||||
}
|
||||
|
||||
return new ControlledPlayerCorrectionResult(
|
||||
correctedPosition,
|
||||
correctedRotation,
|
||||
false,
|
||||
new ControlledPlayerVisualCorrectionState(targetPosition, targetRotation, remainingStepBudget));
|
||||
new ControlledPlayerVisualCorrectionState(targetPosition, targetRotation, remainingStepBudget),
|
||||
positionError,
|
||||
rotationError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +111,10 @@ public class MovementComponent : MonoBehaviour
|
||||
// This matches ServerAuthoritativeMovementConfiguration.SimulationInterval (50ms).
|
||||
private const float kServerSimulationStepSeconds = 0.05f;
|
||||
|
||||
// Dead-band threshold for send-interval correction hysteresis.
|
||||
// Prevents frame-to-frame send interval oscillation when server tick offset hovers near zero.
|
||||
private const int kTickOffsetThreshold = 2;
|
||||
|
||||
private int _speed = 2;
|
||||
[SerializeField] private Rigidbody _rigid;
|
||||
private float _lastSendTime = 0;
|
||||
@@ -207,7 +211,7 @@ public class MovementComponent : MonoBehaviour
|
||||
}
|
||||
|
||||
Simulate(_cachedMoveInput);
|
||||
_predictionBuffer.AccumulateLatest(Time.fixedDeltaTime);
|
||||
_predictionBuffer.AccumulateLatest(kServerSimulationStepSeconds);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -229,9 +233,11 @@ public class MovementComponent : MonoBehaviour
|
||||
return;
|
||||
}
|
||||
|
||||
var predictedPosition = _rigid.position;
|
||||
var predictedRotation = _rigid.rotation;
|
||||
var correction = ControlledPlayerCorrection.Resolve(
|
||||
_rigid.position,
|
||||
_rigid.rotation,
|
||||
predictedPosition,
|
||||
predictedRotation,
|
||||
snapshot.Position,
|
||||
snapshot.RotationQuaternion,
|
||||
new ControlledPlayerCorrectionSettings(kServerSimulationStepSeconds, _speed, TurnSpeedDegreesPerSecond),
|
||||
@@ -243,6 +249,16 @@ public class MovementComponent : MonoBehaviour
|
||||
_rigid.velocity = correction.UsedHardSnap ? snapshot.Velocity : Vector3.zero;
|
||||
_rigid.angularVelocity = Vector3.zero;
|
||||
ReplayPendingInputs(replayInputs);
|
||||
|
||||
if (MainUI.Instance != null)
|
||||
{
|
||||
MainUI.Instance.OnCorrectionMagnitudeChanged?.Invoke(
|
||||
predictedPosition,
|
||||
snapshot.Position,
|
||||
correction.PositionError,
|
||||
correction.RotationErrorDegrees);
|
||||
MainUI.Instance.OnAcknowledgedMoveTickChanged?.Invoke(_predictionBuffer.LastAcknowledgedMoveTick ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
private Vector3 CaptureMovement()
|
||||
@@ -311,14 +327,15 @@ public class MovementComponent : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
if (_currentTickOffset < 0)
|
||||
if (_currentTickOffset < -kTickOffsetThreshold)
|
||||
{
|
||||
_sendInterval = 0.052f;
|
||||
}
|
||||
if (_currentTickOffset > 0)
|
||||
else if (_currentTickOffset > kTickOffsetThreshold)
|
||||
{
|
||||
_sendInterval = 0.048f;
|
||||
}
|
||||
// Within [-kTickOffsetThreshold, +kTickOffsetThreshold]: no correction, keep current interval
|
||||
}
|
||||
|
||||
private void ReplayPendingInputs(IReadOnlyList<PredictedMoveStep> replayInputs)
|
||||
|
||||
@@ -11,12 +11,16 @@ public class MainUI : MonoBehaviour
|
||||
[SerializeField] private Text _serverTickText;
|
||||
[SerializeField] private Text _startTickOffsetText;
|
||||
[SerializeField] private Text _clientTickText;
|
||||
[SerializeField] private Text _correctionText;
|
||||
[SerializeField] private Text _acknowledgedTickText;
|
||||
|
||||
public UnityAction<Vector3> OnServerPosChanged;
|
||||
public UnityAction<Vector3> OnClientPosChanged;
|
||||
public UnityAction<long> OnServerTickChanged;
|
||||
public UnityAction<long> OnStartTickOffsetChanged;
|
||||
public UnityAction<long> OnClientTickChanged;
|
||||
public UnityAction<Vector3, Vector3, float, float> OnCorrectionMagnitudeChanged;
|
||||
public UnityAction<long> OnAcknowledgedMoveTickChanged;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
@@ -30,6 +34,8 @@ public class MainUI : MonoBehaviour
|
||||
OnServerTickChanged += UpdateServerTickText;
|
||||
OnClientTickChanged += UpdateClientTickText;
|
||||
OnStartTickOffsetChanged += UpdateStartTickOffsetText;
|
||||
OnCorrectionMagnitudeChanged += UpdateCorrectionText;
|
||||
OnAcknowledgedMoveTickChanged += UpdateAcknowledgedTickText;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
@@ -39,6 +45,8 @@ public class MainUI : MonoBehaviour
|
||||
OnServerTickChanged -= UpdateServerTickText;
|
||||
OnClientTickChanged -= UpdateClientTickText;
|
||||
OnStartTickOffsetChanged -= UpdateStartTickOffsetText;
|
||||
OnCorrectionMagnitudeChanged -= UpdateCorrectionText;
|
||||
OnAcknowledgedMoveTickChanged -= UpdateAcknowledgedTickText;
|
||||
}
|
||||
|
||||
private void UpdateServerPositionText(Vector3 pos)
|
||||
@@ -65,4 +73,14 @@ public class MainUI : MonoBehaviour
|
||||
{
|
||||
_clientTickText.text = "客户端Tick:" + tick;
|
||||
}
|
||||
|
||||
private void UpdateCorrectionText(Vector3 predictedPos, Vector3 authoritativePos, float positionError, float rotationError)
|
||||
{
|
||||
_correctionText.text = $"校正:pos差={positionError:F4} rot差={rotationError:F2}°";
|
||||
}
|
||||
|
||||
private void UpdateAcknowledgedTickText(long tick)
|
||||
{
|
||||
_acknowledgedTickText.text = "AckTick:" + tick;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user