I am trying to create a smooth placement system (like theme park tycoon 2) that glides smoothly to the mouse position. I am using the Lerp()
function and it's working except for the fact that it is shaking (or appears to be). I think this has to do with the code updating too fast so when I move my mouse it is trying to move to multiple positions resulting in a shaking effect. I have tried adding waits in places to lower the update time but nothing helps. The script is located in StarterGui
later replicated to PlayerGui
. It is also a LocalScript
. This is a test world and script so nothing else is in the place (other than the baseplate). Here is my code:
-- Creates the part and sets properties local part = Instance.new("Part") part.Anchored = true part.CanCollide = false part.Parent = game.Workspace -- Mouse and player local player = game.Players.LocalPlayer local mouse = player:GetMouse() -- Position variables that are set below local PosX, PosY, PosZ -- Don't worry about this function local function Snap() PosX, PosY, PosZ = math.floor(mouse.Hit.X / 2 + 0.5) * 2, 0.5, math.floor(mouse.Hit.Z / 2 + 0.5) * 2 -- Math return PosX, PosY, PosZ end -- Here is where the lerping starts local function Lerp() mouse.TargetFilter = part -- Ignores the part Snap() -- Lerping if mouse.Target and mouse.Target.Name == "Baseplate" then for i = 0, 1, 0.001 do -- This lerps the part to the mouse position local mousePos = part.CFrame:Lerp(CFrame.new(PosX, PosY, PosZ), i) wait(0) -- simple wait part.CFrame = mousePos -- Sets the CFrame end end end mouse.Move:Connect(Lerp) -- Event
Thanks for any help in advance!
Mouse.Move
event only tracks the mouse position, while the RenderStepped
event interpolates the final position, and it will move at constant speed
(set in variable of same name).realPosition
in continuous space. Without tracking `realPosition, the position will never change if movement is not fast enough because the part would never make the jump between two grid cells in a single render step.speed
configuration.speed = math.huge
and it just works. In that case you also do not need to bookkeep the realPosition
anymore, which would make this script more compatible with other scripts.Other than that, this should work just fine :)
There are two ways to move models:
PrimaryPart
to be set (which is often not set simply because for most models it actually does not make any sense). If you have the PrimaryPart
, you can use SetPrimaryPartCFrame, like so: model.SetPrimaryPartCFrame(CFrame.new(newPos))
. In case of a model, you would also have to change part.Position
to model.PrimaryPart.Position
.PrimaryPart
. However it needs relative movement, not an absolute position. Also without a PrimaryPart
, you need a different way of determining what the model's Position
is (which, for some reason, after 12 years of Roblox, is still not trivial to figure out without PrimaryPart
) and would require quite a few more changes to the code.-- configuration local speed = 10 -- the speed at which the part should move -- services local RunService = game:GetService("RunService") -- Mouse and player local player = game.Players.LocalPlayer local mouse = player:GetMouse() -- Creates the part and sets properties local part = Instance.new("Part") part.Anchored = true part.CanCollide = false part.Parent = game.Workspace -- for snapping (moving in discrete space) to work, you will have to keep track of the real position local realPosition = part.Position -- Don't worry about this function local function Snap(pos) return Vector3.new(math.floor(pos.X / 2 + 0.5) * 2, 0.5, math.floor(pos.Z / 2 + 0.5) * 2) -- Math end -- Here is where the lerping starts local mousePos local function OnMouseMove() mouse.TargetFilter = part -- Ignores the part -- Lerping if mouse.Target and mouse.Target.Name == "Baseplate" then -- we want to move the part mousePos = mouse.Hit.p else -- we do not want to move the part mousePos = nil end end mouse.Move:Connect(OnMouseMove) -- Mouse bookkeeping event local function OnRenderStep(dt) if mousePos then local oldPos = realPosition -- part.Position local delta = mousePos - oldPos -- Vector representing movement from oldPos to mousePos local distance = delta.Magnitude local maxDistance = dt * speed --print(distance, maxDistance) local newPos if distance > maxDistance then -- move at speed toward position newPos = oldPos + maxDistance / distance * delta else -- we arrived at mousePos newPos = mousePos end -- track real position (in continuous space) realPosition = newPos -- snap position to grid (you can remove this line, if you want to move continuously) newPos = Snap(newPos) part.CFrame = CFrame.new(newPos) end end RunService.RenderStepped:connect(OnRenderStep)