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.
You will be able to see that LinearVelocity acts a lot later than BodyVelocity
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.
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.
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.
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.