Help with build system! [SOLVED]

Hello there developers, i would like help on the piece of code, i am trying to make a build system but when i place the actual thing on something, (such as a terrain hill) it would simply clip though, not going above it. it’s the same with things on parts.

Any help please?

Script:

local RS = game:GetService("ReplicatedStorage")

local PlaceObject = RS:WaitForChild("PlaceObject")
local EditObject = RS:WaitForChild("EditObject")
local Objects = RS:WaitForChild("Objects")

local proximityPrompt = script:WaitForChild("ProximityPrompt")


function WeldModel(model)
	for objs, obj in pairs(model:GetDescendants()) do
		if obj:IsA("BasePart") and obj ~= model.PrimaryPart then
			local weldConstraint = Instance.new("WeldConstraint")

			weldConstraint.Part0 = obj
			weldConstraint.Part1 = model.PrimaryPart

			weldConstraint.Parent = obj
			obj.Anchored = false
			obj.CanCollide = false
		end
	end

	model.PrimaryPart.CanCollide = false
end


function InitModel(model)
	local hitbox = Instance.new("Part")
	local orientation, size = model:GetBoundingBox()

	hitbox.Size = size
	hitbox.CFrame = orientation

	hitbox.Transparency = 1
	hitbox.Parent = model
	hitbox.Anchored = true
	model.PrimaryPart = hitbox
	
	WeldModel(model)

	return
end

local function place(obj, posX, posY, posZ)
	local orientationY = obj.PrimaryPart.Orientation.Y

	obj:SetPrimaryPartCFrame(CFrame.new(posX, posY, posZ) * CFrame.Angles(0, math.rad(orientationY), 0))
	obj.PrimaryPart.CanCollide = true
	return
end

PlaceObject.OnServerEvent:Connect(function(plr, objName, posX, posZ, orientationY)
	local foundObject = Objects:FindFirstChild(objName)
	
	if foundObject then
		if plr.leaderstats.Money.Value >= foundObject.Price.Value then
			plr.leaderstats.Money.Value -= foundObject.Price.Value
			
			local placedObject = foundObject:Clone()

			InitModel(placedObject)

			placedObject:SetPrimaryPartCFrame(CFrame.new(math.floor(posX),
				placedObject.PrimaryPart.Position.Y, math.floor(posZ)) * CFrame.Angles(0, math.rad(orientationY), 0))
			
			if placedObject:FindFirstChild("Collisions") then
				for _, collision in pairs(placedObject.Collisions:GetChildren()) do
					collision.CanCollide = true
				end
			end


			placedObject.Parent = workspace:WaitForChild("PlacedObjects")
		end
	end
end)

Lately not many people have been helping with over 100 views! I’m getting quite sad & unmotivatied, please help. Please.

Would you mind sending a video showing the issue?

robloxapp-20231229-1246424.wmv (3.1 MB)

Here’s the module script as it also takes part of this.

local module = {}

local UIS = game:GetService("UserInputService")
local TS = game:GetService("TweenService")
local RS = game:GetService("ReplicatedStorage")
local TI = TweenInfo.new(.25, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)

local PlacingGroup = script.Parent:WaitForChild("PlacingGroup")
local NameLabel = PlacingGroup:WaitForChild("NameLabel")
local PriceLabel = PlacingGroup:WaitForChild("PriceLabel")

local PlaceObject = RS:WaitForChild("PlaceObject")
local EditObject = RS:WaitForChild("EditObject")
local PlacedObjects = workspace:WaitForChild("PlacedObjects")

local highlight = script:WaitForChild("Highlight")

local plr = game.Players.LocalPlayer
local mouse = plr:GetMouse()
local selectedModel = nil

local connection = nil


module.Vars = {
	["Debounce"] = false;
	["Placing"] = false;
	["Rotating"] = false;
}


function module:ShowBuildInterface(model)
	NameLabel.Text = model.Name
	PriceLabel.Text = "$" .. model.Price.Value
	
	
	local Tween = TS:Create(PlacingGroup, TI, {GroupTransparency = 0, Position = UDim2.new(.5, 0, 1, 0)})
	Tween:Play()
	Tween.Completed:Wait()
	
	return
