I've been working on a grappling system like Attack On Titan: Revenge and I am stuck on why my RopeConstraint isn't showing up. Here is my code so far.

local UIS = game:GetService("UserInputService")
local plr = game.Players.LocalPlayer
local char = plr.Character
local hum = char:WaitForChild("Humanoid")
local humRootPart = char:FindFirstChild("HumanoidRootPart")
local mouse = plr:GetMouse()
local cam = workspace.CurrentCamera

function grappleLeft()
	UIS.InputBegan:Connect(function(input, gameProcessedEvent)
		if input.KeyCode == Enum.KeyCode.E then
			local ray = Ray.new(humRootPart.Position, (mouse.Hit.p - humRootPart.Position).unit * 300)
			local hit, position, normal = game.Workspace:FindPartOnRay(ray, char)
			local distance = (position - humRootPart.Position).magnitude
			
			if hit and normal then
				print("Ray hit a part!")
				local grappleRight = Instance.new("RopeConstraint", humRootPart)
				local at0 = Instance.new("Attachment", humRootPart)
				at0.Position = humRootPart.Position
				local at1 = Instance.new("Attachment", humRootPart)
				at1.Position = position
				grappleRight.Name = "GrappleRight"
				grappleRight.Attachment0 = at0
				grappleRight.Attachment1 = at1
				grappleRight.Visible = true
				grappleRight.Enabled = true
				grappleRight.Color = BrickColor.new("Black")
				grappleRight.Length = distance
				grappleRight.Thickness = Vector3.new(0.5, 0.5, distance)
			end
		end
	end)
end

grappleLeft()