How do I add collisions to my move tool?

Right now I’m working on making a move tool similar to that of classic Roblox BTools. It is supposed to be able to drag parts around with your mouse as long as the tool is equipped and the part/union is within a 25 stud radius of the player’s character.

Right now it works, except the parts clip into whatever they are being moved against as their position is being set to the part that hits with the thing they are touching. How should I fix this?

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!

  2. What is the issue? Include screenshots / videos if possible!

  3. What solutions have you tried so far? Did you look for solutions on the Developer Hub?

After that, you should include more details if you have any. Try to make your topic as descriptive as possible, so that it’s easier for people to help you!

Local Script in the tool:

local Players = game:GetService("Players")
local UIP = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local runService = game:GetService("RunService")

local Event = ReplicatedStorage:WaitForChild("Events"):WaitForChild("MoveTool")

local Player = Players.LocalPlayer
local Mouse = Player:GetMouse()

local Highlight = Player.PlayerScripts:FindFirstChild("HoverHighlight")
local OutHover = Color3.fromRGB(0, 145, 255)
local OutSelect = Color3.fromRGB(165, 255, 255)

local Target
local Distance
local Furthest = 25
local MouseButton1Down = false

local function getMouseTargetIgnoring(ignoreParts)
	local unitRay = Mouse.UnitRay
	local ray = Ray.new(unitRay.Origin, unitRay.Direction * 1000)
	local hitPart, hitPosition = workspace:FindPartOnRayWithIgnoreList(ray, ignoreParts)
	return hitPart, hitPosition
end

runService.Heartbeat:Connect(function()
	local ignoreParts = {Target, Player.Character}
	local hitPart, hitPosition = getMouseTargetIgnoring(ignoreParts)

	if hitPart and not hitPart.Locked and script.Parent.Parent == Player.Character then
		Distance = (hitPart.Position - Player.Character.HumanoidRootPart.Position).Magnitude
		if Distance <= Furthest then
			Highlight.Adornee = hitPart
		else
			Highlight.Adornee = nil
		end
	else
		Highlight.Adornee = nil
	end

	if MouseButton1Down and Target then
		if Distance <= Furthest then
			Event:FireServer(Target, "Move", hitPosition)
		end
	end
end)

UIP.InputBegan:Connect(function(Input)
	if Mouse.Target and not Mouse.Target.Locked and script.Parent.Parent == Player.Character then
		if Input.KeyCode == Enum.KeyCode.R and Target then
			Event:FireServer(Target, "RotateX")
		elseif Input.KeyCode == Enum.KeyCode.Y and Target then
			Event:FireServer(Target, "RotateY")
		elseif Input.KeyCode == Enum.KeyCode.T and Target then
			Event:FireServer(Target, "RotateZ")
		end

		if Input.UserInputType == Enum.UserInputType.MouseButton1 then
			MouseButton1Down = true
			Target = Mouse.Target
			Highlight.OutlineColor = OutSelect
		end
	end
end)

UIP.InputEnded:Connect(function(Input)
	if Input.UserInputType == Enum.UserInputType.MouseButton1 then
		MouseButton1Down = false
		Target = nil
		Highlight.OutlineColor = OutHover
	end
end)

Server Script for the event:

local Event = game.ReplicatedStorage.Events.MoveTool

Event.OnServerEvent:Connect(function(User, Target, Action, Position)
	print("Event Recieved")
	if Action == "RotateX" then
		Target.Orientation += Vector3.new(0,90,0)
	elseif Action == "RotateY" then
		Target.Orientation += Vector3.new(0,0,90)	
	elseif Action == "RotateZ" then
		Target.Orientation += Vector3.new(90,0,0)	
	elseif Action == "Move" then
		Target.Position = Position
		--Target.Anchored = true
	elseif Action == "Unanchor" then
		Target.Anchored = false
	end
end)

You can use Roblox’s Region3 or raycasting to check for collisions before moving the part.

Here’s an example of collision detection using raycasting:

local Event = game.ReplicatedStorage.Events.MoveTool

Event.OnServerEvent:Connect(function(User, Target, Action, Position)
    print("Event Recieved")
    if Action == "RotateX" then
        Target.Orientation += Vector3.new(0, 90, 0)
    elseif Action == "RotateY" then
        Target.Orientation += Vector3.new(0, 0, 90)
    elseif Action == "RotateZ" then
        Target.Orientation += Vector3.new(90, 0, 0)
    elseif Action == "Move" then
        local ray = Ray.new(Position, Vector3.new(0, -1, 0))
        local hitPart = workspace:FindPartOnRay(ray, Target)
        
        if not hitPart then
            Target.Position = Position
        else
            print("Collision detected, cannot move part.")
        end
    elseif Action == "Unanchor" then
        Target.Anchored = false
    end
end)

This (arguably basic) example uses a downward raycast to check for collisions before setting the new position. You may need to adjust the ray direction and length based on your requirements.