Simple Part Placement Tutorial for Beginners (WITHOUT RAYCASTING)
Hi there! I created a simple placement tool and I wanted to share it with beginners who want to make something like this. I know some my friends don’t know a lot about raycasting so I decided to make it like this for any beginner scripter who doesn’t know how to raycast reading this. Anyways, let’s begin!
Detecting Tool Activation
How to set the part’s position properly
Final touches
Conclusion
BONUS CHAPTER
Detecting Tool Activation
First, we’ll create a tool. Place it in starter pack. Now, we’ll add a local script. I’ll explain everything, so don’t worry if it gets confusing. Now, type this:
script.Parent.Equipped:Connect(function(mouse)
mouse.Button1Down:Connect(function()
end)
end)
By now you should have realized that it checks if the tool is equipped. Then it takes the player’s mouse and connects a function to the Button1Down
event. If you’ve been scripting for a while and have been looking at the API reference for a while, you might be wondering, "Why not use the Activated event instead? It still checks if you click the button while it’s equipped!” Well, it takes no mouse parameter and the mouse will be very useful later on, so we’ll leave out that event. Now that we detected the tool activation, we can add the part and set it’s anchor!
script.Parent.Equipped:Connect(function(mouse)
mouse.Button1Down:Connect(function()
local newpart = Instance.new("Part")
newpart.Parent = game.Workspace
newpart.Anchored = true
end)
end)
Now that we’re done with this, it’s time to start
How to set the part’s position properly
This is where it gets a bit harder. To start, let’s set the part’s position to the mouse’s position:
newpart.Position = mouse.Hit.p --Without p, it would be a CFrame instead of a Vector3, but since Vector3 is simpler and doesn’t affect the rotation, we'll use the Vector3 position.
Sadly we are not done yet. Every time you use this it sets the Y coordinate to 0 all the time. If you don’t want to fix this, skip until the conclusion. Now, I have a formula for getting the correct Y position of a part (at least if you want to place it on the top of any surface), and here it is:
local target = mouse.Target
newpart.Position = Vector3.new(part.Position.X, ((target.Position.Y + target.Size.Y/2) + newpart.Size.Y/2), part.Position.Z)
The X and Z values are not affected because right now we are fixing the Y coordinate. I’ll explain this with pictures to make it simpler:
In the first picture directly below this text, you can see the ‘target.Position.Y’ part of the formula:
In this 2nd picture, I added half the Y axis size of the target, meaning it goes up…
…until you remember that the position of the part is set to the pivot (which is defaulted to the center of the part). Which is why we add half of the new part’s size to compensate for the bottom part stuck though the target:
Now that we’re done we’ve come to the
Final Touches
Are we done yet? Of course not! A proper placement script has to correctly place on every surface, right? So, we just copy-paste the formula, but plug it in for the X and Z axis, depending on where you want it. To start, we need to detect which surface the mouse is targeting so we can have a correct placement system. So, we do this:
if mouse.TargetSurface == Enum.NormalId.PLACEHOLDER then —Replace the placeholder with the surface that should be detected
—The copy-pasted code
end
This checks which surface is detected. Now that we have all the cleared up, it’s time for the important note:
NOTE: Since there are opposites for each surface (Top and Bottom, Left and Right, Front and Back) you need to switch the + with - at times. This is a tutorial and not a place where you simply copy-paste the final script, so I’ll leave you to figure out when to place the + and the - as a challenge. Good luck!
Ready for the answer? Check it below:
Answer
if mouse.TargetSurface == Enum.NormalId.Top then --Top
newpart.Position = Vector3.new(newpart.Position.X, (mouse.Target.Position.Y +(mouse.Target.Size.Y/2)) + newpart.Size.Y/2, newpart.Position.Z)
elseif mouse.TargetSurface == Enum.NormalId.Bottom then --Bottom
newpart.Position = Vector3.new(newpart.Position.X, (mouse.Target.Position.Y - (mouse.Target.Size.Y/2)) - newpart.Size.Y/2, newpart.Position.Z)
elseif mouse.TargetSurface == Enum.NormalId.Right then --Right
newpart.Position = Vector3.new((mouse.Target.Position.X + (mouse.Target.Size.X/2)) + newpart.Size.X/2, newpart.Position.Y, newpart.Position.Z)
elseif mouse.TargetSurface == Enum.NormalId.Left then --Left
newpart.Position = Vector3.new((mouse.Target.Position.X - (mouse.Target.Size.X/2)) - newpart.Size.X/2, newpart.Position.Y, newpart.Position.Z)
elseif mouse.TargetSurface == Enum.NormalId.Back then --Back
newpart.Position = Vector3.new(newpart.Position.X, newpart.Position.Y, (mouse.Target.Position.Z + (mouse.Target.Size.Z/2)) + newpart.Size.Z/2)
elseif mouse.TargetSurface == Enum.NormalId.Front then --Front
newpart.Position = Vector3.new(newpart.Position.X, newpart.Position.Y, (mouse.Target.Position.Z - (mouse.Target.Size.Z/2)) - newpart.Size.Z/2)
end
Now that we set everything up, we can show everyone what you made! If your game is for single player or whatever, skip this part. Now, we have to create a remote event in replicated storage. For this example, I’ll name it ‘BuildEvent’. Now, add this in your code:
game.ReplicatedStorage.BuildEvent:FireServer(mouse.Hit.p, mouse.Target, mouse.TargetSurface)
This will fire the event and pass these arguments to use. Now, we go to server script service and add a script. Paste this in:
game.ReplicatedStorage.BuildEvent.OnServerEvent:Connect(function(plr, hit, target, surface)
end)
The player is for the player parameter (which we don’t need), and the hit, target, and surface parameters are from the arguments we placed earlier. Now, we take the Instance.new, the properties setting, and the position setting out the local script and right in the connected function of the server script! We’re done!
Conclusion
You have reached the conclusion of this tutorial without even seeing me use raycasting in the tutorial! Congratulations! Now that we finished it up, you can edit the script to make it have the feel of your game plot.
The single-player script:
script.Parent.Equipped:Connect(function(mouse)
mouse.Button1Down:Connect(function()
local part = Instance.new(“Part”)
local target = mouse.Target
part.Parent = game.Workspace
part.Anchored = true
part.Position = mouse.Hit.p
if mouse.TargetSurface == Enum.NormalId.Top then --Top
newpart.Position = Vector3.new(newpart.Position.X, (mouse.Target.Position.Y + (mouse.Target.Size.Y/2)) + newpart.Size.Y/2, newpart.Position.Z)
elseif mouse.TargetSurface == Enum.NormalId.Bottom then --Bottom
newpart.Position = Vector3.new(newpart.Position.X, (mouse.Target.Position.Y -(mouse.Target.Size.Y/2)) - newpart.Size.Y/2, newpart.Position.Z)
elseif mouse.TargetSurface == Enum.NormalId.Right then --Right
newpart.Position = Vector3.new((mouse.Target.Position.X + (mouse.Target.Size.X/2)) + newpart.Size.X/2, newpart.Position.Y, newpart.Position.Z)
elseif mouse.TargetSurface == Enum.NormalId.Left then --Left
newpart.Position = Vector3.new((mouse.Target.Position.X - (mouse.Target.Size.X/2)) - newpart.Size.X/2, newpart.Position.Y, newpart.Position.Z)
elseif mouse.TargetSurface == Enum.NormalId.Back then --Back
newpart.Position = Vector3.new(newpart.Position.X, newpart.Position.Y, (mouse.Target.Position.Z + (mouse.Target.Size.Z/2)) + newpart.Size.Z/2)
elseif mouse.TargetSurface == Enum.NormalId.Front then --Front
newpart.Position = Vector3.new(newpart.Position.X, newpart.Position.Y, (mouse.Target.Position.Z - (mouse.Target.Size.Z/2)) - newpart.Size.Z/2)
end
end)
end)
BONUS CHAPTER
You’ve finished the tutorial, but are you ready for the bonus chapter? Let’s make it so that we make the part face the character who placed it if it’s placed on the top of the mouse’s target!
We’ll edit this part of the script:
if mouse.TargetSurface == Enum.NormalId.Top then
newpart.Position = Vector3.new(newpart.Position.X, (mouse.Target.Position.Y + (mouse.Target.Size.Y/2)) + newpart.Size.Y/2, newpart.Position.Z)
This is pretty simple. First we get the player and the character:
local player = game.Players.LocalPlayer
local char = player.CharacterAdded:Wait()
And now to add the torso! (If your game is R15, change this to Upper Torso)
local torso = char:WaitForChild("Torso")
For the looking at the character part of the script, simply add this:
newpart.CFrame = CFrame.lookAt(newpart.Position, torso.Position, newpart.Position.Y)
This makes the part’s CFrame look at the torso’s position, on the part’s Y coordinate!
if mouse.TargetSurface == Enum.NormalId.Top then
newpart.Position = Vector3.new(newpart.Position.X, (mouse.Target.Position.Y + (mouse.Target.Size.Y/2)) + newpart.Size.Y/2, newpart.Position.Z)
local player = game.Players.LocalPlayer
local char = player.CharacterAdded:Wait()
local torso = char:WaitForChild("Torso")
newpart.CFrame = CFrame.lookAt(newpart.Position, torso.Position, newpart.Position.Y)
If you play around with the part size, you might just create the simpler version of trowel tool in Super Doomspire ! Please put your feedback in the replies section so I can improve this tutorial! Thanks!