i currently have a problem with my placement system.
I want to make a collision function using :GetTouchingParts and i want to filter some parts, i used table.remove() but it doesn’t work somehow.
local UserInputService = game:GetService("UserInputService")
local TweenService = game:GetService("TweenService")
local TweenInfo = TweenInfo.new(0.5)
local PlaceEvent = game:GetService("ReplicatedStorage").PlaceEvent
local currentMode = nil
local currentObject = nil
local mouseHitPart = nil
local mouseHitPos = nil
local mouseHitNormal = nil
local canplace = false
local orientation = CFrame.new()
local player = game:GetService("Players").LocalPlayer
local camera = workspace.CurrentCamera
local function raycast()
local mousePos = UserInputService:GetMouseLocation() -- Returns a Vector2 of the mouse position
local params = RaycastParams.new()
params.FilterDescendantsInstances = {workspace:FindFirstChild("Placement")} -- Ignore the object we're placing and the character
params.FilterType = Enum.RaycastFilterType.Include
local unitRay = camera:ScreenPointToRay(mousePos.X, mousePos.Y) -- This creates a UnitRay from the mouse's position
return workspace:Raycast(unitRay.Origin, unitRay.Direction * 200, params)
end
local function getRotatedSize(size)
local newModelSize = orientation * CFrame.new(size) -- Multiply the orientation by the size
newModelSize = Vector3.new(
math.abs(newModelSize.X),
math.abs(newModelSize.Y),
math.abs(newModelSize.Z)
) -- math.abs so our results aren't negative
return newModelSize
end
local function getPlacementPos(size)
local newModelSize = getRotatedSize(size)
return Vector3.new(
math.floor(mouseHitPos.X / 0.5 + 0.5) * 0.5 + mouseHitNormal.X * (newModelSize.X / 2),
math.floor(mouseHitPos.Y / 0.5 + 0.5) * 0.5 + mouseHitNormal.Y * (newModelSize.Y / 2),
math.floor(mouseHitPos.Z / 0.5 + 0.5) * 0.5 + mouseHitNormal.Z * (newModelSize.Z / 2)
) -- Snap the position to 0.5 and offset it using the surface normal
end
local function getTouchingParts(part)
local connection = part.Touched:Connect(function() end)
local result = part:GetTouchingParts()
connection:Disconnect()
return result
end
local function checkCollision(instance)
local IsColliding = false
if instance:IsA("BasePart") then
local result = getTouchingParts(instance)
for i, v in pairs(result) do
if v:IsDescendantOf(instance.Parent) then
table.remove(result,i)
print(v)
end
end
if #result ~= 0 then IsColliding = true end
end
return IsColliding
end
UserInputService.InputBegan:Connect(function(input, gp)
if gp then return end
local key = input.KeyCode
if key == Enum.KeyCode.E then
if currentObject then
currentObject:Destroy()
currentObject = nil
end
if currentMode ~= "Build" then
currentMode = "Build"
currentObject = workspace.Chair:Clone()
currentObject.Parent = workspace
currentObject.PrimaryPart = currentObject:FindFirstChild("Main")
currentObject.PrimaryPart.Transparency = 1
for i, item in pairs(currentObject:GetChildren()) do
if item ~= currentObject.PrimaryPart then
item.Material = Enum.Material.ForceField
item.CanCollide = false
end
end
else
currentMode = nil
end
elseif key == Enum.KeyCode.R then
orientation = CFrame.Angles(0, math.rad(90), 0) * orientation
elseif key == Enum.KeyCode.T then
orientation = CFrame.Angles(0, 0, math.rad(90)) * orientation
elseif input.UserInputType == Enum.UserInputType.MouseButton1 then
if currentMode == "Build" and currentObject ~= nil then
if canplace == true then
PlaceEvent:FireServer(currentObject.Name, currentObject.PrimaryPart.CFrame, orientation)
end
elseif currentMode == "Destroy" and currentObject == nil then
-- placeholder
end
elseif key == Enum.KeyCode.G then
if currentObject then
currentObject:Destroy()
currentObject = nil
end
if currentMode ~= "Build" then
currentMode = "Destroy"
end
end
end)
game:GetService("RunService").Heartbeat:Connect(function()
local raycastResult = raycast() or {}
mouseHitPart = raycastResult.Instance
mouseHitPos = raycastResult.Position
mouseHitNormal = raycastResult.Normal
if currentMode == "Build" then
if mouseHitNormal and mouseHitPos and currentObject then
currentObject:SetPrimaryPartCFrame(CFrame.new(getPlacementPos(currentObject.PrimaryPart.Size)) * orientation)
end
local iscolliding = checkCollision(currentObject.PrimaryPart)
if iscolliding and currentObject ~= nil then
for i, item in pairs(currentObject:GetChildren()) do
if item ~= currentObject.PrimaryPart then
item.BrickColor = BrickColor.new("Really red")
canplace = false
end
end
else
for i, item in pairs(currentObject:GetChildren()) do
if item ~= currentObject.PrimaryPart then
item.BrickColor = BrickColor.new("Lime green")
canplace = true
end
end
end
end
end)
if possible, i want feedback too for my placement system (and collision), i want to know if my script is efficient enough or should i use just use a module.
I would really appreciate any help. Thank you.
i changed it and used :GetPartsInPart(): and filtered the currentObject’s children variable out but it seems like it still doesn’t work for some reason.
local UserInputService = game:GetService("UserInputService")
local TweenService = game:GetService("TweenService")
local TweenInfo = TweenInfo.new(0.5)
local PlaceEvent = game:GetService("ReplicatedStorage").PlaceEvent
local currentMode = nil
local currentObject = nil
local mouseHitPart = nil
local mouseHitPos = nil
local mouseHitNormal = nil
local canplace = false
local orientation = CFrame.new()
local player = game:GetService("Players").LocalPlayer
local camera = workspace.CurrentCamera
local function raycast()
local mousePos = UserInputService:GetMouseLocation()
local params = RaycastParams.new()
params.FilterDescendantsInstances = {workspace:FindFirstChild("Placement")}
params.FilterType = Enum.RaycastFilterType.Include
local unitRay = camera:ScreenPointToRay(mousePos.X, mousePos.Y)
return workspace:Raycast(unitRay.Origin, unitRay.Direction * 200, params)
end
local function getRotatedSize(size)
local newModelSize = orientation * CFrame.new(size)
newModelSize = Vector3.new(
math.abs(newModelSize.X),
math.abs(newModelSize.Y),
math.abs(newModelSize.Z)
)
return newModelSize
end
local function getPlacementPos(size)
local newModelSize = getRotatedSize(size)
return Vector3.new(
math.floor(mouseHitPos.X / 0.5 + 0.5) * 0.5 + mouseHitNormal.X * (newModelSize.X / 2),
math.floor(mouseHitPos.Y / 0.5 + 0.5) * 0.5 + mouseHitNormal.Y * (newModelSize.Y / 2),
math.floor(mouseHitPos.Z / 0.5 + 0.5) * 0.5 + mouseHitNormal.Z * (newModelSize.Z / 2)
)
end
local function getTouchingParts(part, params)
local connection = part.Touched:Connect(function() end)
local result = workspace:GetPartsInPart(part, params)
connection:Disconnect()
return result
end
local function checkCollision(instance)
local IsColliding = false
if instance:IsA("BasePart") then
for i, v in pairs(instance.Parent:GetChildren()) do
local filter = {}
filter[i] = v
local params = OverlapParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = filter
end
end
return IsColliding
end
UserInputService.InputBegan:Connect(function(input, gp)
if gp then return end
local key = input.KeyCode
if key == Enum.KeyCode.E then
if currentObject then
currentObject:Destroy()
currentObject = nil
end
if currentMode ~= "Build" then
currentMode = "Build"
currentObject = workspace.Chair:Clone()
currentObject.Parent = workspace
currentObject.PrimaryPart = currentObject:FindFirstChild("Main")
currentObject.PrimaryPart.Transparency = 1
for i, item in pairs(currentObject:GetChildren()) do
if item ~= currentObject.PrimaryPart then
item.Material = Enum.Material.ForceField
item.CanCollide = false
end
end
else
currentMode = nil
end
elseif key == Enum.KeyCode.R then
orientation = CFrame.Angles(0, math.rad(90), 0) * orientation
elseif key == Enum.KeyCode.T then
orientation = CFrame.Angles(0, 0, math.rad(90)) * orientation
elseif input.UserInputType == Enum.UserInputType.MouseButton1 then
if currentMode == "Build" and currentObject ~= nil then
if canplace == true then
PlaceEvent:FireServer(currentObject.Name, currentObject.PrimaryPart.CFrame, orientation)
end
elseif currentMode == "Destroy" and currentObject == nil then
-- placeholder
end
elseif key == Enum.KeyCode.G then
if currentObject then
currentObject:Destroy()
currentObject = nil
end
if currentMode ~= "Build" then
currentMode = "Destroy"
end
end
end)
game:GetService("RunService").Heartbeat:Connect(function()
local raycastResult = raycast() or {}
mouseHitPart = raycastResult.Instance
mouseHitPos = raycastResult.Position
mouseHitNormal = raycastResult.Normal
if currentMode == "Build" then
if mouseHitNormal and mouseHitPos and currentObject then
currentObject:SetPrimaryPartCFrame(CFrame.new(getPlacementPos(currentObject.PrimaryPart.Size)) * orientation)
end
local iscolliding = checkCollision(currentObject.PrimaryPart)
if iscolliding and currentObject ~= nil then
for i, item in pairs(currentObject:GetChildren()) do
if item ~= currentObject.PrimaryPart then
item.BrickColor = BrickColor.new("Really red")
canplace = false
end
end
else
for i, item in pairs(currentObject:GetChildren()) do
if item ~= currentObject.PrimaryPart then
item.BrickColor = BrickColor.new("Lime green")
canplace = true
end
end
end
end
end)
I can still place the currentObject when its colliding
For Raycast Filter Types, they follow a whitelist or blacklist system. You can think of a whitelist as an exclusive party; you need to have your name on the list to be invited inside. A blacklist is more like buying an airplane ticket; there is a no-fly list, and you need your name to not be on there to buy the ticket. If you want your raycast to automatically include everything, but exclude just the parent parts, you need a blacklist, or an Exclude filter type.
i’m very very sorry about the late reply (i took a break for a while bc of burnout) but i made a solution according to your advice and changed the script a bit. (i followed an old community tutorial so the script is a bit outdated which i didn’t realize that for a while)