Dragging objects with the mouse

Hi! This is one of my first and most popular resources here, and I’m proud of it. After a year or so, decided to check it out again. I found some mistakes and use of stuff that wasn’t deprecated back in the old days, but I also updated almost all of the article. I took a different approach, a different solution, and decided to only explain what’s going on instead of starting with a script and working on from there. A much better end result is provided in the end rather than the simplistic and rusty old version. The older version of the article be found here.


I’m sure you once were wondering how would you make the player able to drag objects around with his mouse, just like in Lumber Tycoon for example.

cfd1aea09bac62a2be8d6a6fe958804f

It is certain that someone who had some decent knowledge with the mouse object has tried to make this by himself utilizing the mouse.Hit property of the mouse, which is the CFrame of the mouse in the 3D world.

We would set whatever the current mouse.Target, the part that the mouse is currently hovering over, to the mouse.Hit.Position, the position of the mouse in the 3D world (any CFrame has a .Position or .p (.p is out-dated) property which is just the position of that cframe (remember that cframe is position and rotation)). We can technically set the CFrame of the target straight away to mouse.Hit and not just the position but the rotation is kind of broken) each time the mouse.Move event fires and only when the mouse is pressing down. Also setting the mouse.TargetFilter to the target itself so the mouse ignores the target while calculating the mouse.Hit to prevent many issues.

So your attempt will look like something similar to this

local player = game.Players.LocalPlayer --the local player
local mouse = player:GetMouse() --his mouse
local target --this variable will hold the part that's being currently dragged
local down --this determines wether we are pressing or not

mouse.Button1Down:connect(function() 
	if mouse.Target ~= nil and mouse.Target.Locked == false then --checking if the mouse is actually hovering over an object, the locked property isn't really important
		target = mouse.Target --the target is set
		mouse.TargetFilter = target --preventing issues
		down = true  
	end 
end)

mouse.Move:Connect(function()
	if down == true and target ~= nil then --this event will be always firing, but we wanna change the target's position only when clicking, that's why we check if down is true
		target.Position = mouse.Hit.Position --the part that sets the position!
	end 
end) 

mouse.Button1Up:connect(function()
	down = false --and remember that after ending the holding, you wanna reset some properties
	mouse.TargetFilter = nil 
	target = nil
end)

Although this script is not the fanciest, and is definitely not perfect for a full-fledged game, it works!

drag1

Great efforts!

This would work, but not totally according to the plan. The parts would always be sticking to the ground; since really the mouse’s position is constantly landing there, it’s not in thin air. And even if you tried to drag the objects around in the sky above your head where there is nothing, they just disappear.

Perspective has a huge role in what’s going on.

What’s happening is, whenever you drag the mouse in the air, you’d think that the mouse.Hit is in the air as well. But the mouse.Hit, when calculated, will go in the same direction that the mouse is hovering in until it hits a surface. A way to prove that is, you try to drag it up in the air where there is no surface to land on, the part literally disappears because the mouse.Hit will go on until it hits its maximum length, so the part is really far away. You can even print the .Magnitude of the mouse.Hit.Position while hovering the mouse in empty space and you’ll see that it’s a large number (Any Vector3 has a .Magnitude or a .magnitude property, which is the length of the vector, magnitude is basically length, size or anything that goes along that).

print(mouse.Hit.Position.Magnitude) --9986.2734375, always rounds to 9986

newdrag1

Side note

Notice how I said “looking from where the camera is”, and drew the line from an actual camera! This shouldn’t be confusing.

The camera’s CFrame would always be the point of view from where the player is looking. It’s a CFrame positioned where he is looking from (for visualization, if you call ScreenPointToRay and tell it to cast a ray from the middle of the screen, the resulting ray’s origin is the camera’s CFrame!) looking towards what he’s focusing on.
drag8 drag9

In the picture above, this is what everything would look like if I was at that camera’s CFrame

newdrag3


So, what’s the plan? Remember that mouse.Hit.Position is the vector that’s laying on the ground, starting from 0,0,0. What we want instead is the vector starting from the camera right there. And we don’t want the vector itself, we want only a part of it, because you can see that even this new vector is going all the way to the ground, we want to limit how far it goes, say to that point I highlighted above.

How!

Well, first thing we need is that vector starting from the camera. If you know how vector subtraction works, that should be! When you subtract two vectors, the resulting vector is actually the vector in-between them! The picture below is in 2D, but everything will apply the same way in 3D.

newdrag4

Note that order of the operands does matter. a-b is the vector in-between looking at a, b-a is the vector in-between looking at b.

newdrag5

It might also be confusing that the vector isn’t starting from the universal origin 0,0 in the pictures above, don’t all vectors start from there? Yes absolutely! I placed the vector there just for visualization, it does start from 0,0. It’s as if I was told to calculate a-b just by drawing, what I did is drew the vector starting from b and looking at a, and transformed it to 0,0.

Ok, so in our case, what are the two operands? The mouse.Hit.Position, and the camera.CFrame.Position (camera is just workspace.CurrentCamera, it has a CFrame property, we’re interested in vectors here so we take its .Position). As I said, the order does matter, and in our case we want the vector to look at the mouse.Hit.Position, so what we have is

mouse.Hit.Position - camera.CFrame.Position

which looks like this

Now the limiting part,

(mouse.Hit.Position - camera.CFrame.Position).Unit * 20

mouse.Hit.Position.Unit is simply the direction of the mouse.Hit.Position (any Vector3 has a .Unit or a .unit property, it is the direction of that vector3), but if you wanna dig deeper, .Unit gives back a unit vector.

Unit vectors are vectors with a length of one (it’s in the name, unit), and they are used to describe directions. These guys are one-long because we don’t really care about their length, just their direction.

