Placement System is unreliable

I’m trying to make simple wall placement system for my game, but sometimes the walls would spawn underneath the baseplate. With further explanation I realized that the mouse.Hit in the below script is sometimes in the negative Y coordinates (which is not what I want). How can I properly place the wall without it noclipping underground? Note: The issue only happens 50% of the time I attempt to build, and sometimes it spawns halfway aboveground.

Local Script:

local folder = game.ReplicatedStorage:WaitForChild("WallFolder")
local plr = game.Players.LocalPlayer
local wall = folder:WaitForChild("Wall")
local preview = folder:WaitForChild("Preview")
local context = game:GetService("ContextActionService")
local runservice = game:GetService("RunService")
local mouse = plr:GetMouse()
local onbuild = game.ReplicatedStorage:WaitForChild("OnBuild")
local isplacingobj = false
local humanoidrootpart = plr.Character:WaitForChild("HumanoidRootPart")


local function onT ()
	if not isplacingobj then
	isplacingobj = true
	local pclone = preview:Clone()
		pclone.Parent = workspace
		local rootpart = pclone.RootPart
		pclone.PrimaryPart = rootpart
		runservice.RenderStepped:Connect(function()
			if isplacingobj then
			mouse.TargetFilter = pclone
			local rawposition = CFrame.new(mouse.Hit.Position.X,mouse.Hit.Position.Y +rootpart.Size.Y/2, mouse.Hit.Position.Z) * CFrame.Angles(math.rad(0),math.rad(humanoidrootpart.Orientation.Y), math.rad(0))
			pclone:PivotTo(rawposition)
			end
		end)
		
		mouse.Button1Down:Connect(function()
			if isplacingobj then isplacingobj = false
				print(mouse.Hit)
			onbuild:FireServer(pclone.RootPart.CFrame)
			pclone:Destroy()
		end	
	end)
			
	end	
end

context:BindAction("Tpressed", onT, false, Enum.KeyCode.T)

Server Side Code:

local repstorage = game:GetService("ReplicatedStorage")
local onbuild = repstorage.OnBuild
local wall = repstorage.WallFolder.Wall