end


function module:HideBuildInterface()
	local Tween = TS:Create(PlacingGroup, TI, {GroupTransparency = 1, Position = UDim2.new(.5, 0, 1.05, 0)})
	Tween:Play()
	Tween.Completed:Wait()

	return
end


function module:WeldModel(model)
	for objs, obj in pairs(model:GetDescendants()) do
		if obj:IsA("BasePart") and obj ~= model.PrimaryPart then
			local weldConstraint = Instance.new("WeldConstraint")

			weldConstraint.Part0 = obj
			weldConstraint.Part1 = model.PrimaryPart

			weldConstraint.Parent = obj
			obj.Anchored = false
			obj.CanCollide = false
		end
	end

	mouse.TargetFilter = model

	module.Vars.Debounce = false
	model.PrimaryPart.CanCollide = false
end


function module:InitModel(model)
	module.Vars.Debounce = true
	
	local hitbox = Instance.new("Part")

	local orientation, size = model:GetBoundingBox()

	hitbox.Size = size
	hitbox.CFrame = orientation

	hitbox.Transparency = 1
	hitbox.Parent = model
	hitbox.Anchored = true
	model.PrimaryPart = hitbox

	module:WeldModel(model)

	return
end


function module:MoveModel(model)
	if not module.Vars.Rotating then
		module.Vars.Debounce = true

		highlight.Enabled = true
		highlight.Adornee = model

		local pos = mouse.Hit.Position

		local roundedX = math.floor(pos.X)
		local roundedZ = math.floor(pos.Z)
		local orientationY = model.PrimaryPart.Orientation.Y

		local Tween = TS:Create(model.PrimaryPart, TI,
			{CFrame = CFrame.new(roundedX, model.PrimaryPart.Position.Y, roundedZ) * CFrame.Angles(0, math.rad(orientationY), 0)})
		Tween:Play()
		Tween.Completed:Wait()

		module.Vars.Debounce = false
	end
end


function module:RotateModel(model)
	module.Vars.Rotating = true
	
	local Tween = TS:Create(model.PrimaryPart, TI,
		{CFrame = model.PrimaryPart.CFrame * CFrame.Angles(math.rad(0), math.rad(90), math.rad(0))})
	Tween:Play()
	Tween.Completed:Wait()
	
	module.Vars.Rotating = false
end


function module:BuildModel(model, isEditing)
	if not module.Vars.Placing then
		module.Vars.Placing = true
		
		selectedModel = model
		
		if not isEditing then
			selectedModel = model:Clone()
		end
		
		module:InitModel(selectedModel)
		
		selectedModel.Parent = workspace:WaitForChild("TemporaryObjects")
		
		module:ShowBuildInterface(selectedModel)
		module:MoveModel(selectedModel)
		
		connection = mouse.Move:Connect(function()
			module:MoveModel(selectedModel)
		end)
		
		mouse.Button1Down:Connect(function()
			if not module.Vars.Debounce and module.Vars.Placing then
				module.Vars.Placing = false
				
				if connection then
					connection:Disconnect()
					connection = nil
				end 
				
				if not isEditing then
					PlaceObject:FireServer(selectedModel.Name, selectedModel.PrimaryPart.Position.X,
						selectedModel.PrimaryPart.Position.Z, selectedModel.PrimaryPart.Orientation.Y)
					
				else
					EditObject:FireServer(selectedModel, selectedModel.PrimaryPart.Position.X,
						selectedModel.PrimaryPart.Position.Z, selectedModel.PrimaryPart.Orientation.Y)
				end
				
				workspace:WaitForChild("TemporaryObjects"):ClearAllChildren()
				module:HideBuildInterface()
			end
		end)
	end
end


UIS.InputBegan:Connect(function(input, gameEvent)
	if not gameEvent then
		if input.KeyCode == Enum.KeyCode.R then
			if selectedModel and not module.Vars.Rotating then
				module:RotateModel(selectedModel)
			end
		end
	end
end)

Sir are you there? I’ve been waiting for a while now.

