Issue with Camera CFrame math

Im trying to create a custom camera system for my directional gravity controller. As part of this, I need a “facingCFrame” CFrame that has the upvector of gravity but faces in the direction of the camera, this is used by the camera is it’s own math and referenced in the character controller. The problem is, I’m using springs in the camera to make it smoother, but the yaw rotation from the facingCFrame is adding to the spring rotation, causing my camera to rotate much faster than intended. In the following video, there is a blue part with an arrow. The GOAL is that the arrow should always point in the direction of the camera (while maintaining the same upvector as gravity), it represents the “self._facingCFrame” variable. You will also see me rotating a purple part and changing the direction of gravity.

Video:

Code:

-- Services

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local PlayerService = game:GetService("Players")
local UIS = game:GetService("UserInputService")

-- Essentials

local Utility = ReplicatedStorage.Modules.Utility
local Spring = require(Utility.Spring)

local Mouse = PlayerService.LocalPlayer:GetMouse()
local Camera = workspace.CurrentCamera

local AdjustCFrameUp = require(script.Parent.Parent.AdjustCFrameUp)

-- CONSTANTS

local ZERO_VECTOR2 = Vector2.zero
local ZERO_VECTOR3 = Vector3.zero

local MAX_UP_ANGLE = math.rad(80)
local MAX_DOWN_ANGLE = math.rad(-80)

local MAX_ZOOM_DISTANCE = 20
local MIN_ZOOM_DISTANCE = 4

local DEFAULT_ZOOM_DISTANCE = 8

local SENSITIVITY = 0.007
local ZOOM_SENSITIVITY = 1.6

local MAX_MOUSE_DELTA = 0.4

local ZOOM_SPRING_SPEED = 20
local ZOOM_SPRING_DAMPER = 0.8

local ROT_SPRING_SPEED = 30
local ROT_SPRING_DAMPER = 0.8

local CAMERA_OFFSET = Vector3.new(0, 2, 0)

-- Module

type main = {
	Player : Player,
	Character : Model,
	Humanoid : Humanoid,
	HRP : BasePart,
	Attachment : Attachment,

	_characterMass : number,
	_gravityStrength : number,

	_upVector : Vector3,

	_gravityForce : Vector3,
	_negateForce : Vector3,

	_totalForce : Vector3,
	
	Physics : typeof(require(script.Parent.Parent.Physics))
}

local CameraController = {}
CameraController.__index = CameraController

function CameraController.new(main : main)
	local self = setmetatable({}, CameraController)
	
	self.Main = main

	self.FirstPerson = false

	self._smoothedUpVector = main._upVector
	self._facingCFrame = CFrame.new(ZERO_VECTOR3)

	self._targetRotX = 0
	self._targetRotY = 0

	self._rotSpringX = Spring.new(0)
	self._rotSpringY = Spring.new(0)

	self._rotSpringX.Speed = ROT_SPRING_SPEED
	self._rotSpringX.Damper = ROT_SPRING_DAMPER

	self._rotSpringY.Speed = ROT_SPRING_SPEED
	self._rotSpringY.Damper = ROT_SPRING_DAMPER

	self._targetZoom = DEFAULT_ZOOM_DISTANCE

	self._zoomSpring = Spring.new(0)

	self._zoomSpring.Speed = ZOOM_SPRING_SPEED
	self._zoomSpring.Damper = ZOOM_SPRING_DAMPER

	self._cameraPos = ZERO_VECTOR3

	self._runConnection = nil

	self:_init()
	--self:_update()
	
	return self
end

-- Private methods

function CameraController:_init()
	self._facingCFrame = AdjustCFrameUp(self.Main.HRP.CFrame, self.Main._upVector).Rotation
	
	self._runConnection = RunService.RenderStepped:Connect(function()
		self:_update()
	end)
	
	UIS.InputChanged:Connect(function(input, gpe)
		if gpe or input.UserInputType ~= Enum.UserInputType.MouseWheel then return end

		self._targetZoom = math.clamp(self._targetZoom - input.Position.Z * ZOOM_SENSITIVITY, MIN_ZOOM_DISTANCE, MAX_ZOOM_DISTANCE)
	end)
end

function CameraController:_getTargetPos()
	return (self.Main.HRP.CFrame * CFrame.new(CAMERA_OFFSET)).Position
end

function CameraController:_update()
	Camera.CameraType = Enum.CameraType.Custom

	local mouseDelta = ZERO_VECTOR3

	if Mouse.Button2Down or self.FirstPerson or self.ShiftLocked then
		mouseDelta = Vector2.new(
			math.clamp(UIS:GetMouseDelta().X * SENSITIVITY, -MAX_MOUSE_DELTA, MAX_MOUSE_DELTA),
			math.clamp(UIS:GetMouseDelta().Y * SENSITIVITY, -MAX_MOUSE_DELTA, MAX_MOUSE_DELTA) / 1.6
		)
	end
	
	self._targetRotX -= mouseDelta.X
	self._targetRotY = math.clamp(self._targetRotY - mouseDelta.Y, MAX_DOWN_ANGLE, MAX_UP_ANGLE)
	
	self._smoothedUpVector = self._smoothedUpVector:Lerp(self.Main._upVector, 0.1)

	self._facingCFrame *= CFrame.Angles(0, -mouseDelta.X, 0)
	self._facingCFrame = AdjustCFrameUp(self._facingCFrame, self._smoothedUpVector)
	
	self._rotSpringX.Target = self._targetRotX
	self._rotSpringY.Target = self._targetRotY
	self._zoomSpring.Target = self._targetZoom
	
	self._cameraPos = self:_getTargetPos()
	
	workspace.FacePart.CFrame = self.Main.HRP.CFrame * self._facingCFrame
	
	Camera.CFrame = CFrame.new(self._cameraPos)
		* self._facingCFrame
		* CFrame.Angles(0, self._rotSpringX.Position, 0)
		* CFrame.Angles(self._rotSpringY.Position, 0, 0) 
		* CFrame.new(0, 0, self._zoomSpring.Position)
end

return CameraController
3 Likes

Just boosting this so people see it.