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 . 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?
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)