How to fix weld interfering with grab/part carry system

I’m trying to make a grab system and everything was going fine until the curse of the blue orb. basically I tried adding a blue orb on the location of the part where the player first grabbed. Seemed simple enough I already had the grabbing itself working fine. But now this one blue dot has made it super janky. I think its because of the weld constraint being created mid script between it and the part but i’m not too sure. before adding it grabbing stuff would be fine but now I have to basically jumpstart it by pushing the blocks around and even then it only works good sometimes.

Anyone know of any solutions or alternate ways to achieve this look? I don’t want to just flat out remove it but if there’s any other way to display the 3d orb in that specific position that would be great. Also already tried just moving it there with heartbeat but its too slow for rapid movements like that.

here are the scripts, main one to look at is the server script since it has the ball:

Server script:

local replicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")

local requestOwnership = replicatedStorage:WaitForChild("RequestOwnership")
local startGrab = replicatedStorage:WaitForChild("StartGrab")
local stopGrab = replicatedStorage:WaitForChild("StopGrab")
local updateGrabTarget = replicatedStorage:WaitForChild("UpdateGrabTarget")

local playerData = {}

requestOwnership.OnServerEvent:Connect(function(player, part)
	if part and part:IsA("BasePart") and not part.Anchored then
		part:SetNetworkOwner(player)
	end
end)

startGrab.OnServerEvent:Connect(function(player, part, hitPosition)
	local char = player.Character
	if not char then return end

	local hand = char:FindFirstChild("RightHand") or char:FindFirstChild("Right Arm")
	if not hand then return end

	if playerData[player] then
		local d = playerData[player]
		if d.Beam then d.Beam:Destroy() end
		if d.HandPart then d.HandPart:Destroy() end
		if d.Connection then d.Connection:Disconnect() end
		if d.GlowBall then d.GlowBall:Destroy() end
	end

	-- Hand beam part
	local handPart = Instance.new("Part")
	handPart.Size = Vector3.new(0.3, 0.3, 0.3)
	handPart.Anchored = false
	handPart.CanCollide = false
	handPart.Transparency = 1
	handPart.Name = "BeamHandPart"
	handPart.CFrame = hand.CFrame * CFrame.new(0, -1, 0)
	handPart.Parent = workspace

	local handWeld = Instance.new("WeldConstraint")
	handWeld.Part0 = handPart
	handWeld.Part1 = hand
	handWeld.Parent = handPart

	local handAttach = Instance.new("Attachment")
	handAttach.Rotation = Vector3.new(0, 0, -90)
	handAttach.Parent = handPart

	-- Attachment on part
	local localOffset = part.CFrame:PointToObjectSpace(hitPosition)
	local partAttach = Instance.new("Attachment")
	partAttach.Position = localOffset
	partAttach.Rotation = Vector3.new(0, 0, -90)
	partAttach.Parent = part

	-- Glowing ball
	local glowBall = Instance.new("Part")
	glowBall.Name = "GlowBall"
	glowBall.Shape = Enum.PartType.Ball
	glowBall.Material = Enum.Material.Neon
	glowBall.Color = Color3.fromRGB(0, 200, 255)
	glowBall.Size = Vector3.new(0.4, 0.4, 0.4)
	glowBall.Anchored = false
	glowBall.CanCollide = false
	glowBall.CanTouch = false
	glowBall.CanQuery = false
	glowBall.Massless = true
	glowBall.CFrame = part.CFrame * CFrame.new(localOffset)
	glowBall.Parent = part

	local weld = Instance.new("WeldConstraint")
	weld.Part0 = glowBall
	weld.Part1 = part
	weld.Parent = part

	-- Beam
	local beam = Instance.new("Beam")
	beam.Attachment0 = handAttach
	beam.Attachment1 = partAttach
	beam.Color = ColorSequence.new(Color3.fromRGB(100, 200, 255))
	beam.Width0 = 0.2
	beam.Width1 = 0.2
	beam.LightEmission = 1
	beam.FaceCamera = true
	beam.TextureSpeed = 2
	beam.CurveSize0 = 0
	beam.CurveSize1 = -2
	beam.Parent = handPart

	-- Animate beam curve
	local t0 = tick()
	local conn = RunService.Heartbeat:Connect(function()
		if beam then
			beam.CurveSize1 = math.sin((tick() - t0) * 2) * -2
		end
	end)

	playerData[player] = {
		Beam = beam,
		HandPart = handPart,
		Connection = conn,
		GlowBall = glowBall,
	}
end)

updateGrabTarget.OnServerEvent:Connect(function(player, _)
	-- No need to process here due to beam visuals being auto-attached (But need this to prevent remote event exhaustion)
end)

stopGrab.OnServerEvent:Connect(function(player)
	local d = playerData[player]
	if d then
		if d.Beam then d.Beam:Destroy() end
		if d.HandPart then d.HandPart:Destroy() end
		if d.Connection then d.Connection:Disconnect() end
		if d.GlowBall then d.GlowBall:Destroy() end
	end
	playerData[player] = nil
end)

