My system will not snap and the preview disappears a second after I place it
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local replicatedStorage = game:GetService("ReplicatedStorage")
local userInput = game:GetService("UserInputService")
local runService = game:GetService("RunService")
local placementFolder = workspace:FindFirstChild("GameMechanics").PlacementFolder
-- Settings
local currentPrefab = replicatedStorage.GameMechanics.Prefab.Wall
local snappingEnabled = true
local buildingEnabled = false
local placementObject
local function createPlacementGhost()
placementObject = currentPrefab:Clone()
placementObject.Parent = workspace
placementObject.PrimaryPart = placementObject:FindFirstChild("PlacementPart") -- Ensure PlacementPart is PrimaryPart
for _, part in ipairs(placementObject:GetDescendants()) do
if part:IsA("BasePart") then
part.Transparency = 0.5
part.CanCollide = false
end
end
end
local function getPlacementPart(model)
if model and model:IsA("Model") then
return model:FindFirstChild("PlacementPart")
end
return nil
end
local function findNearestAttachment(position, parentModel)
local closestAttachment = nil
local closestDistance = math.huge
local placementPart = getPlacementPart(parentModel)
if not placementPart then return nil end
for _, attachment in pairs(placementPart:GetChildren()) do
if attachment:IsA("Attachment") then
local distance = (position - attachment.WorldPosition).Magnitude
if distance < closestDistance then
closestDistance = distance
closestAttachment = attachment
end
end
end
return closestAttachment
end
local function updatePlacement()
if not placementObject or not buildingEnabled then return end
local mouseRay = workspace.CurrentCamera:ScreenPointToRay(mouse.X, mouse.Y)
local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {placementObject} -- Ignore ghost object
raycastParams.FilterType = Enum.RaycastFilterType.Exclude
local rayResult = workspace:Raycast(mouseRay.Origin, mouseRay.Direction * 100, raycastParams)
local targetPosition = rayResult and rayResult.Position or mouse.Hit.Position
local ghostPlacementPart = getPlacementPart(placementObject)
if not ghostPlacementPart then return end
if snappingEnabled then
local nearestAttachment = nil
local closestDistance = math.huge
for _, placedPrefab in pairs(placementFolder:GetChildren()) do
local candidateAttachment = findNearestAttachment(targetPosition, placedPrefab)
if candidateAttachment then
local distance = (targetPosition - candidateAttachment.WorldPosition).Magnitude
if distance < closestDistance then
closestDistance = distance
nearestAttachment = candidateAttachment
end
end
end
local ghostAttachment = findNearestAttachment(targetPosition, placementObject)
if nearestAttachment and ghostAttachment then
local offset = ghostAttachment.CFrame:Inverse() * ghostPlacementPart.CFrame
local newCFrame = nearestAttachment.WorldCFrame * offset
placementObject:SetPrimaryPartCFrame(newCFrame)
return
end
end
placementObject:SetPrimaryPartCFrame(CFrame.new(targetPosition))
end
local function placeObject()
if placementObject and buildingEnabled then
local newObj = currentPrefab:Clone()
newObj:SetPrimaryPartCFrame(placementObject.PrimaryPart.CFrame)
newObj.Parent = placementFolder
placementObject:Destroy()
placementObject = nil
createPlacementGhost()
end
end
local tweenService = game:GetService("TweenService")
local playerGui = game.Players.LocalPlayer:WaitForChild("PlayerGui")
local notificationTemplate = replicatedStorage.GameMechanics.GUI:WaitForChild("Notification") -- Your notification GUI
local maxNotifications = 5
local activeNotifications = {}
local function showNotification(text)
local Cloned = notificationTemplate:Clone()
Cloned.Parent = playerGui
Cloned.Enabled = true
Cloned.Frame.Frame.TextLabel.Text = text
local baseYPosition = 0.3 -- Where the first notification starts (adjustable)
local spacing = 0.07 -- Spacing between notifications
local limit = maxNotifications * spacing -- Prevents going too low
if #activeNotifications >= maxNotifications then
local oldest = table.remove(activeNotifications, 1)
oldest:Destroy()
end
local offset = #activeNotifications * spacing
if offset > limit then offset = limit end
Cloned.Frame.Position = UDim2.new(1, 50, baseYPosition + offset, 0) -- Start off-screen
-- Tween into view
local tweenIn = tweenService:Create(Cloned.Frame, TweenInfo.new(0.5, Enum.EasingStyle.Quad, Enum.EasingDirection.Out), {
Position = UDim2.new(0.8, 0, baseYPosition + offset, 0) -- Slide into view
})
tweenIn:Play()
table.insert(activeNotifications, Cloned)
task.delay(3, function()
local tweenOut = tweenService:Create(Cloned.Frame, TweenInfo.new(0.5, Enum.EasingStyle.Quad, Enum.EasingDirection.In), {
Position = UDim2.new(1, 50, baseYPosition + offset, 0) -- Slide out to the right
})
tweenOut:Play()
tweenOut.Completed:Wait()
Cloned:Destroy()
end)
end
-- Example usage (when toggling snapping)
local function toggleSnapping()
snappingEnabled = not snappingEnabled
showNotification("Snapping " .. (snappingEnabled and "Enabled" or "Disabled"))
end
local istoggle = false
local toggled = false
-- Toggle Build Mode
local function toggleBuildMode()
buildingEnabled = not buildingEnabled
if buildingEnabled then
if placementObject then
placementObject:Destroy()
end
createPlacementGhost()
istoggle = true
else
if placementObject then
placementObject:Destroy()
placementObject = nil
end
istoggle = false
end
end
-- Listen for keybinds
userInput.InputBegan:Connect(function(input)
if input.KeyCode == Enum.KeyCode.B then
if toggled == false then
toggled = true
toggleBuildMode()
else
toggled = false
toggleBuildMode()
end
end
end)
userInput.InputBegan:Connect(function(input)
if input.KeyCode == Enum.KeyCode.T then
if buildingEnabled then -- Directly check if in build mode
toggleSnapping()
else
showNotification("Can't toggle snapping outside of build mode.")
end
end
end)
runService.RenderStepped:Connect(updatePlacement)
mouse.Button1Down:Connect(placeObject)
its client sided on purpose to make sure placement is correct.