onbuild.OnServerEvent:Connect(function(plr, cframe)
	local clone = wall:Clone()
	clone.Parent = workspace
	clone:PivotTo(cframe)
end)
```lua

Does the preview part appear in the correct position?

Can you share the server side code?

Sorry, I accidentally marked yours as the solution. The primary part is a transparent part that covers the entire wall, right now the wall is a single part in a model for testing purposes.

I can’t get the server side code at the moment but it shouldn’t be necessary.

Does pclone appear in the rightposition and it’s just the final party that’s messed up?

And I ask for the server code because this looks fine, but it’s the server that’s doing the actual placement:

I added the server code to the original post.

This printed the exact position where the finished wall was placed, underground or not.

Edit: Just found out that wasn’t true. The wall is 9 studs below mouse.hit.position result

ServerSided Script:

local function GetNearestPointOnRay(origin, direction, point)
    local ray = Ray.new(origin, direction)
    local _, hitPosition = workspace:FindPartOnRay(ray)
    return (hitPosition - origin).magnitude < (point - origin).magnitude and hitPosition or point
end

local function GetMousePositionOnRay(origin, direction)
    local ray = Ray.new(origin, direction)
    local _, hitPosition = workspace:FindPartOnRay(ray)
    return hitPosition
end

local function onT()
    if not isplacingobj then
        isplacingobj = true

        local pclone = preview:Clone()
        pclone.Parent = workspace

        local rootpart = pclone.RootPart
        pclone.PrimaryPart = rootpart

        local mouseTarget = nil

        pclone.ChildAdded:Connect(function(child)
            if child:IsA("BasePart") then
                mouseTarget = child
            end
        end)

        local renderSteppedConnection = nil

        local function updatePreview()
            if isplacingobj then
                local mousePosition = GetMousePositionOnRay(rootpart.Position, camera.CFrame.lookVector)
                local mousePoint = GetNearestPointOnRay(rootpart.Position, camera.CFrame.lookVector, mousePosition)
                local rawposition = CFrame.new(mousePoint.X, mousePoint.Y + rootpart.Size.Y/2, mousePoint.Z) * CFrame.Angles(math.rad(0), math.rad(humanoidrootpart.Orientation.Y), math.rad(0))
                pclone:PivotTo(rawposition)
            else
                renderSteppedConnection:Disconnect()
                pclone:Destroy()
            end
        end

        renderSteppedConnection = RunService.RenderStepped:Connect(updatePreview)
        updatePreview()

        mouse.Button1Down:Connect(function()
            if isplacingobj then
                isplacingobj = false
                print(mouseTarget.CFrame)
                onbuild:FireServer(mouseTarget.CFrame)
            end
        end)
    end
end

Local Code:

local function GetNearestPointOnRay(origin, direction, point)
    local ray = Ray.new(origin, direction)
    local _, hitPosition = workspace:FindPartOnRay(ray)
    return (hitPosition - origin).magnitude < (point - origin).magnitude and hitPosition or point
end

local function GetMousePositionOnRay(origin, direction)
    local ray = Ray.new(origin, direction)
    local _, hitPosition = workspace:FindPartOnRay(ray)
    return hitPosition
end

local function onT()
    if not isplacingobj then
        isplacingobj = true

        local pclone = preview:Clone()
        pclone.Parent = workspace

        local rootpart = pclone.RootPart
        pclone.PrimaryPart = rootpart

        local mouseTarget = nil

        pclone.ChildAdded:Connect(function(child)
            if child:IsA("BasePart") then
                mouseTarget = child
            end
        end)

        local renderSteppedConnection = nil

        local function updatePreview()
            if isplacingobj then
                local mousePosition = GetMousePositionOnRay(rootpart.Position, camera.CFrame.lookVector)
                local mousePoint = GetNearestPointOnRay(rootpart.Position, camera.CFrame.lookVector, mousePosition)
                local rawposition = CFrame.new(mousePoint.X, mousePoint.Y + rootpart.Size.Y/2, mousePoint.Z) * CFrame.Angles(math.rad(0), math.rad(humanoidrootpart.Orientation.Y), math.rad(0))
                pclone:PivotTo(rawposition)
            else
                renderSteppedConnection:Disconnect()
                pclone:Destroy()
            end
        end

        renderSteppedConnection = RunService.RenderStepped:Connect(updatePreview)
        updatePreview()

        mouse.Button1Down:Connect(function()
            if isplacingobj then
                isplacingobj = false
                print(mouseTarget.CFrame)
                onbuild:FireServer(mouseTarget.CFrame)
            end
        end)
    end
end

local repstorage = game:GetService("ReplicatedStorage")
local onbuild = repstorage.OnBuild
local wall = repstorage.WallFolder.Wall

onbuild.OnServerEvent:Connect(function(plr, cframe)
	local clone = wall:Clone()
	clone.Parent = workspace
	clone:PivotTo(cframe)
end)

The code you provided does not work because it is not complete. When you try to use it, you will get errors because some of the variables and functions you are using have not been defined.

To fix this, you need to make sure that all of the variables and functions you are using are defined before you use them. In your case, you need to define the onbuild function before you use it in the onT function.

1 Like

A serverscript has :fireserver() function in it?

Edit: Ah I see it now, you meant

Localscript:

local function GetNearestPointOnRay(origin, direction, point)
    local ray = Ray.new(origin, direction)
    local _, hitPosition = workspace:FindPartOnRay(ray)
    return (hitPosition - origin).magnitude < (point - origin).magnitude and hitPosition or point
end

local function GetMousePositionOnRay(origin, direction)
    local ray = Ray.new(origin, direction)
    local _, hitPosition = workspace:FindPartOnRay(ray)
    return hitPosition
end

local function onT()
    if not isplacingobj then
        isplacingobj = true

        local pclone = preview:Clone()
        pclone.Parent = workspace

        local rootpart = pclone.RootPart
        pclone.PrimaryPart = rootpart

        local mouseTarget = nil

        pclone.ChildAdded:Connect(function(child)
            if child:IsA("BasePart") then
                mouseTarget = child
            end
        end)

        local renderSteppedConnection = nil

        local function updatePreview()
            if isplacingobj then
                local mousePosition = GetMousePositionOnRay(rootpart.Position, camera.CFrame.lookVector)
                local mousePoint = GetNearestPointOnRay(rootpart.Position, camera.CFrame.lookVector, mousePosition)
                local rawposition = CFrame.new(mousePoint.X, mousePoint.Y + rootpart.Size.Y/2, mousePoint.Z) * CFrame.Angles(math.rad(0), math.rad(humanoidrootpart.Orientation.Y), math.rad(0))
                pclone:PivotTo(rawposition)
            else
                renderSteppedConnection:Disconnect()
                pclone:Destroy()
            end
        end

        renderSteppedConnection = RunService.RenderStepped:Connect(updatePreview)
        updatePreview()

        mouse.Button1Down:Connect(function()
            if isplacingobj then
                isplacingobj = false
                print(mouseTarget.CFrame)
                onbuild:FireServer(mouseTarget.CFrame)
            end
        end)
    end
end

Serverscript:

local repstorage = game:GetService("ReplicatedStorage")
local onbuild = repstorage.OnBuild
local wall = repstorage.WallFolder.Wall

onbuild.OnServerEvent:Connect(function(plr, cframe)
	local clone = wall:Clone()
	clone.Parent = workspace
	clone:PivotTo(cframe)
end)

Unfortunately, I can’t declare this the solution. There are many errors and :FindPartOnRay is a deprecated function.

After adding variables and testing your script, the preview started at the wrong position as I watched it get launched into the sky never to be seen again.

Edit: Just know this is also a first person game, I should’ve said that earlier i apologize

Add this somewhere after it places the wall or whatever

local wall = nil--path wall/placed item here
local height = 0 --height of baseplate
if wall.Position.Y <= height then
wall.Position = Vector3.new(wall.Position.X, height, wall.Position.Z
end

It checks if the placed item is under a certain height, if yes then it will place it to the minimum height.
Yes, it’s possibly this simple.

Oh sorry about that, I am trying to fix that ASAP.

I figured it out the solution, I used raycasting instead of mouse.hit for the Y value, and it worked perfectly. I’m going to @Redluo the solution since he gave me the idea, but everyone else still helped out alot :slight_smile:

Here is the final script if you are curious:

Local Script:

local folder = game.ReplicatedStorage:WaitForChild("WallFolder")
local plr = game.Players.LocalPlayer
local wall = folder:WaitForChild("Wall")
local preview = folder:WaitForChild("Preview")
local context = game:GetService("ContextActionService")
local runservice = game:GetService("RunService")
local mouse = plr:GetMouse()
local onbuild = game.ReplicatedStorage:WaitForChild("OnBuild")
local isplacingobj = false
local humanoidrootpart = plr.Character:WaitForChild("HumanoidRootPart")
local rayparams = RaycastParams.new()
rayparams.FilterType = Enum.RaycastFilterType.Blacklist
rayparams.FilterDescendantsInstances = {preview}

local function onT ()
	if not isplacingobj then
	isplacingobj = true
	local pclone = preview:Clone()
		pclone.Parent = workspace
		local rootpart = pclone.RootPart
		pclone.PrimaryPart = rootpart
		runservice.RenderStepped:Connect(function()
			if isplacingobj then
			mouse.TargetFilter = pclone
			local rawposition = CFrame.new(mouse.Hit.Position.X,mouse.Hit.Position.Y +rootpart.Size.Y/2, mouse.Hit.Position.Z) * CFrame.Angles(math.rad(0),math.rad(humanoidrootpart.Orientation.Y), math.rad(0))
			pclone:PivotTo(rawposition)
			end
		end)
		
		mouse.Button1Down:Connect(function()
			if isplacingobj then isplacingobj = false
			local rayresult = workspace:Raycast(preview.RootPart.Position, Vector3.new(0,-100,0), rayparams)
				onbuild:FireServer(CFrame.new(pclone.RootPart.CFrame.X, rayresult.Position.Y + pclone.RootPart.Size.Y/2, pclone.RootPart.CFrame.Z) * CFrame.Angles(math.rad(0),math.rad(humanoidrootpart.Orientation.Y), math.rad(0)))
			pclone:Destroy()
		end	
	end)
			
	end	
end

context:BindAction("Tpressed", onT, false, Enum.KeyCode.T)

Server Script:

local repstorage = game:GetService("ReplicatedStorage")
local onbuild = repstorage.OnBuild
local wall = repstorage.WallFolder.Wall

onbuild.OnServerEvent:Connect(function(plr, cframe)
	local clone = wall:Clone(cframe)
	clone.Parent = workspace
	clone:PivotTo(cframe)
end)

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.