Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
1854d2d
fix: half float position encoding manufactures motion on resting objects
NoelStephensUnity Aug 18, 2026
20d9ccd
test: integration coverage for half float position encoding
NoelStephensUnity Aug 18, 2026
03966e5
Update changelog for NetworkTransform precision change
NoelStephensUnity Aug 19, 2026
8398dc3
Fix jitter issue with stationary non-authority objects
NoelStephensUnity Aug 19, 2026
6100893
test - update
NoelStephensUnity Aug 20, 2026
1bbdfdb
Merge branch 'develop-3.x.x' into fix/half-float-delta-position-dithe…
NoelStephensUnity Aug 20, 2026
36f66ec
test - update
NoelStephensUnity Aug 21, 2026
c7d3ecd
update
NoelStephensUnity Aug 21, 2026
ad3c564
Merge branch 'develop-3.x.x' into fix/half-float-delta-position-dithe…
NoelStephensUnity Aug 22, 2026
d6f5649
test: Refer to the single non-authority instance directly
NoelStephensUnity Aug 26, 2026
038311b
update
NoelStephensUnity Aug 26, 2026
0de0857
test: Drop two NetworkDeltaPosition tests that cover an already cover…
NoelStephensUnity Aug 26, 2026
19ef4f2
Merge develop-3.x.x into fix/half-float-delta-position-dither-up-port
netcode-ci-service Aug 27, 2026
8773a34
test: Move NetworkDeltaPositionTests into the editor test assembly
NoelStephensUnity Aug 27, 2026
5ed0118
test: Walk the stationary ticks with a while loop
NoelStephensUnity Aug 28, 2026
05d7c7f
Merge develop-3.x.x into fix/half-float-delta-position-dither-up-port
netcode-ci-service Aug 28, 2026
0f53406
Merge branch 'develop-3.x.x' into fix/half-float-delta-position-dithe…
NoelStephensUnity Aug 28, 2026
d9c69cf
Merge develop-3.x.x into fix/half-float-delta-position-dither-up-port
netcode-ci-service Aug 31, 2026
dff0016
Merge branch 'develop-3.x.x' into fix/half-float-delta-position-dithe…
NoelStephensUnity Aug 31, 2026
c202b4f
test: remove the NetworkDeltaPositionTests remarks block
NoelStephensUnity Aug 31, 2026
d16954d
Merge develop-3.x.x into fix/half-float-delta-position-dither-up-port
netcode-ci-service Sep 1, 2026
bd0c255
update
NoelStephensUnity Sep 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions com.unity.netcode.gameobjects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Additional documentation and release notes are available at [Multiplayer Documen

### Changed

- Changed `NetworkTransform.UseHalfFloatPrecision` to synchronize position with a resolution of approximately 1mm regardless of how far an object has travelled. Previously the resolution could degrade to approximately 3cm. This does not increase bandwidth, but projects using `NetworkTransform.UseUnreliableDeltas` will send full precision position updates more often. (#4129)

- All editor assembly definitions are renamed with `Unity.Netcode.GameObjects.x` variants
- `Unity.Netcode.Editor` → `Unity.Netcode.GameObjects.Editor`
- `Unity.Netcode.Editor.CodeGen` → `Unity.Netcode.GameObjects.Editor.CodeGen`
Expand All @@ -29,6 +31,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
- Issue where `NetworkTransform.GetTickLatencyInSeconds` returned an absolute network timestamp that grew for as long as the session ran, rather than the tick latency as a duration in seconds that it is documented to return. (#4135)
- Issue where lerp smoothing was applied per frame instead of over time, which caused the `Lerp` and `SmoothDampening` interpolation types to smooth by different amounts at different frame rates. Results at 60fps are unchanged. (#4132)
- Issue where setting a maximum interpolation time of 1.0 would stop a `NetworkTransform` from interpolating at all when using the `Lerp` or `SmoothDampening` interpolation types. (#4132)
- Issue where objects using `NetworkTransform.UseHalfFloatPrecision` appeared to jitter on non-authority instances while they were stationary or coming to rest, even though the authority was not moving them. (#4129)
- Issue with not being able to spawn initially disabled in-scene placed objects. (#4093)
- Issue with pre-instantiated network prefab instances being marked as in-scene placed. Now pre-instantiated network prefabs are dynamically spawned. (#4093)
- Issue where a user could spawn runtime created `NetworkObject` that has a GlobalObjectIdHash of zero. These are not valid instances and will no longer be allowed to spawn. (#4093)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,24 @@ namespace Unity.Netcode.Components
[Serializable]
public struct NetworkDeltaPosition : INetworkSerializable
{
internal const float MaxDeltaBeforeAdjustment = 64f;
/// <summary>
/// How far the delta may grow before it is folded into the base position.
/// </summary>
/// <remarks>
/// This determines the transmitted position resolution, since a half float's step size grows with its
/// magnitude. Keeping the delta small keeps that step small: at 2 the coarsest step is roughly 1mm.
/// </remarks>
internal const float MaxDeltaBeforeAdjustment = 2f;

/// <summary>
/// Masks off a half float's sign bit, leaving its magnitude.
/// </summary>
internal const ushort HalfMagnitudeMask = 0x7FFF;

/// <summary>
/// The bit pattern of the largest finite half float (65504); anything above it is an infinity or a NaN.
/// </summary>
internal const ushort LargestFiniteHalfBits = 0x7BFF;

/// <summary>
/// The HalfVector3 used to synchronize the delta in position
Expand Down Expand Up @@ -138,14 +155,30 @@ public void UpdateFrom(ref Vector3 vector3, int networkTick)
{
CollapsedDeltaIntoBase = false;
NetworkTick = networkTick;
DeltaPosition = (vector3 + PrecisionLossDelta) - CurrentBasePosition;
for (int i = 0; i < HalfVector3.Length; i++)
{
if (HalfVector3.AxisToSynchronize[i])
{
var rawDelta = vector3[i] - CurrentBasePosition[i];

// Adding the previous rounding loss back in keeps the average position accurate while the
// value is moving, but it also changes the value being sent. Once the value stops moving
// that is all it does, which makes a stationary object appear to oscillate.
var movedSinceLastSend = Mathf.Abs(vector3[i] - PreviousPosition[i]);
var applyPrecisionLoss = movedSinceLastSend >= HalfPrecisionQuantum(rawDelta);

DeltaPosition[i] = applyPrecisionLoss ? rawDelta + PrecisionLossDelta[i] : rawDelta;

HalfVector3.Axis[i] = math.half(DeltaPosition[i]);
HalfDeltaConvertedBack[i] = Mathf.HalfToFloat(HalfVector3.Axis[i].value);
PrecisionLossDelta[i] = DeltaPosition[i] - HalfDeltaConvertedBack[i];

// Only recompute the carried loss when it was applied. Leaving it alone otherwise is
// what keeps it around to apply once movement resumes.
if (applyPrecisionLoss)
{
PrecisionLossDelta[i] = DeltaPosition[i] - HalfDeltaConvertedBack[i];
}

if (Mathf.Abs(HalfDeltaConvertedBack[i]) >= MaxDeltaBeforeAdjustment)
{
CurrentBasePosition[i] += HalfDeltaConvertedBack[i];
Expand All @@ -165,6 +198,26 @@ public void UpdateFrom(ref Vector3 vector3, int networkTick)
}
}

/// <summary>
/// The smallest change a half float can represent at the magnitude of the value passed in.
/// </summary>
/// <param name="value">The value to get the step size for.</param>
/// <returns>The distance to the next representable half float value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static float HalfPrecisionQuantum(float value)
{
// The step size is symmetric about zero, so the sign is dropped.
var magnitude = (ushort)(math.half(value).value & HalfMagnitudeMask);

// Guard only: stepping past this would give infinity.
if (magnitude >= LargestFiniteHalfBits)
{
return MaxDeltaBeforeAdjustment;
}

return Mathf.HalfToFloat((ushort)(magnitude + 1)) - Mathf.HalfToFloat(magnitude);
}

/// <summary>
/// Constructor
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ namespace Unity.Netcode
/// </summary>
internal static class NetworkConstants
{
internal const string PROTOCOL_VERSION = "15.0.0";
internal const string PROTOCOL_VERSION = "15.1.0";
}
}
Loading
Loading