(Background - at this point, I am a somewhat seasoned roblox developer - this post comes from my experience teaching others how to use the platform…)
Currently, in roblox, it is too hard for novice users to build multiplayer experiences that are not jittery and glitchy feeling if they need to move objects around.
This can be traced back to that doing the simplest possible thing in code to manipulate the world is unfortunately, right now, not the correct thing to do - when perhaps it should be?
Consider the following server script:
local part = script.Parent
local startPos = part.Position
part.Anchored = true
game["Run Service"].Heartbeat:Connect(function()
local sinWave = math.sin(tick()) * 5
part.Position = startPos + Vector3.new(0, sinWave, 0)
end)
What this script does should be obvious to even people coming from off-platform - it takes a part, anchors it, and then on Heartbeat makes it bob up and down. (you could use a while(true) loop, too)
But right now in roblox if you applied this to a server script, several things would be wrong with it:
- the part would be replicated to all clients at 20hz, making them look unsmooth
- if a player tried to stand on the part, they would not move correctly
- if it touches other physics parts, they will not move correctly either
(not smooth and other parts dont interact properly)
Here is the official roblox way to make this work smoothly, in as few steps as possible:
local part = script.Parent
local startPos = part.Position
local localAttachment = Instance.new("Attachment")
localAttachment.Parent = part
local worldAttachment = Instance.new("Attachment")
worldAttachment.Parent = game.Workspace.Terrain -- Aww come on, seriously...
local alignPosition = Instance.new("AlignPosition")
alignPosition.Parent = part
alignPosition.RigidityEnabled = true
alignPosition.Attachment0 = localAttachment
alignPosition.Attachment1 = worldAttachment
local alignOrientation = Instance.new("AlignOrientation")
alignOrientation.Parent = part
alignOrientation.RigidityEnabled = true
alignOrientation.Attachment0 = localAttachment
alignOrientation.Attachment1 = worldAttachment
part:SetNetworkOwner(nil) --Dont forget this or its glitchy
game["Run Service"].Heartbeat:Connect(function()
local sinWave = math.sin(tick()) * 5
worldAttachment.Position = startPos + Vector3.new(0, sinWave, 0)
end)
( This method also has a pretty big drawback if the part tries pass through other geometry - it glitches pretty hard and is capable of exploding the physics sim if it penetrates an assembly. )
What I propose is a third option - an Kinematic flag (or Instance?) for parts and assemblies.
Kinematic objects would simply be able to be CFramed/Positioned/Orientated to wherever you want, and would be 20hz interpolated from server to client.
On the client, the assembly would automatically set its velocity and angular velocity to automatically derived values based on the delta every frame. Kinematic parts would also never change network ownership.
This would make the correct way to animate a colliding part on the server as so:
local part = script.Parent
local startPos = part.Position
part.Kinematic = true --Tada!
game["Run Service"].Heartbeat:Connect(function()
local sinWave = math.sin(tick()) * 5
part.Position = startPos + Vector3.new(0, sinWave, 0)
end)