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)