I am currently trying to make a placement system where you can select an object from a GUI that you want to place, and it will be appear wherever your mouse clicks. The object however spawns halfway into the ground.
It won’t move from that spot no matter where your mouse clicks. You also can’t spawn another part as it will just overlap the past instance.
Client script:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local PlaceStructure = ReplicatedStorage:WaitForChild("PlaceStructure")
local Structures = ReplicatedStorage:WaitForChild("Structures")
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local Player = game.Players.LocalPlayer
local StructureFrame = script.Parent.StructureFrame
local Character = Player.Character or Player.Character:Wait()
local HumanoidRootPart = Character:WaitForChild("HumanoidRootPart")
local Mouse = Player:GetMouse()
local yBuildingOffset = 5
local MaxPlacingDistance = 50
local rKeyIsPressed = false
local PlacingStructure = false
for _, structureButton in pairs(StructureFrame:GetChildren()) do
if structureButton:IsA("TextButton") then
structureButton.MouseButton1Up:Connect(function()
StructureFrame.Visible = false
local yOrientation = 0
local GoodToPlace = false
local PlacedStructure
if PlacingStructure == false then
PlacingStructure = true
local ClientStructure = Structures:FindFirstChild(structureButton.Name):Clone()
ClientStructure.BrickColor = BrickColor.new("Forest green")
ClientStructure.Material = "Neon"
ClientStructure.CanCollide = false
ClientStructure.Parent = game.Workspace
local StartingCFrame = CFrame.new(0, -2, -15)
ClientStructure.CFrame = HumanoidRootPart.CFrame:ToWorldSpace(StartingCFrame)
RunService.RenderStepped:Connect(function()
local MouseRay = Mouse.UnitRay
local CastRay = Ray.new(MouseRay.Origin, MouseRay.Direction * 1000)
local IgnoreList = {ClientStructure, Character}
local hit, position = workspace:FindPartOnRayWithIgnoreList(CastRay, IgnoreList)
if hit and (HumanoidRootPart.Position - ClientStructure.Position).Magnitude < MaxPlacingDistance then --and (hit:IsA("Terrain") or hit.Name:lower() == "terrain")
GoodToPlace = true
ClientStructure.BrickColor = BrickColor.new("Forest green")
else
GoodToPlace = false
ClientStructure.BrickColor = BrickColor.new("Crimson")
end
local NewAnglesCFrame = CFrame.Angles(0, math.rad(yOrientation), 0)
local NewCFrame = CFrame.new(position.X, position.Y + yBuildingOffset, position.Z)
ClientStructure.CFrame = NewCFrame * NewAnglesCFrame
end)
UserInputService.InputBegan:Connect(function(input) -- GPE
if input.KeyCode == Enum.KeyCode.R then -- and not GPE then
rKeyIsPressed = true
local RotationSpeed = 5
while rKeyIsPressed do
task.wait()
if PlacingStructure == true then
yOrientation = yOrientation + RotationSpeed
end
end
end
end)
UserInputService.InputEnded:Connect(function(input)
if input.KeyCode == Enum.KeyCode.R then
rKeyIsPressed = false
end
end)
UserInputService.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
if PlacingStructure == true then
if GoodToPlace == true then
local StructureCFrame = ClientStructure.CFrame
PlacedStructure = PlaceStructure:InvokeServer(ClientStructure.Name, StartingCFrame)
if PlacedStructure == true then
PlacingStructure = false
ClientStructure:Destroy()
StructureFrame.Visible = true
end
end
end
end
end)
end
end)
end
end
Server script:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local PlaceStructure = ReplicatedStorage:WaitForChild("PlaceStructure")
local Structures = ReplicatedStorage:WaitForChild("Structures")
PlaceStructure.OnServerInvoke = function(Player, StructureName, StructureCFrame)
local Crafted
local RealStructure = Structures:FindFirstChild(StructureName):Clone()
if RealStructure then
RealStructure.CFrame = StructureCFrame
RealStructure.Parent = game.Workspace
Crafted = true
else
Crafted = false
end
return Crafted
end
And yes, I am aware that some things may be deprecated, as this was from an old tutorial. I can change things if need be, but I would prefer to keep it as is. Also, let me know if I need to explain anything better.
If you think you know what went wrong, please let me know. Thank you and have wonderful day!
You are placing the Part at the Position the mouse is pointing at on the baseplate. You need to add 1/2 the height of the Part you are placing to the CFrame Position of the mouse.
There are plenty of posts on the forums about “grid building”, “placing parts in game” etc. Try searching them up for better explanations than I can give.
It’s as @Scottifly said. I believe you can simply change this line
to
local NewCFrame = CFrame.new(position.X, position.Y + ClientStructure.Size.Y, position.Z)
(this is assuming the yOffset variable was to try and fix this issue, but wouldn’t work for every structure)
If the yOffset was different than I thought and was for a completely different purpose, you can do
local NewCFrame = CFrame.new(position.X, position.Y + yBuildingOffset + ClientStructure.Size.Y, position.Z)
If ClientStructure is a model, just change ClientStructure.Size.Y to ClientStructure.PrimaryPart.Size.Y and make the primary part an invisible bounding box of the model.
You can just spawn it at the mouse’s position but use :MoveTo() which accounts for physics and will move it to the nearest spot above the ground. It will also stop the object from being able to be spawned in a wall.
You can use :MoveTo() anywhere you move a model with CFrame. Note that :MoveTo() only works on models.
--CFrame version
RealStructure.CFrame = StructureCFrame
--MoveTo version
--get rid of the structure holder part if the realstructure is already a model
local structureHolder = Instance.new("Model")
structureHolder.Parent = game.Workspace
structureHolder.Name = "structureHolder"
RealStructure.Parent = structureHolder
structureHolder:MoveTo(StructureCFrame.Position) --:MoveTo() uses vector
--edit:fixed rotation. not sure if this works
structureHolder:PivotTo(structureHolder:GetPivot() * CFrame.Angles(StructureCFrame.Rotation))
It has to be noted that :MoveTo() will remove any rotation present on the model.
If the rotation of the model has to be preserved a different approach has to be used.
I’m guessing the model would have to be rotated to fit the normal vector of the surface the mouse is pointing at.
Sadly this won’t work for me. The structure is not a model, but a regular part I changed the properties on. This could probably work for me in the future though, as I will most likely include models to be selected later on.
Are you adding the (workspace oriented, not Part oriented) Y size of the placed Part, or 1/2 the Y size?
You need to place the Position of the Part 1/2 the Y value to make it sit right on the ground.
If your Part is 10 studs high in the workspace Y direction then it’s placed Position needs to sit 1/2 of the Y value to be centered 5 studs above the Mouse Postion.
This approach wouldn’t account for the rotation of an object. If a part was flipped so that its up vector was pointing in a different direction than the y axis the part would no longer align correctly.
The code I made accounts for the fact that the structure could be a part and already puts it in a model. I made a comment above saying to remove that part if you decide to change to models later.
I rescripted the whole system in a new game with nothing inside it, so other scripts wouldn’t interfere. But sadly, I’m still having the same issue as before.
Main Client Script:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local PlaceStructure = ReplicatedStorage:WaitForChild("PlaceStructure")
local Structures = ReplicatedStorage:WaitForChild("Structures")
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local Player = game.Players.LocalPlayer
local StructureFrame = script.Parent.StructureFrame
local Character = Player.Character or Player.Character:Wait()
local HumanoidRootPart = Character:WaitForChild("HumanoidRootPart")
local Mouse = Player:GetMouse()
local yBuildingOffset = 5
local MaxPlacingDistance = 50
local rKeyIsPressed = false
local PlacingStructure = false
for _, StructureButton in pairs(StructureFrame:GetChildren()) do
if StructureButton:IsA("TextButton") then
StructureButton.MouseButton1Click:Connect(function()
StructureFrame.Visible = false
local yOrientation = 0
local GoodToPlace = false
local PlacedStructure
if PlacingStructure == false then
PlacingStructure = true
local ClientStructure = Structures:FindFirstChild(StructureButton.Name):Clone()
ClientStructure.BrickColor = BrickColor.new("Forest green")
ClientStructure.Material = "Neon"
ClientStructure.CanCollide = false
ClientStructure.Parent = game.Workspace
local StartingCFrame = CFrame.new(0, -2, -15)
ClientStructure.CFrame = HumanoidRootPart.CFrame:ToWorldSpace(StartingCFrame)
RunService.RenderStepped:Connect(function()
local MouseRay = Mouse.UnitRay
local CastRay = Ray.new(MouseRay.Origin, MouseRay.Direction * 1000)
local IgnoreList = {ClientStructure, Character}
local hit, position = workspace:FindPartOnRayWithIgnoreList(CastRay, IgnoreList)
if hit and (hit:IsA("Terrain") or hit.Name:lower() == "terrain") and (HumanoidRootPart.Position - ClientStructure.Position).Magnitude < MaxPlacingDistance then
GoodToPlace = true
ClientStructure.BrickColor = BrickColor.new("Forest green")
else
GoodToPlace = false
ClientStructure.BrickColor = BrickColor.new("Crimson")
end
local NewAnglesCFrame = CFrame.Angles(0, math.rad(yOrientation), 0)
local NewCFrame = CFrame.new(position.X, position.Y + yBuildingOffset + ClientStructure.Size.Y, position.Z)
ClientStructure.CFrame = NewCFrame * NewAnglesCFrame
end)
UserInputService.InputBegan:Connect(function(input)
if input.KeyCode == Enum.KeyCode.R then
rKeyIsPressed = true
local RotationSpeed = 5
while rKeyIsPressed do
task.wait()
if PlacingStructure == true then
yOrientation = yOrientation + RotationSpeed
end
end
end
end)
UserInputService.InputEnded:Connect(function(input)
if input.KeyCode == Enum.KeyCode.R then
rKeyIsPressed = false
end
end)
UserInputService.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
if PlacingStructure == true then
if GoodToPlace == true then
local StructureCFrame = ClientStructure.CFrame
PlacedStructure = PlaceStructure:InvokeServer(ClientStructure.Name, StartingCFrame)
if PlacedStructure == true then
PlacedStructure = false
ClientStructure:Destroy()
StructureFrame.Visible = false
end
end
end
end
end)
end
end)
end
end
Server script:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local PlaceStructure = ReplicatedStorage:WaitForChild("PlaceStructure")
local Structures = ReplicatedStorage:WaitForChild("Structures")
PlaceStructure.OnServerInvoke = function(Player, StructureName, StructureCFrame)
local crafted
local RealStructure = Structures:FindFirstChild(StructureName):Clone()
if RealStructure then
RealStructure.CFrame = StructureCFrame
RealStructure.Parent = game.Workspace
crafted = true
else
crafted = false
end
return crafted
end
Just going to bump this because I really need to help fixing this.
I made a few small tweaks to the code, but nothing real big as the issue persists. I marked where I think the bug is happening.
Hierarchy:
LocalScript:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local PlaceStructure = ReplicatedStorage:WaitForChild("PlaceStructure")
local Structures = ReplicatedStorage:WaitForChild("Structures")
print(PlaceStructure, Structures)
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local Player = game.Players.LocalPlayer
local StructureFrame = script.Parent:WaitForChild("StructureFrame")
local Character = Player.Character or Player.CharacterAdded:Wait()
local HumanoidRootPart = Character:WaitForChild("HumanoidRootPart")
local Mouse = Player:GetMouse()
local yBuildingOffset = 5
local MaxPlacingDistance = 50
local rKeyIsPressed = false
local PlacingStructure = false
for _, structureButton in pairs(StructureFrame:GetChildren()) do
if structureButton:IsA("TextButton") then
structureButton.MouseButton1Click:Connect(function()
StructureFrame.Visible = false
local yOrientation = 0
local GoodToPlace = false
local PlacedStructure
if PlacingStructure == false then
PlacingStructure = true
local ClientStructure = Structures:FindFirstChild(structureButton.Name):Clone()
ClientStructure.BrickColor = BrickColor.new("Forest green")
ClientStructure.Material = "Neon"
ClientStructure.CanCollide = false
ClientStructure.Parent = game.Workspace
--ISSUE
local StartingCFrame = CFrame.new(0, -2, -15)
ClientStructure.CFrame = HumanoidRootPart.CFrame:ToWorldSpace(StartingCFrame)
--
RunService.RenderStepped:Connect(function()
local MouseRay = Mouse.UnitRay
local CastRay = Ray.new(MouseRay.Origin, MouseRay.Direction * 1000)
local IgnoreList = {ClientStructure, Character}
local hit, position = workspace:FindPartOnRayWithIgnoreList(CastRay, IgnoreList) --Maybe replace this with the workspace:Raycast() function and use RaycastParams.
if hit then --and (HumanoidRootPart.Position - ClientStructure.Position).Magnitude < MaxPlacingDistance and (hit:IsA("Terrain") or hit.Name:lower() == "terrain")
GoodToPlace = true
ClientStructure.BrickColor = BrickColor.new("Forest green")
else
GoodToPlace = false
ClientStructure.BrickColor = BrickColor.new("Crimson")
end
local NewAnglesCFrame = CFrame.Angles(0, math.rad(yOrientation), 0)
local NewCFrame = CFrame.new(position.X, position.Y + yBuildingOffset, position.Z)
ClientStructure.CFrame = NewCFrame * NewAnglesCFrame
end)
UserInputService.InputBegan:Connect(function(input)
if input.KeyCode == Enum.KeyCode.R then
rKeyIsPressed = true
local RotationSpeed = 5
while rKeyIsPressed do
task.wait()
if PlacingStructure == true then
yOrientation += RotationSpeed
end
end
end
end)
UserInputService.InputEnded:Connect(function(input)
if input.KeyCode == Enum.KeyCode.R then
rKeyIsPressed = false
end
end)
UserInputService.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
if PlacingStructure == true then
if GoodToPlace == true then
local StructureCFrame = ClientStructure.CFrame
PlacedStructure = PlaceStructure:InvokeServer(ClientStructure.Name, StartingCFrame)
if PlacedStructure == true then
PlacingStructure = false
ClientStructure:Destroy()
StructureFrame.Visible = true
GoodToPlace = false --Don't know if I have to set this back to false or not but I did anyway lol
end
end
end
end
end)
end
end)
end
end
Server:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local PlaceStructure = ReplicatedStorage:WaitForChild("PlaceStructure")
local Structures = ReplicatedStorage:WaitForChild("Structures")
PlaceStructure.OnServerInvoke = function(Player, StructureName, StructureCFrame)
local Crafted = false
local RealStructure = Structures:FindFirstChild(StructureName):Clone()
if RealStructure then
RealStructure.CFrame = StructureCFrame
RealStructure.Parent = game.Workspace.PlacedStructures
Crafted = true
else
Crafted = false
end
return Crafted
end
Also, for some reason, whenever an object is placed, WoodWall is duplicated twice. I just noticed this and am not real sure why this is occurring either.