BaseCamera CoreScript goes wild when switching to Type Custom

Hey guys! I was using this code to change the player’s camera and to also animate it, based on the sent targetPart.

-- Listen for the server event
Events.CameraEvent.OnClientEvent:Connect(function(targetPart, tweeningNeeded)
	print(targetPart, tweeningNeeded)
	
	if targetPart then
		if not targetPart:IsA("BasePart") then return end
		
		-- Switch to Scriptable camera and store the target
		Camera.CameraType = Enum.CameraType.Scriptable
		
		if tweeningNeeded then -- if tweening needed tween camera
			if Camera.CFrame == targetPart.CFrame then return end -- if same cframe return
			
			local toTween = TweenService:Create(
				Camera, TweenInfo.new(1), {CFrame = targetPart.CFrame}
			)

			toTween:Play()
		else -- if not just set to position
			Camera.CFrame = targetPart.CFrame
		end
	else
		local Character = Player.Character
		if not Character or not Character.Parent then return end
		
		local Humanoid = Character:FindFirstChild("Humanoid")
		local HRP = Character:FindFirstChild("HumanoidRootPart")
		if not Humanoid or not HRP then return end
		
		-- Reset camera to follow the player
		Camera.CameraType = Enum.CameraType.Custom
		Camera.CameraSubject = Humanoid
	end
end)

Well this script went wild a couple of times, when it keeps repeating this:

Players.Player3.PlayerScripts.PlayerModule.CameraModule.BaseCamera:732: invalid argument #3 to ‘clamp’ (max must be greater than or equal to min)
RunService:fireRenderStepEarlyFunctions unexpected error while invoking callback: Players.Player3.PlayerScripts.PlayerModule.CameraModule.BaseCamera:732: invalid argument #3 to ‘clamp’ (max must be greater than or equal to min)

This is printed every single frame. Can happen for any player.

Later I updated the code to this:

-- Listen for the server event
Events.CameraEvent.OnClientEvent:Connect(function(targetPart, tweeningNeeded)
	print(targetPart, tweeningNeeded)
	
	if targetPart then
		if not targetPart:IsA("BasePart") then return end
		
		-- Switch to Scriptable camera and store the target
		Camera.CameraType = Enum.CameraType.Scriptable
		
		if tweeningNeeded then -- if tweening needed tween camera
			if Camera.CFrame == targetPart.CFrame then return end -- if same cframe return
			
			local toTween = TweenService:Create(
				Camera, TweenInfo.new(1), {CFrame = targetPart.CFrame}
			)

			toTween:Play()
		else -- if not just set to position
			Camera.CFrame = targetPart.CFrame
		end
	else
		local Character = Player.Character
		if not Character or not Character.Parent then return end
		
		local Humanoid = Character:FindFirstChild("Humanoid")
		local HRP = Character:FindFirstChild("HumanoidRootPart")
		if not Humanoid or not HRP then return end
		
		Camera.CameraType = Enum.CameraType.Scriptable
		Camera.CFrame = HRP.CFrame
		task.wait() -- short delay
		
		-- Reset camera to follow the player
		Camera.CameraType = Enum.CameraType.Custom
		Camera.CameraSubject = Humanoid
	end
end)

It works fine mostly now, sometimes it does the same thing when the player’s camera is updated while the player is dead. (Health <= 0)
Does anyone have an idea why does this happen? I’ve read about this here but haven’t really found anything useful. Have you had experience with this before?
Just to note, the event is not spammed, it is only called once (not like 10 times in half a second) - so I ruled out this part as being the problem.

To the people in the future having the same problem.

local yTheta = math.clamp(rotateInput.Y, -MAX_Y + currPitchAngle, -MIN_Y + currPitchAngle)

This is the root of the problem, this part is in the BaseCamera module in:

Players > PlayerScripts > PlayerModule > CameraModule > BaseCamera

Copy the whole PlayerModule and put it in StarterPlayer > StarterPlayerScripts. The error is around the ~730th line, find it and replace it with this:

function BaseCamera:CalculateNewLookCFrameFromArg(suppliedLookVector: Vector3?, rotateInput: Vector2): CFrame
	local currLookVector: Vector3 = suppliedLookVector or self:GetCameraLookVector()
	local currPitchAngle = math.asin(currLookVector.Y)
	
	local rawMin = -MAX_Y + currPitchAngle
	local rawMax = -MIN_Y + currPitchAngle

	-- make sure we have valid numbers
	rawMin = (rawMin == rawMin) and rawMin or 0  -- NaN check
	rawMax = (rawMax == rawMax) and rawMax or 0  -- NaN check

	-- ensure min <= max
	local minTheta = math.min(rawMin, rawMax)
	local maxTheta = math.max(rawMin, rawMax)

	-- last safety: if for some reason max < min, force them equal
	if maxTheta < minTheta then
		maxTheta = minTheta
	end

	-- finally, clamp safely
	local yTheta = math.clamp(rotateInput.Y or 0, minTheta, maxTheta)
	
	local constrainedRotateInput = Vector2.new(rotateInput.X, yTheta)
	local startCFrame = CFrame.new(ZERO_VECTOR3, currLookVector)
	local newLookCFrame = CFrame.Angles(0, -constrainedRotateInput.X, 0) * startCFrame * CFrame.Angles(-constrainedRotateInput.Y,0,0)
	return newLookCFrame
end

And it should work fine using this. Hopefully.

4 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.