I’m trying to create a system where players can build cars. It works fine for the first item the player chooses, but when they choose a second item, then there’s a problem. The ghostPart that shows the user where the part will be places, flickers around the screen when the user chooses a second part to place.
Here is a video to show more of what I mean:
Here is the local script:
local inventoryFrame = script.Parent.Parent.InventoryFrame
local button = script.Parent
local basicFunctions = require(game.ReplicatedStorage.Functions.BasicFunctions)
local conversions = require(game.ReplicatedStorage.Conversions)
local placeItem = game.ReplicatedStorage.PlaceItem
local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
local player = Players.LocalPlayer
local mouse = player:GetMouse()
local camera = Workspace.CurrentCamera
local matchRotation = true
local gridSize = 1
local function roundToGrid(value)
return math.round(value / gridSize) * gridSize
end
local function getPlacementCFrame(ghostPart)
local origin = camera.CFrame.Position
local direction = (mouse.Hit.Position - origin).Unit * 1000
local rayParams = RaycastParams.new()
rayParams.FilterDescendantsInstances = {player.Character, ghostPart}
rayParams.FilterType = Enum.RaycastFilterType.Exclude
local result = workspace:Raycast(origin, direction, rayParams)
if not result then return nil end
local hitPos = result.Position
local normal = result.Normal
local target = result.Instance
local up = normal
local forward
if math.abs(normal:Dot(Vector3.new(0, 1, 0))) > 0.9 then
forward = target.CFrame.LookVector
else
forward = (camera.CFrame.Position - hitPos)
forward = Vector3.new(forward.X, 0, forward.Z)
if forward.Magnitude == 0 then forward = Vector3.new(0, 0, -1) end
forward = forward.Unit
end
local right = forward:Cross(up).Unit
forward = up:Cross(right).Unit
local ghostSize = ghostPart.Size
local offset = ghostSize.Y / 2
local normalAbs = Vector3.new(math.abs(normal.X), math.abs(normal.Y), math.abs(normal.Z))
if normalAbs.X > normalAbs.Y and normalAbs.X > normalAbs.Z then
offset = ghostSize.X / 2
elseif normalAbs.Z > normalAbs.X and normalAbs.Z > normalAbs.Y then
offset = ghostSize.Z / 2
end
local exactPos = hitPos + normal * offset
local snappedPos = Vector3.new(
roundToGrid(exactPos.X),
roundToGrid(exactPos.Y),
roundToGrid(exactPos.Z)
)
return CFrame.fromMatrix(snappedPos, right, up)
end
local function placePart(id, class)
local part = conversions.idToPart(id, class)
local ghostTemplate = part
local ghostPart = ghostTemplate:Clone()
ghostPart.Anchored = true
ghostPart.CanCollide = false
ghostPart.Transparency = 0.5
ghostPart.Name = "GhostPart"
ghostPart.Parent = workspace
mouse.TargetFilter = ghostPart
RunService.RenderStepped:Connect(function()
local cf = getPlacementCFrame(ghostPart)
if cf then
ghostPart.CFrame = cf
end
end)
mouse.Button1Down:Connect(function()
local cf = getPlacementCFrame(ghostPart)
local ghostPart = ghostTemplate:Clone()
ghostPart.Anchored = true
ghostPart.CanCollide = false
ghostPart.Transparency = 0.5
ghostPart.Name = "GhostPart"
ghostPart.Parent = workspace
mouse.TargetFilter = ghostPart
placeItem:FireServer(cf, id, class)
end)
end
button.MouseButton1Click:Connect(function()
inventoryFrame.Visible = true
inventoryFrame.Inventory.Text = "Choose a part"
for _, button:TextButton in basicFunctions.GetAllChildrenWhichAreA(inventoryFrame.ScrollingFrame, "TextButton") do
button.MouseButton1Click:Connect(function()
inventoryFrame.Visible = false
local id = button:GetAttribute("Id")
local class = button:GetAttribute("Class")
placePart(id, class)
end)
end
end)