Placement System using the Hitbox of the entire model instead of a specific part

Hi, I’m having an issue with my grid placement system in Roblox. The script is using the hitbox of the entire model instead of the specific part named “HitBox” that I want it to use. I’ve tried a lot of fixes (from different sources aswell) but can’t get it to use the correct part :confused:. Also not everything is made by me, most of it is from a youtube tutorial but i modified it to fit my needs.

Could anyone help me to get it detect the correct part? :pray:

Here are 2 scripts that are causing the problem(its most likely the client placer script that causing the wrong hitbox)

Client Placer (Module script):

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local SelectionBoxTemplate = ReplicatedStorage.SelectionBox
local PlacableObjects = ReplicatedStorage:WaitForChild("PlacableObjects")
local TryPlace = ReplicatedStorage:WaitForChild("TryPlace")
local TryDelete = ReplicatedStorage:WaitForChild("TryDelete")
local UserInputService = game:GetService("UserInputService")
local ContextActionService = game:GetService("ContextActionService")
local RunService = game:GetService("RunService")
local CurrentCamera = game.Workspace.CurrentCamera
local PlacementValidator = require(ReplicatedStorage.PlacementValidator)

local PREVIEW_RENDER = "RenderPreview"
local PLACE_ACTION = "Place"
local ROTATE_ACTION = "Rotate"
local DELETE_ACTION = "Delete"
local SWITCH_ACTION = "Switch"

local function CastMouse()
	local MouseLocation = UserInputService:GetMouseLocation()
	local MouseRay = CurrentCamera:ViewportPointToRay(MouseLocation.X, MouseLocation.Y)
	
	return game.Workspace:Raycast(MouseRay.Origin, MouseRay.Direction * 1000)
end

local function AllowTransparent(object: Model, part: Part)
	if object.Name.find("ConveyorBelt", "ConveyorBelt") then
		if part.Name == "Texture Part" then return 1
		else
			return 0
		end
	end
end

local ClientPlacer = {}
ClientPlacer.__index = ClientPlacer

function ClientPlacer.new(plot: Model)
	local self = setmetatable({
		Plot = plot,
		Preview = nil,
		PreviewIndex = 1,
		Rotation = 0
	}, ClientPlacer)
	
	self:InitiateRenderPreview()
	
	ContextActionService:BindAction(PLACE_ACTION, function(...) self:TryPlaceObject(...) end, false, Enum.UserInputType.MouseButton1)
	ContextActionService:BindAction(ROTATE_ACTION, function(...) self:RotateObject(...) end, false, Enum.KeyCode.R)
	ContextActionService:BindAction(DELETE_ACTION, function(...) self:TryDeleteObject(...) end, false, Enum.KeyCode.X)
	ContextActionService:BindAction(SWITCH_ACTION, function(...) self:SwitchObject(...) end, false, Enum.KeyCode.E, Enum.KeyCode.Q)
	return self
end

function ClientPlacer:InitiateRenderPreview()
	self:PreparePreviewModel(PlacableObjects:GetChildren()[self.PreviewIndex])
	RunService:BindToRenderStep(PREVIEW_RENDER, Enum.RenderPriority.Camera.Value, function(...) self:RenderPreview(...) end)
end

function ClientPlacer:PreparePreviewModel(model: Model)
	if self.Preview then
		self.Preview:Destroy()
	end
	
	self.Preview = model:Clone()
	local SelectionBox = SelectionBoxTemplate:Clone()
	SelectionBox.Adornee = self.Preview
	SelectionBox.Parent = self.Preview
	
	for _, part in self.Preview:GetDescendants() do
		if part:IsA("BasePart") then
			part.CanCollide = false
			part.CanQuery = false
			part.Transparency = 0.5 -- AllowTransparent(model, part)
		end
	end
	
	self.Preview.Parent = game.Workspace
end

local function SnapToGrid(position: Vector3, snap: number)
	local function Snap(value)
		return math.round(value / snap) * snap
	end
	return Vector3.new(Snap(position.X), Snap(position.Y), Snap(position.Z))
end