Local Script:

local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local runService = game:GetService("RunService")
local replicatedStorage = game:GetService("ReplicatedStorage")
local userInputService = game:GetService("UserInputService")

local requestOwnership = replicatedStorage:WaitForChild("RequestOwnership")
local startGrab = replicatedStorage:WaitForChild("StartGrab")
local stopGrab = replicatedStorage:WaitForChild("StopGrab")
local updateGrabTarget = replicatedStorage:WaitForChild("UpdateGrabTarget")

local holdingPart = nil
local vectorForce = nil
local attachment = nil
local grabbing = false

-- Arm physics
local rightShoulder = nil
local originalC0 = nil
local currentArmAngle = 0
local armVelocity = 0

local holdDistance = 10

userInputService.InputBegan:Connect(function(input, processed)
	if processed or input.UserInputType ~= Enum.UserInputType.MouseButton1 or grabbing then return end

	local target = mouse.Target
	if target and target:IsA("BasePart") and not target.Anchored then
		-- Clean up any old grab
		if vectorForce then vectorForce:Destroy() end
		if attachment then attachment:Destroy() end

		holdingPart = target
		local hitPos = mouse.Hit.Position

		requestOwnership:FireServer(target)
		startGrab:FireServer(target, hitPos)

		task.delay(0.05, function()
			if holdingPart and holdingPart.Parent then
				attachment = Instance.new("Attachment")
				attachment.Name = "GrabAttachment"
				attachment.Parent = holdingPart

				vectorForce = Instance.new("VectorForce")
				vectorForce.Name = "GrabForce"
				vectorForce.Attachment0 = attachment
				vectorForce.RelativeTo = Enum.ActuatorRelativeTo.World
				vectorForce.ApplyAtCenterOfMass = true
				vectorForce.Force = Vector3.zero
				vectorForce.Parent = holdingPart
			end
		end)

		local char = player.Character or player.CharacterAdded:Wait()
		local torso = char:FindFirstChild("Torso")
		local arm = char:FindFirstChild("Right Arm")
		if torso and arm then
			rightShoulder = torso:FindFirstChild("Right Shoulder")
			if rightShoulder and rightShoulder:IsA("Motor6D") then
				originalC0 = rightShoulder.C0
			end
		end

		grabbing = true
	end
end)

userInputService.InputEnded:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 and grabbing then
		stopGrab:FireServer()
		if vectorForce then vectorForce:Destroy() end
		if attachment then attachment:Destroy() end
		holdingPart = nil
		vectorForce = nil
		attachment = nil
		grabbing = false
	end
end)

runService.RenderStepped:Connect(function()
	local char = player.Character
	if not char then return end

	if grabbing and holdingPart and holdingPart.Parent and vectorForce then
		local unitRay = mouse.UnitRay
		local massFactor = math.clamp(holdingPart.AssemblyMass / 25, 0.8, 1.5)
		local targetPos = unitRay.Origin + unitRay.Direction * holdDistance * massFactor

		local bobOffset = math.sin(tick() * 2) * 0.2
		targetPos += Vector3.new(0, bobOffset, 0)

		updateGrabTarget:FireServer(targetPos)

		local velocity = (targetPos - holdingPart.Position) * 50
		local drag = holdingPart.AssemblyLinearVelocity * 5
		vectorForce.Force = (velocity - drag) * holdingPart.AssemblyMass
	end

	local torso = char:FindFirstChild("Torso")
	if torso and rightShoulder and originalC0 then
		local torsoPos = torso.Position
		local aimTarget = (holdingPart and holdingPart.Position) or (torsoPos + Vector3.new(0, 0, -10))
		local direction = (aimTarget - torsoPos).Unit
		local verticalOffset = direction.Y
		local targetAngle = math.clamp(verticalOffset * 2, -1.2, 1.2)

		local stiffness = 0.15
		local damping = 0.2

		local force = (targetAngle - currentArmAngle) * stiffness
		armVelocity = armVelocity + force - armVelocity * damping
		currentArmAngle = currentArmAngle + armVelocity

		rightShoulder.C0 = originalC0 * CFrame.Angles(0, 0, currentArmAngle)
	end
end)

Thanks!

Hmm, it may be that mouse.target and/or the ray you’re getting from the mouse are still hitting the orb even though you do have collision and touch disabled.

1 Like

I thought it was that too initially, but even after moving the orb a whole stud away from the click position it still causes it to be janky. It’s very odd, it starts off not working at all but after clicking the part for like a minute then the movement works fine.

After testing a bit more by putting the ball creation in a delayed function it seems that the weld is causing it to just drop and breaking the grab functionality.

Only solution ive found thus far is just making the circle out of beams and having it face the player. Any type of welding seems to just break the functionality of it for some time.

Have you tried looking at the assembly at runtime (after welding the ball on) and making sure that the ball did not become the assembly root? Parts have a RootPriority you can use to get control over this, normally by increasing priority of the Part you want the root to be. If a part you weld on becomes the new assembly root, Part.Massless is not respected and the part has its full mass.