Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
1

Part shaking when I move my mouse (for placement system)?

Asked by
zblox164 531 Moderation Voter
5 years ago

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 StarterGuilater 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!

0
The Move event is being fired each time the mouse moves, resulting in tons of lerps. A debounce may be what you're looking for. User#19524 175 — 5y
0
Thanks I will try that! zblox164 531 — 5y

1 answer

Log in to vote
1
Answered by
doomiiii 112
5 years ago
Edited 5 years ago

Explanation

  1. To make this work properly, you have to disentangle the lerping and the mouse position book-keeping. The code below does just that: The 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).
  2. In order to also snap to grid, you need to keep track of the 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.
  3. Feel free to play with the speed configuration.

Caveats

  1. If you want build placement to work like in Minecraft or Fortnite, you definitely don't want the lerp part. In that case, you can simply set 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.
  2. I'm sure you are aware: For this to work in multiplayer, you still need to split this script into client and server side.

Other than that, this should work just fine :)

How to move Models?

There are two ways to move models:

  1. The first way requires the model's 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.
  2. The lesser known TranslateBy function does not require a 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.

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)
0
Thanks! I am aware that this will not work on a server game (unless it is a 1 of 1 game) zblox164 531 — 5y
1
Would there be a way to use this using a model? zblox164 531 — 5y
0
Also it sometimes go off the grid but for the most part it works! zblox164 531 — 5y
0
I updated my answer to explain how to make this work with models doomiiii 112 — 5y
Ad

Answer this question