So I’ve been trying to prevent this but it doesn’t seem to work.
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Modules = ReplicatedStorage:WaitForChild("Modules")
local Remotes = ReplicatedStorage:WaitForChild("Remotes")
local ItemsData = require(Modules:WaitForChild("ItemsData"))
local tool = script.Parent
local itemData = ItemsData.GetBlocksData(tool.Name)
local currentCamera = workspace.CurrentCamera
local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local mouse = player:GetMouse()
local BlocksFolder = workspace:WaitForChild("BlocksFolder")
local equipped = false
local maxPlaceBlockRange = itemData.PlaceRange
local displayBlockConnection
local blockPrediction = false
local displayBlock = Instance.new("Part")
displayBlock.Size = Vector3.new(3, 3, 3)
displayBlock.Anchored = true
displayBlock.CanCollide = false
displayBlock.Transparency = 1
displayBlock.Parent = workspace
local selectionBox = Instance.new("SelectionBox")
selectionBox.LineThickness = 0.05
selectionBox.Color3 = Color3.fromRGB(0, 0, 0)
selectionBox.Adornee = displayBlock
selectionBox.Parent = displayBlock
local closestSelectionBox = Instance.new("SelectionBox")
closestSelectionBox.LineThickness = 0.05
closestSelectionBox.Color3 = Color3.fromRGB(255, 0, 0)
local currentClosestPart = nil
local placementCFrame = nil
local obstruction = false
mouse.TargetFilter = displayBlock
local function setDisplayBlockConnectionNil()
if displayBlockConnection then
displayBlockConnection:Disconnect()
displayBlockConnection = nil
end
end
local function findClosestPart()
local currentPart = nil
local currentClosestDir = nil
local currentClosestMag = nil
local mouseRay = currentCamera:ScreenPointToRay(mouse.X, mouse.Y)
for i,v in ipairs(BlocksFolder:GetDescendants()) do
if v:IsA("BasePart") then
local mouse3DPos = mouseRay:ClosestPoint(v.Position)
local magnitude = (v.Position - mouse3DPos).Magnitude
if not currentClosestMag then
currentClosestMag = magnitude
currentPart = v
elseif magnitude < currentClosestMag then
currentClosestMag = magnitude
currentPart = v
end
end
end
if currentPart then
local surfaceOffset = Vector3.new(25, 25, 25) -- Offset to determine surface positions
local surfaceIDs = {
Left = currentPart.Position - Vector3.new(surfaceOffset.X, 0, 0),
Right = currentPart.Position + Vector3.new(surfaceOffset.X, 0, 0),
Front = currentPart.Position - Vector3.new(0, 0, surfaceOffset.Z),
Back = currentPart.Position + Vector3.new(0, 0, surfaceOffset.Z),
}
if mouse.Target == nil then
local closestMag = nil
local rayDir = mouseRay.Direction
local rayOrigin = mouseRay.Origin
for surfaceId, pos in pairs(surfaceIDs) do
local dir = (pos - rayOrigin).Unit
local magnitude = (rayDir - dir).Magnitude
if not closestMag then
closestMag = magnitude
currentClosestDir = surfaceId
elseif magnitude < closestMag then
closestMag = magnitude
currentClosestDir = surfaceId
end
end
else
local mouse3DPos = mouseRay:ClosestPoint(currentPart.Position)
local closestMag = nil
for surfaceId, pos in pairs(surfaceIDs) do
local magnitude = (mouse3DPos - pos).Magnitude
if not closestMag then
closestMag = magnitude
currentClosestDir = surfaceId
elseif magnitude < closestMag then
closestMag = magnitude
currentClosestDir = surfaceId
end
end
end
if currentClosestDir then
return currentPart, currentClosestDir
end
end
end
local function display()
local mouseTarget = mouse.TargetSurface
local HumanoidRootPart = character:FindFirstChild("HumanoidRootPart") or character.PrimaryPart
if HumanoidRootPart then
if mouse.Target then
if (mouse.Target.Position - HumanoidRootPart.Position).Magnitude <= maxPlaceBlockRange then
blockPrediction = false
displayBlock.CFrame = CFrame.new(mouse.Target.Position)
local newPlacementCFrame = nil
if mouseTarget == Enum.NormalId.Top then
newPlacementCFrame = CFrame.new(mouse.Target.Position + Vector3.new(0, 3, 0))
elseif mouseTarget == Enum.NormalId.Bottom then
newPlacementCFrame = CFrame.new(mouse.Target.Position - Vector3.new(0, 3, 0))
elseif mouseTarget == Enum.NormalId.Left then
newPlacementCFrame = CFrame.new(mouse.Target.Position - Vector3.new(3, 0, 0))
elseif mouseTarget == Enum.NormalId.Right then
newPlacementCFrame = CFrame.new(mouse.Target.Position + Vector3.new(3, 0, 0))
elseif mouseTarget == Enum.NormalId.Front then
newPlacementCFrame = CFrame.new(mouse.Target.Position - Vector3.new(0, 0, 3))
elseif mouseTarget == Enum.NormalId.Back then
newPlacementCFrame = CFrame.new(mouse.Target.Position + Vector3.new(0, 0, 3))
end
placementCFrame = newPlacementCFrame
else
local closestPart, direction = findClosestPart()
local offsetVector = Vector3.new()
blockPrediction = true
currentClosestPart = closestPart
if closestPart and direction then
if tostring(direction) == "Left" then
offsetVector = Vector3.new(-3, 0, 0)
elseif tostring(direction) == "Right" then
offsetVector = Vector3.new(3, 0, 0)
elseif tostring(direction) == "Front" then
offsetVector = Vector3.new(0, 0, -3)
elseif tostring(direction) == "Back" then
offsetVector = Vector3.new(0, 0, 3)
end
local targetPos = closestPart.Position + offsetVector
displayBlock.CFrame = CFrame.new(targetPos)
placementCFrame = displayBlock.CFrame
closestPart = nil
direction = nil
end
end
else
local closestPart, direction = findClosestPart()
local offsetVector = Vector3.new()
blockPrediction = true
currentClosestPart = closestPart
if closestPart and direction then
if tostring(direction) == "Left" then
offsetVector = Vector3.new(-3, 0, 0)
elseif tostring(direction) == "Right" then
offsetVector = Vector3.new(3, 0, 0)
elseif tostring(direction) == "Front" then
offsetVector = Vector3.new(0, 0, -3)
elseif tostring(direction) == "Back" then
offsetVector = Vector3.new(0, 0, 3)
end
local targetPos = closestPart.Position + offsetVector
displayBlock.CFrame = CFrame.new(targetPos)
placementCFrame = displayBlock.CFrame
closestPart = nil
direction = nil
end
end
end
end
local function displayBlockPlacement()
displayBlockConnection = RunService.Heartbeat:Connect(function()
display()
local HumanoidRootPart = character:FindFirstChild("HumanoidRootPart") or character.PrimaryPart
if currentClosestPart and placementCFrame and HumanoidRootPart and (placementCFrame.Position - HumanoidRootPart.Position).Magnitude <= maxPlaceBlockRange then
obstruction = false
closestSelectionBox.Adornee = currentClosestPart
closestSelectionBox.Parent = currentClosestPart
for i,v in ipairs(BlocksFolder:GetDescendants()) do
if v:IsA("BasePart") then
if v.Position == placementCFrame.Position or (blockPrediction and v.Position == currentClosestPart.Position) or (blockPrediction and displayBlock.Position == v.Position) then
--obstruction = true
--print("Block obstruction detected.")
break
end
end
end
else
--obstruction = true
end
if obstruction then
selectionBox.Visible = false
else
selectionBox.Visible = true
end
end)
end
Video:
The black selection box is the predicted position and the red selection box is the closest part.