Help on script that makes part move to cursor when holding

I need help with a script that makes the part the script is in move to the cursor when holding down on the object(with a clickdetector)
Its similar to a few games where you can click and hold and the part will fling around, I don’t need it to fling around, just follow the cursor and drop when you stop holding down.
Keep in mind that I’m still very new to scripting.

> local target = script.Parent
> 
> local CursorId = "rbxassetid://6739628607"
> 
> local ClickDetector = Instance.new("ClickDetector")
> 
> ClickDetector.Parent = target
> 
> ClickDetector.MaxActivationDistance = 18
> 
> ClickDetector.CursorIcon = "CursorId"
> 
> ClickDetector.MouseClick:Connect(function()
> 
> --Where I need help--
> 
> end)

I’m awful at formatting code, sorry.

1 Like

Format your code using 3 of these (3 on the first line, and 3 on the last line) → `

^ It’ll help you in the future, as it makes your code more readable. :smiley:

Ok thanks! Do you by chance know how to complete this script?

You’re probably going to want to look into RunService, UserInputService and PlayerMouse.

Here’s an example, try inserting it into StarterPlayerScripts:

local players = game:GetService('Players')
local userInputService = game:GetService('UserInputService')
local runService = game:GetService('RunService')

local mouse = players.LocalPlayer:GetMouse() -- get the player's mouse object

userInputService.InputBegan:Connect(function(key, isSystemReserved)
    if key.UserInputType == Enum.UserInputType.MouseButton1 then -- if the key/input object is the mouse button 1
        local part = mouse.Target -- this returns the object at the target, basically a ray is casted from your screen from the position your mouse is at
        if part then -- we need this if statement, otherwise it would error if we, for example, clicked on the sky
            while userInputService:IsMouseButtonPressed() do -- while the mouse button is pressed
                part.Position = mouse.Hit.p -- move the part to the mouse hit position. Mouse.Hit returns a CFrame value so we only want the position component, not the rotation
                runService.RenderStepped:Wait() -- this basically means that it is going to wait until a new frame is rendered to check if the button is down, then go into the loop again
            end
        end
    end
end)
1 Like

thank you!

How do I apply this to a lot of parts?

It should already pick up any part in the workspace.

Oh, sorry i should of explained. I need only some parts to be able to be picked up, is there a way I could put a value of some sort into objects that I want to grab? Sorry for the constant questions :I

Ahh okay, I get what you mean.

You could probably add them to a folder. Then add an if statement like if part:IsDescendantOf(folder). You could also go for the value method that you mentioned but I recommend IsDescendantOf for simplicity’s sake.

local players = game:GetService('Players')
local userInputService = game:GetService('UserInputService')
local runService = game:GetService('RunService')

local partsToPickUp = workspace.PartsToPickUp -- can be any instance such as a model, folder, even a part if you really wanted it to

local mouse = players.LocalPlayer:GetMouse() -- get the player's mouse object

userInputService.InputBegan:Connect(function(key, isSystemReserved)
    if key.UserInputType == Enum.UserInputType.MouseButton1 then -- if the key/input object is the mouse button 1
        local part = mouse.Target -- this returns the object at the target, basically a ray is casted from your screen from the position your mouse is at
        if part then -- we need this if statement, otherwise it would error if we, for example, clicked on the sky
            if part:IsDescendantOf(partsToPickUp) then -- if the part is a descendant (either the part is or one of the part's ancestors are a child of the partsToPickUp instance)
                while userInputService:IsMouseButtonPressed() do -- while the mouse button is pressed
                    part.Position = mouse.Hit.p -- move the part to the mouse hit position. Mouse.Hit returns a CFrame value so we only want the position component, not the rotation
                    runService.RenderStepped:Wait() -- this basically means that it is going to wait until a new frame is rendered to check if the button is down, then go into the loop again
                end
            end
        end
    end
end)
1 Like

Thank you sooo much!
I really enjoy scripting and this helps me see concepts like this a whole lot more.

1 Like

Although, on line 13 it says “Argument 1 missing or nil”
It also doesn’t seem to pick it up, I created a folder with a part, the folders named “PartsToPickUp”

Hmmm.

Maybe try changing the variable name to something shorter? It’s quite a long name so maybe I made a typo I can’t see.

I shortened it to ptpu and edited the lines with the old var. I’m presented with the same error along with "attempt to index nil with ‘GetMouse’ "

Ahh okay, it’s probably because LocalPlayer is returning nil for some reason. Is the script a server script? If so, try switching it to local.

Ok, I accidently had a server script version of it lol. It still seems to say the same error(without the 2nd one) whenever I click the part. I apologize for all this trouble!

Sorry just curious, do you know how to fix this? I’m awful at coding, it says line 14 argument 1 missing or nil still and it wont work.

Hmmm.

I’ll take a look into it, let me start up Studio rq and I’ll let you know.

1 Like

Found the issue, it was because IsMouseButtonDown requires a UserInputType enum as an argument. I also added the TargetFilter, otherwise the part would move around weirdly because the ray would keep casting onto the part you’ve picked up.

local players = game:GetService('Players')
local userInputService = game:GetService('UserInputService')
local runService = game:GetService('RunService')

local partsToPickUp = workspace.PartsToPickUp -- can be any instance such as a model, folder, even a part if you really wanted it to

local mouse = players.LocalPlayer:GetMouse() -- get the player's mouse object

userInputService.InputBegan:Connect(function(key, isSystemReserved)
    if key.UserInputType == Enum.UserInputType.MouseButton1 then -- if the key/input object is the mouse button 1
        local part = mouse.Target -- this returns the object at the target, basically a ray is casted from your screen from the position your mouse is at
        mouse.TargetFilter = part
        if part then -- we need this if statement, otherwise it would error if we, for example, clicked on the sky
            if part:IsDescendantOf(partsToPickUp) then -- if the part is a descendant (either the part is or one of the part's ancestors are a child of the partsToPickUp instance)
                while userInputService:IsMouseButtonPressed(Enum.UserInputType.MouseButton1) do -- while the mouse button is pressed
                    part.Position = mouse.Hit.p -- move the part to the mouse hit position. Mouse.Hit returns a CFrame value so we only want the position component, not the rotation
                    runService.RenderStepped:Wait() -- this basically means that it is going to wait until a new frame is rendered to check if the button is down, then go into the loop again
                end
            end
        end
    end
end)
1 Like

Works great! Although, there are a few concerns for me:
1.It seems sometimes the part will teleport back to its original position.
2.The part sinks into the objects by 1 stud, it’s not that big of a deal as it jumps back up after you release but it can cause physics issues.
3. Sometimes the part wont fall(almost like physics is disabled) until a couple secs or I move another part.

I feel bad as this has been going on for a while, sorry!

No worries haha.

1- Try using :SetNetworkOwner on the part. Ensure you’re doing it from a server script. You’re going to have to look into remote events and functions if you are planning on using it with more than one player though.

2- You can offset its CFrame by 0.5x its size. Thankfully, Roblox made offsetting CFrames easy!

part.CFrame *= CFrame.new(0, part.Size.Y / 2, 0) -- offset it on the Y axis by 1/2 of its size

3- That’s weird. I’m thinking it might have something to do with network ownership since physics are simulated on the server IIRC. And since the part is unanchored and the server has network ownership of it, that’s likely to happen.

1 Like