Can you send the LocalScript that fires the event PlaceObject here? So I can test your building system for myself and find the problems.

https://create.roblox.com/marketplace/asset/15796381237/Model%3Fkeyword=&pageNumber=&pagePosition= The model, just so you know. The problem is that it doesn’t go on terrain or parts higher than the baseplate itself. Keep that in mind please!

Basically the script doesn’t set an Y Pos to the model, and that’s why its position is going to be on the same height, to solve that, you should use mouse.hit.Y, but there’s another problem, the model is now in the middle of the mouse, because the model is moving to the center of the mouse. So I used the model:GetBoundingBox() to get the actual height of the model, and divide it to 2 so the model won’t float while placing it, using:

local cf, size = model:GetBoundingBox()

local roundedX = math.floor(pos.X)
local roundedZ = math.floor(pos.Z)
local roundedY = math.floor(pos.Y)
local roundedsizeY = math.floor(size.Y)

CFrame = CFrame.new(roundedX, roundedY + (roundedsizeY / 2), roundedZ) * CFrame.Angles(0, math.rad(orientationY), 0)

I also applied the var posY to the place building event so it won’t be placed on the baseplate even though there’s an object on it. You can also do limits like using math.clamp() to the X and Z positions, even to the Y so it won’t be outside the map, but it’s yours to do it!

PlacementHandler script:

local RS = game:GetService("ReplicatedStorage")

local PlaceObject = RS:WaitForChild("PlaceObject")
local EditObject = RS:WaitForChild("EditObject")
local Objects = RS:WaitForChild("Objects")

local proximityPrompt = script:WaitForChild("ProximityPrompt")


function WeldModel(model)
	for objs, obj in pairs(model:GetDescendants()) do
		if obj:IsA("BasePart") and obj ~= model.PrimaryPart then
			local weldConstraint = Instance.new("WeldConstraint")

			weldConstraint.Part0 = obj
			weldConstraint.Part1 = model.PrimaryPart

			weldConstraint.Parent = obj
			obj.Anchored = false
			obj.CanCollide = false
		end
	end

	model.PrimaryPart.CanCollide = false
end


function InitModel(model)
	local hitbox = Instance.new("Part")
	local orientation, size = model:GetBoundingBox()

	hitbox.Size = size
	hitbox.CFrame = orientation

	hitbox.Transparency = 1
	hitbox.Parent = model
	hitbox.Anchored = true
	model.PrimaryPart = hitbox
	
	WeldModel(model)

	return
end


PlaceObject.OnServerEvent:Connect(function(plr, objName, posX, posZ, posY, orientationY)
	local foundObject = Objects:FindFirstChild(objName)
	
	if foundObject then
		if plr.leaderstats.Money.Value >= foundObject.Price.Value then
			plr.leaderstats.Money.Value -= foundObject.Price.Value
			
			local placedObject = foundObject:Clone()

			InitModel(placedObject)

			placedObject:SetPrimaryPartCFrame(CFrame.new(math.floor(posX),
				math.floor(posY), math.floor(posZ)) * CFrame.Angles(0, math.rad(orientationY), 0))
			
			if placedObject:FindFirstChild("Collisions") then
				for _, collision in pairs(placedObject.Collisions:GetChildren()) do
					collision.CanCollide = true
				end
			end


			placedObject.Parent = workspace:WaitForChild("PlacedObjects")
		end
	end
end)


--[[ EditObject.OnServerEvent:Connect(function(plr, obj, posX, posZ, posY, orientationY, price)
	if obj then
		if plr.leaderstats.Money.Value >= price then
			plr.leaderstats.Money.Value -= price
			
			local placedObject = obj:Clone()

			InitModel(placedObject)

			placedObject:SetPrimaryPartCFrame(CFrame.new(math.floor(posX),
				math.floor(posY), math.floor(posZ)) * CFrame.Angles(0, math.rad(orientationY), 0))

			local prompt = proximityPrompt:Clone()
			prompt.Enabled = true
			prompt.Parent = placedObject.PrimaryPart

			obj:Destroy()

			placedObject.Parent = workspace:WaitForChild("PlacedObjects")
		end
	end
end) --]]

