local RunService = game:GetService("RunService")
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local tool = script.Parent
local SelectionPart = script:WaitForChild("SelectionPart")
local SelectionBox = script:WaitForChild("SelectionBox")
local gridSize = 3
local connection = nil
local surfaceData = {
["Front"] = Vector3.new(0, 0, 1),
["Back"] = Vector3.new(0, 0, -1),
["Top"] = Vector3.new(0, 1, 0),
["Bottom"] = Vector3.new(0, -1, 0),
["Left"] = Vector3.new(-1, 0, 0),
["Right"] = Vector3.new(1, 0, 0),
}
local function Unequip()
if connection then
connection:Disconnect()
connection = nil
end
SelectionBox.Visible = false
end
local function Equip()
SelectionBox.Visible = true
connection = RunService.Heartbeat:Connect(function()
local mouseTarget = mouse.Target
local mousePos = mouse.Hit.Position
if mouseTarget then
local targetSurface = mouse.TargetSurface
local surface = surfaceData[targetSurface.Name]
if surface then
local targetPos = mouseTarget.Position
local targetSize = mouseTarget.Size
local offset = targetPos + (targetSize / 2 * surface) + (Vector3.one * gridSize / 2 * surface)
local position = mousePos + offset
SelectionPart.Position = position
end
end
end)
end
tool.Equipped:Connect(function()
Equip()
end)
tool.Unequipped:Connect(function()
Unequip()
end)
local RunService = game:GetService("RunService")
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local tool = script.Parent
local SelectionPart = script:WaitForChild("SelectionPart")
local SelectionBox = script:WaitForChild("SelectionBox")
local gridSize = 3
local connection = nil
local surfaceData = {
["Front"] = Vector3.new(0, 0, -1),
["Back"] = Vector3.new(0, 0, 1),
["Top"] = Vector3.new(0, 1, 0),
["Bottom"] = Vector3.new(0, -1, 0),
["Left"] = Vector3.new(-1, 0, 0),
["Right"] = Vector3.new(1, 0, 0),
}
local function Unequip()
if connection then
connection:Disconnect()
connection = nil
end
SelectionBox.Visible = false
end
local function SnapToGrid(x)
return math.floor(x / gridSize) * gridSize
end
local function Equip()
SelectionBox.Visible = true
connection = RunService.Heartbeat:Connect(function()
local mouseTarget = mouse.Target
local mousePos = mouse.Hit.Position
if mouseTarget then
local targetSurface = mouse.TargetSurface
local surface = surfaceData[targetSurface.Name]
if surface then
local targetPos = mouseTarget.Position
local targetSize = mouseTarget.Size
local position = mousePos + (Vector3.one * gridSize / 2 * surface)
position = Vector3.new(SnapToGrid(position.X), SnapToGrid(position.Y), SnapToGrid(position.Z))
SelectionPart.Position = position
end
end
end)
end
tool.Equipped:Connect(function()
Equip()
end)
tool.Unequipped:Connect(function()
Unequip()
end)
It works perfectly without the grid but adding the grid ruins the positioning
You can make a targetPosition that offsets the mouse.Hit.Position by the Y value divided by 2 of the part, then set the part’s position to the targetPosition.
You could also put the part into a model and use Model:MoveTo() which automatically avoids collisions, but it always resets the orientation of the model.