LinearVelocity has an inherent replication delay when created on the server and viewed on the client

When a LinearVelocity is created on the server, it will appear to have a noticeable “delay” before it acts when viewed as a player. This causes a sort of “teleportation” or “lag” effect to the part of which LinearVelocity is a descendant. A reproduction file listed below uses LinearVelocity & BodyVelocity as a comparison. BodyVelocity does not have this issue (or at least, a noticeable issue). This makes using LinearVelocity at high velocities almost unusable on the server as the first few moments cause the instance to teleport:

All constraints always had a delay before they started simulating (as expected when created on the server due to ping), but LinearVelocity, in particular, suffers from this issue the most as it can create unpredictable movement when used in high-velocity scenarios.

Comparison: LinearVelocity used in a production game
This is an enemy AI using LinearVelocity to displace its position. As you can see, it is choppy on the initial jump, making it seem like he teleports.

Comparison: Same thing as above, but instead using BodyVelocity
Still has a delay, but mostly unnoticeable.

System Information

  • AMD Ryzen 3700x
  • GTX 1070 Ti
  • 32GB 3200mhz Ram
  • Wins 10 Pro

Reproduction Steps
File: VelocityConstraintBugs.rbxl (47.0 KB)

  1. Open the file above
  2. Press Play Solo or F5
  3. You will be able to see that LinearVelocity acts a lot later than BodyVelocity
  4. Optionally, you can view it on the server (or run with F8 instead of F5), and you can see how both constraints should be working

Expected behavior

I expect LinearVelocity, when created on the server, to have a minimal lag effect after a force is acted on the part when viewed from a client, similar to the deprecated BodyVelocity.

7 Likes

Thanks for the report! We’ll follow up when we have an update for you.

4 Likes

I can confirm this issue also occurs with AlignPosition, which makes the switch from the deprecated BodyMovers basically impossible due to the very visible replication delay.
When the knockback is applied, a neon blue box is spawned at the damaged character for a split second:


This was the code used to implement the above knockback behavior:

Code
export type KnockbackData = {
	Direction: Vector3,
	Distance: number,
	Time: number,
	IgnoreGravity: boolean,
}

function Module.Knockback(DamagedRoot: BasePart, Data: KnockbackData)
	local ExistingKnockForce = DamagedRoot:FindFirstChild("KnockForce")
	if ExistingKnockForce then
		ExistingKnockForce:Destroy()
	end

	local DamagedHumanoid = (DamagedRoot.Parent :: Model):FindFirstChildOfClass("Humanoid") :: Humanoid
	DamagedHumanoid:SetStateEnabled(Enum.HumanoidStateType.Running, false)
	DamagedHumanoid:ChangeState(Enum.HumanoidStateType.Physics)

	local Time = Data.Time
	local Acceleration = 2 * Data.Distance / Time ^ 2
	local Velocity = Acceleration * Time

	local DamagedRootRigAttachment: Attachment = (DamagedRoot :: any).RootRigAttachment
	Gizmo:DrawBox(DamagedRootRigAttachment.WorldCFrame, Vector3.new(2, 2, 1), 0.2, Color3.new(0, 0, 1))

	local AlignPosition = Instance.new("AlignPosition")
	AlignPosition.Name = "KnockForce"
	AlignPosition.MaxForce = 1000000000000
	AlignPosition.MaxVelocity = Velocity
	AlignPosition.RigidityEnabled = false
	AlignPosition.Responsiveness = 200
	AlignPosition.Mode = Enum.PositionAlignmentMode.OneAttachment
	AlignPosition.Attachment0 = DamagedRootRigAttachment
	AlignPosition.ApplyAtCenterOfMass = true
	AlignPosition.ReactionForceEnabled = false
	AlignPosition.Position = DamagedRoot.Position + Data.Direction * Data.Distance
	AlignPosition.Parent = DamagedRoot

	DamagedRoot.AssemblyLinearVelocity = Acceleration * Data.Time * Data.Direction.Unit

	local GravityForce
	if Data.IgnoreGravity then
		GravityForce = Instance.new("VectorForce")
		GravityForce.Name = "KnockGravity"
		GravityForce.Attachment0 = DamagedRootRigAttachment
		GravityForce.Force = Vector3.new(0, workspace.Gravity * DamagedRoot.AssemblyMass, 0)
		GravityForce.Parent = DamagedRoot
	end

	local BodyGyro = Instance.new("BodyGyro")
	BodyGyro.Name = "KnockGyro"
	BodyGyro.MaxTorque = Vector3.new(math.huge, math.huge, math.huge)
	BodyGyro.CFrame = CFrame.lookAt(Vector3.zero, -Data.Direction)
	BodyGyro.P = 5000
	BodyGyro.Parent = DamagedRoot

	task.delay(Data.Time, function()
		DamagedHumanoid:SetStateEnabled(Enum.HumanoidStateType.Running, true)
		DamagedHumanoid:ChangeState(Enum.HumanoidStateType.Running)

		AlignPosition:Destroy()
		BodyGyro:Destroy()
		if GravityForce then
			GravityForce:Destroy()
		end
	end)
end

I tried taking into account for as many factors as possible, including humanoid states & initial velocity, but none of these alleviated the huge replication delay.

3 Likes

I just enabled a change that should fix the original problem with LinearVelocity. Please take a look and see if the issue has been resolved. While debugging LinearVelocity, I looked at AlignPosition and I wasn’t seeing any replication problems but I’ll take another look. Thanks and let me know if you are still having problems.

9 Likes

Thank you! It looks like the original topic of LinearVelocity having replication delay is fixed!

2 Likes

Sorry, I actually had to rollback this change due to a potentially bad interaction with other physics related changes. I’ll let you know once I re-enable it. Thank you for your patience.

15 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.

The fix for this issue is now available through a new workspace property which is part of a three phase rollout. More information can be found here:

Setting this new property to Enabled should fix the replication delay and allow the LinearVelocity constraint to behave more like the deprecated BodyVelocity instance.

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.