Back from custom camera to default with new coordinates

Hi there! I have problem with properly back from custom camera to default camera with new Cframe. Here is a effect what i already have.

CustomCamera.wmv (1.6 MB)

Problem:
When lerp (back lerp) is finished and cameraType is set to custom then camera jumps a little bit.

Currently:
When camera is set from scriptable to custom then he back to position before changed to scriptable. SubjectDistance in default camera is not correct, so player need to scroll mouse to get old position. To fix this i wrote new function in “PlayerModule → CameraModule → ZoomController” to set zoom position and goal.

function Zoom.SetZoomParametersFromCustomCamera(newPosition)
	zoomSpring.goal = newPosition -- Goal
	zoomSpring.x = newPosition -- Position
end

Default camera use cameraAngleX/cameraAngleY, but here cameraAngleY is static and i used focus offset from HumanoidRootPart to get vision up or down. When camera is set back then is a jump. To repair this problem, before lerp i swap camera to new CFrame with cameraAngleX/cameraAngleY without offset. Then saved endPosition, coming back to last position and lerp to new destination. That helps a lot, but still is a little bit jump. Jump power depends on camera Y axis before lerp.
*Jump exist because lerp endPosition is not the same as position when default camera update position. I can’t figure out this …

Code:

-- Services
local TweenService = game:GetService("TweenService")
local UserInputService = game:GetService("UserInputService")
local ContextActionService = game:GetService("ContextActionService")
local RunService = game:GetService("RunService")

-- Player and Character
local Player = game:GetService("Players").LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local RootPart = Character:WaitForChild("HumanoidRootPart")
local Humanoid = Character:WaitForChild("Humanoid")

-- Configuration
local SENSITIVITY_X = 0.15
local SENSITIVITY_Y = 0.2 
local CAMERA_MAX_ZOOM_DISTANCE = Player.CameraMaxZoomDistance
local CAMERA_MIN_ZOOM_DISTANCE = Player.CameraMinZoomDistance

-- Gui
local CursorGui = Player.PlayerGui:WaitForChild("CursorGui")
local CustomCursor = CursorGui.CursorFrame.CustomCursor

-- Variables
local cameraAngleX = 0
local cameraAngleY = 0

local mouse = Player:GetMouse()
local camera = workspace.CurrentCamera
local cameraOffset = CFrame.new(0, 3, 8)
local targetOffset = Vector3.new(0, 0, 0) -- Dynamically setting in UpdateCameraTarget

local resolution = CursorGui
local mousePositionY = 0.3 * resolution.AbsoluteSize.Y -- Set custom mouse default position (0.3% value started from top to down on screen)
local screenRange = resolution.AbsoluteSize.Y / 1.2







local function getCameraAngleInDegrees(object, axis)
	local y, x, z = object.CFrame:ToEulerAnglesYXZ() -- Returned radians
	local result = {["y"] = y, ["x"] = x, ["z"] = z}
	
	if axis and result[axis] then
		return math.deg(result[axis]) -- Convert to degrees
	else
		return math.deg(y), math.deg(x), math.deg(z)
	end
end

local function updateCustomMousePosition()
	-- Example: ScreenSize = 1024/720 // Our frame range: 360 from center point to 720
	-- Viewfinder can move only from center to the top of screen
	local screenSize = resolution.AbsoluteSize
	local positionInFrame = math.clamp(mousePositionY, 0, screenSize.Y / 2)
	--print ("Position in frame: ", positionInFrame)
	
	-- Set custom cursor position
	local yScale = positionInFrame / (screenSize.Y / 2) -- Convert to scale 0 - 1
	CustomCursor.Position = UDim2.new(0.5, 0, yScale, 0)
end

local function updateCameraTarget(delta)
	-- Update mouse position
	-- Max value in clamp is screenRange from 0% to (0.8% * screenSize.Y)
	mousePositionY = math.clamp(mousePositionY + delta.Y, 0, screenRange)
	--print ("Mouse position Y: ", mousePositionY)

	-- Range camera target offset from Humanoid
	local min = -4
	local max = 8
	local difference = min + (max * -1)
	local y = ((mousePositionY / screenRange) * difference) + max -- Example: ((100 / 360) * -12) - (-8) = 4.6
	print ("Current value: ", y)

	targetOffset = Vector3.new(0, y, 0) -- Offset from HumanoidRootPart
end

local function playerInput(actionName, inputState, inputObject)
	if inputState == Enum.UserInputState.Change then
		local delta = inputObject.Delta

		updateCameraTarget(delta) 
		updateCustomMousePosition()

		cameraAngleX = cameraAngleX - delta.X * SENSITIVITY_X
		-- cameraAngleY is not needed. Camera is moving only up and down with targetOffset
	end
end

local function cameraUpdate(delta)
	local startPosition = CFrame.new(RootPart.CFrame.Position) * CFrame.Angles(0, math.rad(cameraAngleX), 0) * cameraOffset
	local target = RootPart.CFrame.Position + targetOffset
	camera.CFrame = CFrame.new(startPosition.Position, target)

	-- Set character position to camera look vector
	RootPart.CFrame = CFrame.new(RootPart.Position, RootPart.Position + Vector3.new(camera.CFrame.lookVector.X, 0, camera.CFrame.lookVector.Z))
end