Again, .Unit is the direction of the given vector (given in the form of a unit vector). We take the direction of the vector resulting from the subtraction ((mouse.Hit.Position - camera.CFrame.Position).Unit). Here is the fun part, you can multiply by vectors by numbers, this scales them (the number is called a scalar). We can be multiply (mouse.Hit.Position - camera.CFrame.Position).Unit by, say 20, to get a vector in the same direction, but only 20 studs long. If the mouse was pointing very far away, we will only get back a 20 studs vector. This is how we limit.

drag5

Ok one last thing! Don’t let the placement of the blue vector fool you. That vector isn’t actually leading to that position, if I put him where he actually belongs (meaning starting from 0,0,0), you’ll understand why.

newdrag8

If you understand vector addition, you might as well know how to get that light blue vector! Adding two vectors is like placing the second on the tip of the first.

newdrag9

Same applies in 3D of course. Have you guessed how we can get that light blue vector? Yes! camera.CFrame.Position + our resulting blue vector.

We’re actually done! This is the result we needed

camera.CFrame.Position + (mouse.Hit.Position - camera.CFrame.Position).Unit * 20

It’s dragged in the air, goal achieved, but kind of rusty, but this is only because our starting script isn’t really perfect. The simple procedure of passing network ownership (with :SetNetworkOwnership()) to the client makes it way better, and also makes it wonderfully work in multiplayer.

Here is a way better script I made, using AlignPosition, and some fancy additions.

Another thing as well, this whole time we’ve been doing things based on the camera, it is actually better to do this based on the character’s head! This is a mistake I did in the previous version of the article. Making the dragging centered around the camera is bad, since if the player zoomed out really far, the part will go with him that far away (since it’s around the camera’s position). With the head, it will always be limited to 20 studs (that’s the distance we chose here) from the head, solving this problem. What I mean, this would be our end result instead

character.Head.Position + (mouse.Hit.Position - character.Head.Position).Unit * 20

This version of the script can be found here: dragging.rbxl (29.9 KB)
Here is an even better version, with some fixes, and even the ability to rotate dragged parts even_better_dragging.rbxl (33.4 KB)

And also if you want a much more advanced system that you can check it out (it’s uncopylocked) made by @BenSBk!

That’s it, have a wonderful day!

293 Likes

Thank you very much for this! It’s a pretty good resource for those that’s trying to figure out a way on how to carry stuff without keeping it too simple! (eg, putting the part inside the player’s backpack/welds it to your characters’ hand/etc)

Also, it would be useful if you could also provide on how to claim the part as yours so that other players wouldn’t be able to drag it (just like Lumber Tycoon).

19 Likes

Thank you a lot for the amazing feedback!

And for the claim part thing, it should be simple, perhaps insert a string value object into the current dragged part indicating for who it belongs, or even better using the Collection Service

13 Likes

Woah! Amazing tutorial! One question though:

How do we make it so when the dragged item is mid air it slowly spins around in there direction we move it in. For example, if we move it to the left in mid-air,
instead of it standing still, how do we make it move to the direction is being dragged to.

4 Likes

Thank you a lot!

As you can see, I ended up using BodyPositions to move the parts, and BodyPositions don’t have a CFrame property, just a Position property. So you can’t control the direction/rotation/ that much! So if you wanted to take count of direction, you can use tweening instead, and tween the cframe of the targeted part to the mouse’s cframe, which should take care of the direction.

4 Likes

Thanks a lot, I was currently looking about this. Good job.

6 Likes

Ty a lot! I’m gald it helped you

3 Likes

In that case, couldn’t you use BodyPosition + Gyro to be able to adjust the CFrame?

3 Likes

Well you’re actually very correct! Nice noticing that. The BodyGyro was just used to make the part always perpendicular and would always stand still, because most of the time it was freaking it out when I dragged it. But I guess you can utilise it that way

4 Likes

Update: did some tweaks on the article, all the way from grammar fixes to wording things differentley

3 Likes

Understandable and comprehensive; pretty nice tutorial

5 Likes

Thank you a lot for the encouraging feedback!

4 Likes

Thanks for this tutorial and the insight on unit rays! I was getting a bit confused on units back a few months ago when I first heard mention of them, but this is a great explanation honestly.

The fact that the mouse’s origin is the camera completely dazzles me too. I used to struggle a lot with these back then.

3 Likes

Thank you a lot! I’m glad that I helped you fix the ideas you had in mind. Personally, this article did way better than I though. Always happy to help!

Not trying to be selfish, but I think I really out-did myself with the unit explanation!

2 Likes

This is helpful but lumber tycoon 2 actually does not put the BodyPosition and the BodyGyro into the target itself. They are located in the circle cursor with the particles which is welded to the object that is being dragged. This allows the object to be moved around depending on its angle. So for example, if you were to drag the object it would not reorient itself and rather stay in the same orientation when you begin dragging.

3 Likes

i spent months wondering what unit is until i found this tutorial! and now i fully understand what unit is! Big Thanks to @starmaq

2 Likes

I didn’t actually know that, ty for the info! The purpose of this article wasn’t really to show how to make a replica of the lumber tycoon dragging system, I just took that system as a headstart, the main goal of this article is to salvage some of the given ideas, such as usage of unit and problem with origins. Thank you again!

2 Likes

Thank you a lot! I’m gald I helped, I always see people asking about .Unit, it might be confusing at first but after you think logically, and give some good visualisations on things (like that gif) you get a good picture of things!

3 Likes

at one time there was an open source copy of LT2 (its deleted now) but what defaultio did was create it was either a string or object value and put it on the model/part and put the name of the user or the actual player and then just check if the part is being dragged by that player by seeing how close it is to them

2 Likes

I gave it a try it’s a little buggy but it points you in the right direction.

4 Likes