I am trying to make a part placement system for my custom tycoon game, and i am having the issue of only the base part moving even though they are linked together using weld constraints. Ths rises issues with multi part placement overlapping the second tile.
whole code (local script):
local remoteEvent = workspace.RemoteEvents.PlacePart
local gridSize = 4
local currentRotation = 0
local Clone
local isValidPlacement = true
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local screenGui = script.Parent.Parent.Parent
-- Function to snap to the grid
local function snapToGrid(position, gridSize, partSize)
return Vector3.new(
math.floor(position.X / gridSize + 0.5) * gridSize,
math.floor(position.Y / gridSize) + partSize.Y / 2,
math.floor(position.Z / gridSize + 0.5) * gridSize
)
end
-- Function to check if a spot is available for all parts (including descendants)
local function isSpotAvailableForAllParts(parts)
for _, part in ipairs(parts) do
local region = Region3.new(
part.Position - part.Size / 2 + Vector3.new(0.05, 0.05, 0.05),
part.Position + part.Size / 2 - Vector3.new(0.05, 0.05, 0.05)
)
local partsInRegion = workspace:FindPartsInRegion3(region, nil, math.huge)
for _, foundPart in ipairs(partsInRegion) do
if workspace.Factories.Factory1.PlacedParts:IsAncestorOf(foundPart) then
return false
end
end
end
return true
end
-- Function to get all descendants of a part
local function getAllDescendants(part)
local descendants = {}
for _, child in ipairs(part:GetDescendants()) do
if child:IsA("BasePart") then
table.insert(descendants, child)
end
end
return descendants
end
-- Function to detect if a part is touching another part
local function isTouchingAnotherPart(part)
local touchingParts = part:GetTouchingParts()
for _, touchingPart in ipairs(touchingParts) do
if workspace.Factories.Factory1.PlacedParts:IsAncestorOf(touchingPart) then
return touchingPart
end
end
return nil
end
-- Function to move all ancestors and descendants of a part
local function moveAncestorsAndDescendants(part)
local targetPosition = Vector3.new(100, 0, 100)
local partsToMove = {}
-- Move all descendants
local function collectDescendants(p)
for _, child in ipairs(p:GetDescendants()) do
if child:IsA("BasePart") then
table.insert(partsToMove, child)
end
end
end
-- Collect ancestors and descendants
local currentPart = part
while currentPart.Parent and currentPart.Parent ~= workspace do
table.insert(partsToMove, currentPart)
currentPart = currentPart.Parent
end
-- Collect descendants of the original part
collectDescendants(part)
-- Move all parts (ancestors and descendants)
for _, partToMove in ipairs(partsToMove) do
partToMove.Position = targetPosition + Vector3.new(0, partToMove.Size.Y / 2, 0)
end
end
-- Function to hide UI and start placing block
local function startPlacingBlock()
screenGui.Enabled = false
workspace.Factories.Factory1.Base.Texture.Transparency = 0.8
if Block then
Clone = Block:Clone()
Clone.Orientation = Vector3.new(0,script.Parent.Parent.Parent.LastRot.Value,0)
Clone.CanQuery = false
Clone.CanCollide = false
Clone.Transparency = 0.5
Clone.BrickColor = BrickColor.new("Bright green")
Clone.Parent = workspace
-- Function to update block position
local function updateBlockPosition()
local targetPosition = mouse.Hit.p
local targetSize = Clone.Size
local snappedPosition = snapToGrid(targetPosition, gridSize, targetSize)
Clone.Position = snappedPosition
Clone.Orientation = Vector3.new(0, currentRotation, 0)
local partsToCheck = getAllDescendants(Clone)
table.insert(partsToCheck, Clone)
-- Check if spot is available
if isSpotAvailableForAllParts(partsToCheck) then
isValidPlacement = true
Clone.BrickColor = BrickColor.new("Bright green") -- Green for valid
else
-- Find the part it's touching
local touchingPart = isTouchingAnotherPart(Clone)
if touchingPart then
-- Move ancestors and descendants
moveAncestorsAndDescendants(touchingPart)
isValidPlacement = true
Clone.BrickColor = BrickColor.new("Bright green")
else
isValidPlacement = false
Clone.BrickColor = BrickColor.new("Bright red") -- Red for invalid
end
end
end
game:GetService("RunService").RenderStepped:Connect(function()
if Clone then
updateBlockPosition()
end
end)
mouse.Button1Down:Connect(function()
if Clone and isValidPlacement then
remoteEvent:FireServer({
Position = Clone.Position,
Orientation = Clone.Orientation,
Name = Clone.Name
})
Clone:Destroy()
Clone = nil
screenGui.Enabled = true
workspace.Factories.Factory1.Base.Texture.Transparency = 1
end
end)
end
end
-- Rotate block by 90 degrees on "R" key press
local function rotateBlock()
currentRotation += 90
script.Parent.Parent.Parent.LastRot.Value += 90
if Clone then
Clone.Orientation = Vector3.new(0, currentRotation, 0)
end
end
-- Connect "R" key to rotate the block
mouse.KeyDown:Connect(function(key)
if key == "r" then
rotateBlock()
end
end)
-- Start block placement on button click
script.Parent.MouseButton1Click:Connect(startPlacingBlock)
but i believe the problem rests in this function:
local targetPosition = Vector3.new(100, 0, 100)
local partsToMove = {}
-- Move all descendants
local function collectDescendants(p)
for _, child in ipairs(p:GetDescendants()) do
if child:IsA("BasePart") then
table.insert(partsToMove, child)
end
end
end
-- Collect ancestors and descendants
local currentPart = part
while currentPart.Parent and currentPart.Parent ~= workspace do
table.insert(partsToMove, currentPart)
currentPart = currentPart.Parent
end
-- Collect descendants of the original part
collectDescendants(part)
-- Move all parts (ancestors and descendants)
for _, partToMove in ipairs(partsToMove) do
partToMove.Position = targetPosition + Vector3.new(0, partToMove.Size.Y / 2, 0)
end
end