LocalPlacementHandler:

local RS = game:GetService("ReplicatedStorage")
local TS = game:GetService("TweenService")

local TI = TweenInfo.new(.4, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)

local Objects = RS:WaitForChild("Objects")
local placementModule = require(script.Parent:WaitForChild("LocalPlacementModule"))

local objectsGroup = script.Parent:WaitForChild("ObjectsGroup")
local scrollingFrame = objectsGroup:WaitForChild("ScrollingFrame")

local objectButton = script:WaitForChild("ObjectButton"):Clone()
local isEnabled = false


local function loadObjects()
	local amount = 0
	
	for _, object in pairs(Objects:GetChildren()) do
		amount += 1
		local button = objectButton:Clone()
		
		button.Name = object.Name
		button.Text = object.Name 
		
		button.Parent = scrollingFrame
		button.Visible = true
		
		button.MouseButton1Click:Connect(function()
			if Objects:FindFirstChild(button.Name) then
				objectsGroup.Visible = false
				isEnabled = false
				placementModule:BuildModel(Objects[button.Name])
			end
		end)
	end
	
	scrollingFrame.CanvasSize = UDim2.new(0, 0, 0, amount * (objectButton.Size.Y.Offset + 12) * amount)
end


script.Parent:WaitForChild("BuildButton").MouseButton1Click:Connect(function()
	if not placementModule.Vars.Placing then
		isEnabled = not isEnabled
		
		if isEnabled then
			objectsGroup.Visible = true
			local Tween = TS:Create(objectsGroup, TI, {GroupTransparency = 0})
			Tween:Play()
			
		else
			local Tween = TS:Create(objectsGroup, TI, {GroupTransparency = 1})
			Tween:Play()
			Tween.Completed:Wait()
			
			objectsGroup.Visible = false
		end
	end
end)

loadObjects()

Module:

local module = {}

local UIS = game:GetService("UserInputService")
local TS = game:GetService("TweenService")
local RS = game:GetService("ReplicatedStorage")
local TI = TweenInfo.new(.25, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)

local PlacingGroup = script.Parent:WaitForChild("PlacingGroup")
local NameLabel = PlacingGroup:WaitForChild("NameLabel")
local PriceLabel = PlacingGroup:WaitForChild("PriceLabel")

local PlaceObject = RS:WaitForChild("PlaceObject")
local EditObject = RS:WaitForChild("EditObject")
local PlacedObjects = workspace:WaitForChild("PlacedObjects")

local highlight = script:WaitForChild("Highlight")

local plr = game.Players.LocalPlayer
local mouse = plr:GetMouse()
local selectedModel = nil

local connection = nil


module.Vars = {
	["Debounce"] = false;
	["Placing"] = false;
	["Rotating"] = false;
}


function module:ShowBuildInterface(model)
	NameLabel.Text = model.Name
	PriceLabel.Text = "$" .. model.Price.Value
	
	
	local Tween = TS:Create(PlacingGroup, TI, {GroupTransparency = 0, Position = UDim2.new(.5, 0, 1, 0)})
	Tween:Play()
	Tween.Completed:Wait()
	
	return
end


function module:HideBuildInterface()
	local Tween = TS:Create(PlacingGroup, TI, {GroupTransparency = 1, Position = UDim2.new(.5, 0, 1.05, 0)})
	Tween:Play()
	Tween.Completed:Wait()

	return
end


function module:WeldModel(model)
	for objs, obj in pairs(model:GetDescendants()) do
		if obj:IsA("BasePart") and obj ~= model.PrimaryPart then
			local weldConstraint = Instance.new("WeldConstraint")

			weldConstraint.Part0 = obj
			weldConstraint.Part1 = model.PrimaryPart

			weldConstraint.Parent = obj
			obj.Anchored = false
			obj.CanCollide = false
		end
	end

	mouse.TargetFilter = model

	module.Vars.Debounce = false
	model.PrimaryPart.CanCollide = false
end