local function cameraAnimation()
	local increment = 1 / 30
	local alpha = 0
	local alphaResult
	local cameraStartPosition, target, cameraFinishPosition
	local rootFinishPosition
	local initialCamera = camera.CFrame

	-- Update camera angle X and Y
	cameraAngleX = getCameraAngleInDegrees(camera, "x")
	cameraAngleY = getCameraAngleInDegrees(camera, "y")

	while alpha <= 1 do
		RunService.RenderStepped:Wait()

		-- Get current alpha with easing style
		alphaResult = TweenService:GetValue(alpha, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
		alpha = alpha + increment

		-- Lerp camera with freely mouse rotation
		cameraStartPosition = CFrame.new(RootPart.CFrame.Position) * CFrame.Angles(0, math.rad(cameraAngleX), 0) * cameraOffset
		target = RootPart.CFrame.Position + targetOffset
		cameraFinishPosition = CFrame.new(cameraStartPosition.Position, target)
		camera.CFrame = initialCamera:Lerp(cameraFinishPosition, alphaResult)

		-- Lerp character to camera look vector
		rootFinishPosition = CFrame.new(RootPart.Position, RootPart.Position + Vector3.new(camera.CFrame.lookVector.X, 0, camera.CFrame.lookVector.Z))
		RootPart.CFrame = RootPart.CFrame:Lerp(rootFinishPosition, alphaResult / 4) -- Slow down character rotation
		print (alpha)
	end
end

local function ResetCamera()
	local function printDebug()
		local currentSubjectDistance = math.abs((Character.Head.CFrame.p - camera.CFrame.p).Magnitude)
		print ("CameraAngle X = ", getCameraAngleInDegrees(camera, "x"), " // CameraAngle Y = ", getCameraAngleInDegrees(camera, "y"))
		print ("Camera finish position = ", camera.CFrame)
		warn ("CurrentSubjectDistance = ", currentSubjectDistance)
		print ("------------------------------------------------------")
	end
	
	-- Set camera temporary to destination cframe and get subjectDistance
	-----------------------------------------------------------------------------------
	local tempCameraPosition = camera.CFrame

	-- Update camera angle X and Y
	cameraAngleX = getCameraAngleInDegrees(camera, "x")
	cameraAngleY = getCameraAngleInDegrees(camera, "y")

	local MOBILE_OFFSET = Vector3.new(0, 1, 0)
	local ZERO_VECTOR3 = Vector3.new(0,0,0)
	local HEAD_OFFSET = Vector3.new(0,1.5,0)
	local R15_HEAD_OFFSET = Vector3.new(0,2,0)
	local HumanoidIsDead = Humanoid:GetState() == Enum.HumanoidStateType.Dead
	local heightOffset = Humanoid.RigType == Enum.HumanoidRigType.R15 and R15_HEAD_OFFSET or HEAD_OFFSET
	if HumanoidIsDead then
		heightOffset = ZERO_VECTOR3
	end

	local startPosition = CFrame.new(RootPart.CFrame.Position) * CFrame.Angles(0, math.rad(cameraAngleX), 0) * CFrame.Angles(math.rad(cameraAngleY), 0, 0) * cameraOffset
	local target = RootPart.CFrame.Position + heightOffset
	local cameraFinishPosition = CFrame.new(startPosition.Position, target)
	camera.CFrame = cameraFinishPosition

	local currentSubjectDistance = math.abs((Character.Head.CFrame.p - camera.CFrame.p).Magnitude)
	
	printDebug()
	
	-- Back to last CFrame position
	camera.CFrame = tempCameraPosition
	-----------------------------------------------------------------------------------

	-- Tween to cameraFinishPosition
	local increment = 1 / 30
	local alpha = 0
	local alphaResult
	local rootFinishPosition
	local initialCamera = tempCameraPosition

	while alpha <= 1 do
		RunService.RenderStepped:Wait()

		alphaResult = TweenService:GetValue(alpha, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
		alpha = alpha + increment

		camera.CFrame = initialCamera:Lerp(cameraFinishPosition, alphaResult)
	end
	
	
	-- Back to default camera
	camera.CameraType = Enum.CameraType.Custom
	
	local PlayerModule = require(Player.PlayerScripts.PlayerModule)
	local ZoomController = require(Player.PlayerScripts.PlayerModule.CameraModule.ZoomController)
	local defaultCamera = PlayerModule:GetCameras()
	
	-- In PlayerModule --> CameraModule --> ZoomController in line 125 there is added new function to set zoom position and goal
	defaultCamera.activeCameraController.currentSubjectDistance = currentSubjectDistance -- Update current position to fix camera when scrolling
	ZoomController.SetZoomParametersFromCustomCamera(currentSubjectDistance) -- Set zoom position and goal
	
	printDebug()
	wait(1)
	printDebug()
end

UserInputService.InputBegan:Connect(function(input)
	-- Start custom mode
	if input.KeyCode == Enum.KeyCode.X then
		CustomCursor.Visible = true
		Humanoid.AutoRotate = false
		camera.CameraType = Enum.CameraType.Scriptable
		UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
		UserInputService.MouseIconEnabled = false

		ContextActionService:BindAction("PlayerInput", playerInput, false, Enum.UserInputType.MouseMovement)
		cameraAnimation()
		RunService:BindToRenderStep("CameraUpdate", 1, cameraUpdate)
		
	-- Start default mode
	elseif input.KeyCode == Enum.KeyCode.Z then
		CustomCursor.Visible = false
		Humanoid.AutoRotate = true
		UserInputService.MouseBehavior = Enum.MouseBehavior.Default
		UserInputService.MouseIconEnabled = true

		RunService:UnbindFromRenderStep("CameraUpdate")
		ContextActionService:UnbindAction("PlayerInput")
		ResetCamera()
	end
end)

Here is a new place:
Custom camera.rbxl (135.8 KB)

2 Likes