I’ve just spent the last two days trying to make this work, and I’ve finally done it! However, my code is pretty unoptimized. I’d like to place special emphasis on the hingePreviewForAttachment()
function, as it’s ran every heartbeat. By the way, this isn’t my entire code, this is only a small chunk of it. All the variables that aren’t defined (don’t have ‘local’ before them) are defined somewhere else, no variables are global, I checked. Thank y’all so much in advance!
local rodPreview = nil
local function createHingePreviewForAttachment()
outline = Instance.new("SelectionBox")
outline.Color3 = Color3.fromRGB(25, 153, 255)
outline.LineThickness = 0.02
outline.Parent = workspace.origin
highlight = Instance.new("Highlight")
highlight.FillColor = Color3.new(1,1,0)
highlight.DepthMode = Enum.HighlightDepthMode.Occluded
highlight.Parent = workspace.origin
highlight.Enabled = false
rodPreview = Instance.new("Part")
rodPreview.Shape = Enum.PartType.Cylinder
rodPreview.Color = Color3.fromRGB(30,50,60)
rodPreview.Material = Enum.Material.Neon
rodPreview.CastShadow = false
rodPreview.CanCollide = false
rodPreview.CanQuery = false
rodPreview.Anchored = true
rodPreview.Parent = workspace.origin
attachPreview = Instance.new("Part")
attachPreview.Shape = Enum.PartType.Ball
attachPreview.Size = Vector3.new(0.3,0.3,0.3)
attachPreview.Color = Color3.fromRGB(0, 240, 0)
attachPreview.Material = Enum.Material.SmoothPlastic
attachPreview.CastShadow = false
attachPreview.CanCollide = false
attachPreview.CanQuery = false
attachPreview.Anchored = true
attachPreview.Parent = workspace.origin
spherHingePreview = Instance.new("Part")
spherHingePreview.Shape = Enum.PartType.Cylinder
spherHingePreview.Size = Vector3.new(0.4, 0.2, 0.2)
spherHingePreview.Color = Color3.new(1,1,0)
spherHingePreview.Material = Enum.Material.SmoothPlastic
spherHingePreview.CastShadow = false
spherHingePreview.CanCollide = false
spherHingePreview.CanQuery = false
spherHingePreview.Anchored = true
spherHingePreview.Parent = workspace.origin
AttachmentArrowsClone = AttachmentArrows:Clone()
AttachmentArrowsClone.Parent = workspace.origin
AttachmentArrowsClone.Name = "AttachmentArrowsCloneAttachment"
for i, v in AttachmentArrowsClone:GetChildren() do
table.insert(raycastSelectionFilterList, v)
end
CastParams.FilterDescendantsInstances = raycastSelectionFilterList
end
local function removeHingePreviewForAttachment()
outline:Destroy()
highlight:Destroy()
rodPreview:Destroy()
attachPreview:Destroy()
spherHingePreview:Destroy()
for i, v in AttachmentArrowsClone:GetChildren() do
table.remove(raycastSelectionFilterList, table.find(raycastSelectionFilterList, v))
end
AttachmentArrowsClone:Destroy()
end
local previousHinge = nil -- an instance
local function hingePreviewForAttachment()
local mouseLocation = UserInputService:GetMouseLocation()
local unitRay = camera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
local cast = workspace:Raycast(unitRay.Origin, unitRay.Direction * 1000, CastParams)
highlight.Enabled = false
if cast then
if previousHinge ~= nil and cast.Instance ~= workspace.Baseplate then
if cast.Instance:FindFirstChild("Attachment") then
if cast.Instance.Parent ~= previousHinge.Parent then
local pos1 = cast.Instance.Position
local pos2 = previousHinge.Position
rodPreview.CFrame = CFrame.lookAt(pos1, pos2) * CFrame.Angles(0,math.rad(90),0)
rodPreview.Position = Vector3.new( (pos1.X+pos2.X)/2 , (pos1.Y+pos2.Y)/2 , (pos1.Z+pos2.Z)/2 )
rodPreview.Size = Vector3.new( math.sqrt((pos2.X-pos1.X)^2 + (pos2.Y-pos1.Y)^2 + (pos2.Z-pos1.Z)^2) ,0.1,0.1)
highlight.Enabled = true
highlight.Adornee = cast.Instance
attachPreview.Position = Vector3.new(0,-10,0)
spherHingePreview.CFrame = cast.Instance.CFrame
AttachmentArrowsClone:PivotTo( CFrame.new(0,-10,0) )
return
end
elseif cast.Instance ~= previousHinge.Parent then
local pos1 = Vector3.new( math.round(cast.Position.X / GridSize.Value) , math.round(cast.Position.Y / GridSize.Value) , math.round(cast.Position.Z / GridSize.Value) ) * GridSize.Value
local pos2 = previousHinge.Position
rodPreview.CFrame = CFrame.lookAt(pos1, pos2) * CFrame.Angles(0,math.rad(90),0)
rodPreview.Position = Vector3.new( (pos1.X+pos2.X)/2 , (pos1.Y+pos2.Y)/2 , (pos1.Z+pos2.Z)/2 )
rodPreview.Size = Vector3.new( math.sqrt((pos2.X-pos1.X)^2 + (pos2.Y-pos1.Y)^2 + (pos2.Z-pos1.Z)^2) ,0.1,0.1)
local gridPosition = Vector3.new( math.round(cast.Position.X / GridSize.Value) , math.round(cast.Position.Y / GridSize.Value) , math.round(cast.Position.Z / GridSize.Value) ) * GridSize.Value
outline.Adornee = cast.Instance
attachPreview.Position = gridPosition
spherHingePreview.Position = gridPosition
AttachmentArrowsClone:PivotTo( CFrame.new(gridPosition) )
return
end
end
if cast.Instance:FindFirstChild("Attachment") then -- when the cursor is over an attachment
highlight.Enabled = true
highlight.Adornee = cast.Instance
rodPreview.Position = Vector3.new(0,-10,0)
attachPreview.Position = Vector3.new(0,-10,0)
spherHingePreview.CFrame = cast.Instance.CFrame
AttachmentArrowsClone:PivotTo( CFrame.new(0,-10,0) )
return
else -- when the cursor is over a part
local gridPosition = Vector3.new( math.round(cast.Position.X / GridSize.Value) , math.round(cast.Position.Y / GridSize.Value) , math.round(cast.Position.Z / GridSize.Value) ) * GridSize.Value
outline.Adornee = cast.Instance
rodPreview.Position = Vector3.new(0,-10,0)
attachPreview.Position = gridPosition
spherHingePreview.Position = gridPosition
AttachmentArrowsClone:PivotTo( CFrame.new(gridPosition) )
return
end
else
attachPreview.Position = Vector3.new(0,-10,0)
spherHingePreview.Position = Vector3.new(0,-10,0)
return
end
end
local function createHingeToPlace()
local hinge = Instance.new("Part")
hinge.Shape = Enum.PartType.Cylinder
hinge.Size = Vector3.new(0.4, 0.2, 0.2)
hinge.Color = Color3.new(1,1,0)
hinge.Material = Enum.Material.SmoothPlastic
hinge.CastShadow = false
hinge.CanCollide = false
hinge.Anchored = false
hinge.Massless = true
hinge.Name = "Hinge"
return hinge
end
local function placeHingeOnAttachment(_name, inputState, _inputObj)
if inputState == Enum.UserInputState.Begin then
if outline.Adornee == workspace.Baseplate and highlight.Enabled == false then
print("a hinge can't be placed on the baseplate")
return
end
for i, v in placedSpherHingeArray do
if v.Position == spherHingePreview.Position then
table.remove(raycastSelectionFilterList, table.find(raycastSelectionFilterList, v))
table.remove(raycastAttachmentFilterList, table.find(raycastAttachmentFilterList, v))
table.remove(placedSpherHingeArray, i)
v:Destroy()
CastParams.FilterDescendantsInstances = raycastSelectionFilterList
break
end
end
for i, v in placedAttachmentArray do -- replace hinge
if v.Position == spherHingePreview.Position then
local hinge = createHingeToPlace()
hinge.CFrame = spherHingePreview.CFrame
hinge.Parent = highlight.Adornee.Parent
local weld = Instance.new("WeldConstraint")
weld.Part0 = hinge
weld.Part1 = highlight.Adornee
weld.Parent = hinge
local hingeConstraint = Instance.new("HingeConstraint")
hingeConstraint.Attachment0 = highlight.Adornee:FindFirstChildOfClass("Attachment")
hingeConstraint.Parent = hinge
previousHinge = hinge
table.insert(placedSpherHingeArray, hinge)
table.insert(raycastSelectionFilterList, hinge)
table.insert(raycastAttachmentFilterList, hinge)
CastParams.FilterDescendantsInstances = raycastSelectionFilterList
return
end
end
local part = nil
local Attachment = nil
if highlight.Enabled == false then -- the cursor is over an attachment
part = Instance.new("Part")
part.Position = attachPreview.Position
part.Shape = Enum.PartType.Ball
part.Size = Vector3.new(0.3,0.3,0.3)
part.Color = Color3.fromRGB(0, 130, 0)
part.Material = Enum.Material.SmoothPlastic
part.CastShadow = false
part.CanCollide = false
part.Anchored = true
part.Massless = true
part.Name = "GreenAttachmentBall"
part.Parent = outline.Adornee --cast.instance
Attachment = Instance.new("Attachment")
Attachment.Parent = part
Attachment.WorldCFrame = CFrame.new(attachPreview.Position)
local weld = Instance.new("WeldConstraint")
weld.Part0 = part
weld.Part1 = outline.Adornee
weld.Parent = part
end
local hinge = createHingeToPlace()
local weld = Instance.new("WeldConstraint")
weld.Part0 = hinge
weld.Parent = hinge
local hingeConstraint = Instance.new("HingeConstraint")
hingeConstraint.Parent = hinge
if highlight.Enabled == true then
hinge.Parent = highlight.Adornee.Parent
hinge.Position = attachPreview.Position
weld.Part1 = highlight.Adornee
hingeConstraint.Attachment0 = highlight.Adornee:FindFirstChildOfClass("Attachment")
else
hinge.Parent = outline.Adornee --cast.instance
hinge.CFrame = part.CFrame
weld.Part1 = part
hingeConstraint.Attachment0 = Attachment
end
if rodPreview.Position ~= Vector3.new(0,-10,0) then
local rod = rodPreview:Clone()
rod.Name = "Rod"
rod.Parent = vehicleFolder
local attachment1 = Instance.new("Attachment")
attachment1.Parent = rod
attachment1.WorldCFrame = previousHinge.CFrame -- the attachment that the rod is attaching to
previousHinge:FindFirstChildOfClass("HingeConstraint").Attachment1 = attachment1
local attachment2 = Instance.new("Attachment")
attachment2.Parent = rod
attachment2.WorldCFrame = hinge.CFrame
hingeConstraint.Attachment1 = attachment2
rod.CanQuery = true
rod.Anchored = false
table.insert(raycastSelectionFilterList, rod)
previousHinge = nil
else
previousHinge = hinge
end
table.insert(raycastSelectionFilterList, hinge)
table.insert(raycastAttachmentFilterList, part)
table.insert(raycastAttachmentFilterList, hinge)
table.insert(placedAttachmentArray, part)
table.insert(placedSpherHingeArray, hinge)
CastParams.FilterDescendantsInstances = raycastSelectionFilterList
end
end