Constraining Neck C0 Angle

Hey, and thanks for reading in advance.

I was following this link for guidance on how to make a character’s head point towards another using the Neck joint, but issues came about trying to constrain the angle the neck joint was allowed to rotate:

The princess here has owl-syndrome, being able to 180 entirely - while at the same time being unable to turn her head towards her right, in either direction.

Sauce:

RunService.Heartbeat:Connect(function(Delta)
	local lookDir = Root.CFrame.LookVector
	local lookTargets = {}
	
	local function worldCFrameToC0ObjectSpace(Joint, WorldCF)
		local part1CF = Joint.Part1.CFrame
		
		local c1Store = Joint.C1
		local c0Store = Joint.C0
		
		local relativeToPart1 = c0Store * c1Store:Inverse() * part1CF:Inverse() * WorldCF * c1Store
		relativeToPart1 -= relativeToPart1.Position

		local goalC0CFrame = relativeToPart1 + c0Store.Position
		
		return goalC0CFrame
	end

	for _,Player in pairs(game.Players:GetPlayers()) do
		if Player.Character and Player.Character:FindFirstChild("Head") then
			local Health = Player.Character:GetAttribute("Health") or 0
			local P_Head = Player.Character.Head

			if Health > 0 and (P_Head.Position - Head.Position).Magnitude <= 18 then
				table.insert(lookTargets, P_Head)
			end
		end
	end

	if #lookTargets > 0 then
		table.sort(lookTargets, function(partA, partB)
			local distanceA = (partA.Position - Head.Position).Magnitude
			local distanceB = (partB.Position - Head.Position).Magnitude
			return distanceA < distanceB
		end)

		lookDir = (lookTargets[1].Position - Head.Position).Unit
	end
	
	local lookAtWorld = CFrame.lookAt(Head.Position, Head.Position + lookDir, Torso.CFrame.UpVector)
	local goalCFrame
	
	local x, y, z = lookAtWorld:ToOrientation()

	local constrainedX = math.rad(math.clamp(math.deg(x), -45, 45))
	local constrainedY = math.rad(math.clamp(math.deg(y), -90, 90))
	
	goalCFrame = CFrame.fromOrientation(constrainedX, constrainedY, z)
	TweenService:Create(Neck, neckInfo, {["C0"] = worldCFrameToC0ObjectSpace(Neck, goalCFrame)}):Play()
end)

Am I missing some components here, or is my math incorrect? Help or advice is appreciated.

You should clamp the angles in object space, not world. Also, I’d recommend calculating the pitch/yaw you need directly from the vector with trig instead of relying on ToOrientation() which can have unexpected effects.

I do a similar thing here for limiting arm rotation. I was having trouble getting it to work for this as-is, but maybe you’ll have better luck. I can take another look later.