function ClientPlacer:RenderPreview()
	local Cast = CastMouse()
	if Cast and Cast.Position then
		local Cframe = CFrame.new(SnapToGrid(Cast.Position, 0.5)) * CFrame.Angles(0, self.Rotation, 0)
		self.Preview:PivotTo(Cframe)
		
		local Size = self.Preview:GetExtentsSize()--Vector3.new(self.Preview:FindFirstChild("HitBox").Size.X, self.Preview:FindFirstChild("HitBox").Size.Y, self.Preview:FindFirstChild("HitBox").Size.Z)
		
		self.Preview.SelectionBox.Color3 =
			if PlacementValidator.WithinBounds(self.Plot, Size, Cframe, self.Preview)
			and PlacementValidator.NotIntersectingObjects(self.Plot, Size, Cframe, self.Preview)
			then Color3.new(0.45, 0.65, 1)
			else Color3.new(1, 0.45, 0.45)
	end
end

function ClientPlacer:TryPlaceObject(_, state, _)
	if state ~= Enum.UserInputState.Begin then
		return
	end
	
	local success = TryPlace:InvokeServer(self.Preview.Name, self.Preview:GetPivot())
end

function ClientPlacer:RotateObject(_, state, _)
	if state == Enum.UserInputState.Begin then
		self.Rotation += math.pi / 2
	end
end

function ClientPlacer:TryDeleteObject(_, state, _)
	if state ~= Enum.UserInputState.Begin then
		local Cast = CastMouse()
		if Cast and Cast.Instance then
			local success = TryDelete:InvokeServer(Cast.Instance)
		end
	end
end

function ClientPlacer:SwitchObject(_, state, object)
	if state == Enum.UserInputState.Begin then
		local Objects = PlacableObjects:GetChildren()
		local Direction = if object.KeyCode == Enum.KeyCode.E then 1 else -1
		self.PreviewIndex = (self.PreviewIndex - 1 + Direction) % #Objects + 1
		self:PreparePreviewModel(Objects[self.PreviewIndex])
	end
end

function ClientPlacer:Destroy()
	if self.Preview then
		self.Preview:Destroy()
	end
	RunService:UnbindFromRenderStep(PREVIEW_RENDER)
	
	ContextActionService:UnbindAction(PLACE_ACTION)
	ContextActionService:UnbindAction(ROTATE_ACTION)
	ContextActionService:UnbindAction(DELETE_ACTION)
	ContextActionService:UnbindAction(SWITCH_ACTION)
end


return ClientPlacer

And Placement Validator (Module script):

local PlacementValidator = {}

local AdjustedObjectSize
local Margin = Vector3.new(0.2,0.2,0.2)
local ShrinkFactor = 0.9

function PlacementValidator.WithinBounds(plot: Model, objectSize: Vector3, worldCF: CFrame, object: Model)

	local PlotCF, PlotSize = plot:GetBoundingBox()
	local ObjectCF = PlotCF:ToObjectSpace(worldCF)
	
	AdjustedObjectSize = objectSize - Margin
	
	local CornerPoints = {}
	for _, x in {-1, 1} do
		for _, z in {-1, 1} do
			table.insert(CornerPoints, ObjectCF:PointToWorldSpace(
				Vector3.new(x * AdjustedObjectSize.X / 2, 0, z * AdjustedObjectSize.Z / 2)
			))
		end
	end
	
	for _, corner in CornerPoints do
		if math.abs(corner.X) > PlotSize.X / 2 or math.abs(corner.Z) > PlotSize.Z / 2 then
			return false
		end
	end
	
	return true
end

function PlacementValidator.NotIntersectingObjects(plot: Model, objectSize: Vector3, worldCF: CFrame, object: Model)
	
	local Parameters = OverlapParams.new()
	Parameters:AddToFilter(plot:WaitForChild("Objects"))
	Parameters.FilterType = Enum.RaycastFilterType.Include
	
	local players = game:GetService("Players")
	for _, player in ipairs(players:GetPlayers()) do
		if player.Character then
			Parameters:AddToFilter(player.Character)
		end
	end

	
	local AdjustedObjectSize = objectSize - Margin
	local Parts = game.Workspace:GetPartBoundsInBox(worldCF, AdjustedObjectSize, Parameters)
	return #Parts == 0
end

return PlacementValidator 

Images of the error:


(also it has a wierd bug as shown in the last image)