Placement system bug

  1. What do I want to achieve?
    Hello!, I want to achieve a item placement system like bloxburg, fast food simulator, etc

  2. What is the issue?
    for some odd reason i’m having 2 very odd bugs where either the meat pan model falls through other models that my mouse is on top on or either just fall through the world.
    Watch Screen Recording 2025-06-23 at 3.30.50 PM | Streamable

  3. **What solutions have you tried so far?
    I’ve tried anchoring the model but oddly enough the model just goes below the baseplate

Here’s the placement handler

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local PlacementRemote = ReplicatedStorage.RemoteEvents:WaitForChild("Placement")

local function GetItemClone(name)
	for _, item in pairs(ReplicatedStorage.FoodItems:GetDescendants()) do
		if item.Name == name then
			return item:Clone()
		end
	end
	return nil
end

PlacementRemote.OnServerEvent:Connect(function(player, cframe, itemName) 
	print(cframe)
	local item = GetItemClone(itemName)
	if not item then
		warn("Item not found:", itemName)
		return false
	end

	if item:IsA("Model") then
		local primary = item.PrimaryPart
		if not primary then
			warn("No PrimaryPart found for model:", item.Name)
			return false
		end

		primary.CFrame = cframe	
		primary.Anchored = true


	elseif item:IsA("MeshPart") then
		item.CFrame = cframe
		item.Anchored = true
	end

	item:SetAttribute("Owner", player.Name)
	item.Parent = workspace 
end)

Placement Module Client Sided

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

local PlaceRemote = ReplicatedStorage.RemoteEvents.Placement
local Mouse = Players.LocalPlayer:GetMouse()
local Camera = workspace.CurrentCamera
local MAX_RANGE = 20

local Placement = {}
Placement.__index = Placement

function Placement.new(itemName, player, heldItem)
	return setmetatable({
		ItemName = itemName,
		Player = player,
		HeldItem = heldItem,
		Preview = nil,
		Root = nil,
		Conn = nil,
		InputConn = nil
	}, Placement)
end

function Placement:CreatePreview()
	print(self.ItemName)
	local source 
	
	for i, v in pairs(ReplicatedStorage.FoodItems:GetDescendants()) do
		if v.Name == self.ItemName then
			source = v:Clone()
			break
		end
	end
	
	if not source then warn("Missing source item") return end

	local clone = source:Clone()
	self.Preview = clone

	if clone:IsA("Model") then
		self.Root = clone.PrimaryPart
	elseif clone:IsA("BasePart") then
		self.Root = clone
	end

	for _, p in clone:GetDescendants() do
		if p:IsA("BasePart") then
			p.Transparency = 0.5
			p.CanCollide = false
		end
	end

	clone.Parent = workspace
end

function Placement:InRange()
	if self.Root then
		local Distance = (self.Player.Character.HumanoidRootPart.Position - self.Root.Position).Magnitude
		if Distance <= MAX_RANGE then
			return true
		else
			return false
		end
	end
	return false
end

function Placement:FollowMouse()
	if not self.Root or not self.Preview then return end

	local rayParams = RaycastParams.new()
	rayParams.FilterDescendantsInstances = {self.Preview, self.Player.Character}
	rayParams.FilterType = Enum.RaycastFilterType.Exclude

	local Mouse = UserInputService:GetMouseLocation()
	local ray = Camera:ViewportPointToRay(Mouse.X, Mouse.Y)
	local result = workspace:Raycast(ray.Origin, ray.Direction * 1000, rayParams)

	if result then
		
		local HitPosition = result.Position
		local Normal = result.Normal
		
		local Offset = Normal * self.Root.Size.Y/2
		local PlacePos = HitPosition + Offset
		self.Root.CFrame = CFrame.new(PlacePos) 
	else
		print("no result")
	end
end


function Placement:Cleanup()
	if self.Conn then self.Conn:Disconnect() end
	if self.InputConn then self.InputConn:Disconnect() end
	if self.Preview then self.Preview:Destroy() end
	if self.HeldItem then self.HeldItem:Destroy() end
end

function Placement:Start()
	self:CreatePreview()

	self.Conn = RunService.RenderStepped:Connect(function()
		self:FollowMouse()
	end)

	self.InputConn = UserInputService.InputBegan:Connect(function(input, gpe)
		if gpe then return end
		if input.UserInputType == Enum.UserInputType.MouseButton1 and self:InRange() then
			if self.Root then
				print(self.Root.CFrame)
				PlaceRemote:FireServer(self.Root.CFrame, self.ItemName)
				self:Cleanup()
				print("Destroyed")
			end
		end
	end)
end

return Placement

You could try to check the model you’re placing down to see if it matches entirely the one you’re moving around with your mouse [primary part, missing or random parts]

I’ve checked but still the same result I’m wondering is it because of collision fidelity?

I don’t really think so since anchoring the model should ignore any physics and just place the model at the specified position. I think you should copy the printed position after you place the model and then check with your anchored model to see if it’s the same as the printed one or if it gets messed up after placing the model

Try setting the CFrame after anchoring the part.

SERVER

CLIENT

Try using Model:PivotTo() instead of setting the PrimaryPart of the Model’s CFrame

for the client side it’s supposed to be a preview of where the item is supposed to be placed, now that works as intended it’s just on the serverside where it’s time to place the actual part where it phases through whatever I put it on top of. This is honestly intresting behavior that I’ve never encountered before

and using PivotTo still leads to the same bug


same coordinates @SebyX_X

Then the issue probably is in the client module, try maybe using the same logic of placing the model that you have in your follow function to determine the CFrame that you send to the server instead of just getting the cframe from the model before sending it, I have no other idea

Also don’t think that’s an issue but you’re cloning the item twice and I couldn’t figure out why so i’m just saying that in case it’s a mistake

1 Like