function module:InitModel(model)
	module.Vars.Debounce = true
	
	local hitbox = Instance.new("Part")

	local orientation, size = model:GetBoundingBox()

	hitbox.Size = size
	hitbox.CFrame = orientation

	hitbox.Transparency = 1
	hitbox.Parent = model
	hitbox.Anchored = true
	model.PrimaryPart = hitbox

	module:WeldModel(model)

	return
end


function module:MoveModel(model)
	if not module.Vars.Rotating then
		local cf, size = model:GetBoundingBox()
		
		module.Vars.Debounce = true

		highlight.Enabled = true
		highlight.Adornee = model

		local pos = mouse.Hit.Position

		local roundedX = math.floor(pos.X)
		local roundedZ = math.floor(pos.Z)
		local roundedY = math.floor(pos.Y)
		local roundedsizeY = math.floor(size.Y)
		local orientationY = model.PrimaryPart.Orientation.Y

		local Tween = TS:Create(model.PrimaryPart, TI,
			{CFrame = CFrame.new(roundedX, roundedY + (roundedsizeY / 2), roundedZ) * CFrame.Angles(0, math.rad(orientationY), 0)})
		Tween:Play()
		Tween.Completed:Wait()

		module.Vars.Debounce = false
	end
end


function module:RotateModel(model)
	module.Vars.Rotating = true
	
	local Tween = TS:Create(model.PrimaryPart, TI,
		{CFrame = model.PrimaryPart.CFrame * CFrame.Angles(math.rad(0), math.rad(90), math.rad(0))})
	Tween:Play()
	Tween.Completed:Wait()
	
	module.Vars.Rotating = false
end


function module:BuildModel(model, isEditing)
	if not module.Vars.Placing then
		module.Vars.Placing = true
		
		selectedModel = model
		
		if not isEditing then
			selectedModel = model:Clone()
		end
		
		module:InitModel(selectedModel)
		
		selectedModel.Parent = workspace:WaitForChild("TemporaryObjects")
		
		module:ShowBuildInterface(selectedModel)
		module:MoveModel(selectedModel)
		
		connection = mouse.Move:Connect(function()
			module:MoveModel(selectedModel)
		end)
		
		mouse.Button1Down:Connect(function()
			if not module.Vars.Debounce and module.Vars.Placing then
				module.Vars.Placing = false
				
				if connection then
					connection:Disconnect()
					connection = nil
				end 
				
				if not isEditing then
					PlaceObject:FireServer(selectedModel.Name, selectedModel.PrimaryPart.Position.X,
						selectedModel.PrimaryPart.Position.Z,selectedModel.PrimaryPart.Position.Y, selectedModel.PrimaryPart.Orientation.Y)
					
				else
					EditObject:FireServer(selectedModel, selectedModel.PrimaryPart.Position.X,
						selectedModel.PrimaryPart.Position.Z,selectedModel.PrimaryPart.Position.Y, selectedModel.PrimaryPart.Orientation.Y)
				end
				
				workspace:WaitForChild("TemporaryObjects"):ClearAllChildren()
				module:HideBuildInterface()
			end
		end)
	end
end


UIS.InputBegan:Connect(function(input, gameEvent)
	if not gameEvent then
		if input.KeyCode == Enum.KeyCode.R then
			if selectedModel and not module.Vars.Rotating then
				module:RotateModel(selectedModel)
			end
		end
	end
end)


--[[ local function checkForProximityPrompts()
	task.wait()
	
	for models, model in pairs(PlacedObjects:GetChildren()) do
		if model:IsA("Model") then
			if model.PrimaryPart then
				model.PrimaryPart:WaitForChild("ProximityPrompt").Triggered:Connect(function(plr)
					if not module.Vars.Placing and not module.Vars.Debounce then
						module:BuildModel(model, true)
					end
				end)
			end
		end
	end
end --]]


-- PlacedObjects.ChildAdded:Connect(checkForProximityPrompts)
-- PlacedObjects.ChildRemoved:Connect(checkForProximityPrompts)

return module

1 Like

Oh my god… You fixed it! I’m actaully grateful for your help! You have 100% earned my respect, and i tried your game. it was neat.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.