[SOLVED] Changing camera CFrame causes a "pseudo-FOV"

I am scripting a “free move” camera system, like the one in Roblox Studio.

The issue starts when the player moves the camera LookVector. The camera FOV appears to be increasing when looking up. Checking the actual FOV shows that the FOV isn’t actually changing. Heres a video:

robloxapp-20200319-1855109.wmv (3.1 MB)

(sorry if it’s laggy, my computer sucks :sob:)

I’m completely stumped as to what causes this and there aren’t any threads discussing this. If you’re curious, here’s the code:

--Variables
local UserInputService = game:GetService("UserInputService")
local Player = game.Players.LocalPlayer
local Camera = workspace.CurrentCamera

--Camera settings
local CameraObject = {
	Angle = Vector2.new(0, 0),
	Subject = Vector3.new(0, 0, 0),
	Zoom = 2,
	Speed = .5,
	Y_Limit = 75
}

--Mix
local Mix = require(game.ReplicatedStorage.Components.MixPlus)
local MixObj = Mix:GetSublibrary("RbxObj")

UserInputService.InputChanged:Connect(function(Input, success)
	--Move camera
	if Input.UserInputType == Enum.UserInputType.MouseMovement then
		--Get mouse delta
		local Delta = UserInputService:GetMouseDelta()
		
		--Prevent spam
		if Delta ~= Vector2.new(0, 0) then
			Pan(-Delta.X*.5, Delta.Y*.5)
		end
	end
end)

--Start mouse lock
UserInputService.InputBegan:Connect(function(Input, success)
	if Input.UserInputType == Enum.UserInputType.MouseButton2 then
		UserInputService.MouseBehavior = Enum.MouseBehavior.LockCurrentPosition
	end
end)

--Stop mouse lock
UserInputService.InputEnded:Connect(function(Input, success)
	if Input.UserInputType == Enum.UserInputType.MouseButton2 then
		UserInputService.MouseBehavior = Enum.MouseBehavior.Default
	end
end)

--Pans camera by X and Y angles
function Pan(X, Y)
	--Adjust speed
	local X = X*CameraObject.Speed
	local Y = Y*CameraObject.Speed or 0
	
	--Change angle
	CameraObject.Angle = Vector2.new(CameraObject.Angle.X + X, CameraObject.Angle.Y + Y)
	
	--Y angle limit
	if math.abs(CameraObject.Angle.Y) > CameraObject.Y_Limit then
		CameraObject.Angle = Vector2.new(CameraObject.Angle.X, CameraObject.Y_Limit * math.sign(CameraObject.Angle.Y))
	end
	
	--Convert the angles to CFrame
	local Vector = AnglesToUnit(CameraObject.Angle.X, CameraObject.Angle.Y)
	local CameraCFrame = MixObj:CreateCFrame(CameraObject.Subject - Vector*CameraObject.Zoom, Vector)
	
	--Set CFrame
	Camera.CFrame = CameraCFrame
end

--Convert angles into unit sphere
function AnglesToUnit(Angle1, Angle2)
	--Gets the Angle2 distortion value
	local Angle2_Distortion = math.cos(math.rad(Angle2))
	
	--Gets the coordinates for the unit sphere
	local X = math.cos(math.rad(Angle1)) * Angle2_Distortion
	local Y = math.sin(math.rad(Angle2))
	local Z = math.sin(math.rad(Angle1)) * Angle2_Distortion
	
	return Vector3.new(X, Y, Z)
end

Player.CharacterAdded:Connect(function()
	wait() --Idk why this needs to be here, but it does so camera doesn't break
	
	--Initiates camera
	Camera.CameraType = Enum.CameraType.Scriptable
	Player.Character.Parent = nil
end)
1 Like

Here is the CreateCFrame function:

function CreateCFrame(Position, LookVector)
	local TrueUpVector = Vector3.new(0, 1, 0)
	local RightVector = LookVector:Cross(TrueUpVector)
	local UpVector = RightVector:Cross(LookVector)
	
	local FinalCFrame = CFrame.fromMatrix(Position, RightVector, UpVector)
	
	return FinalCFrame 
end

Can you give us a video that don’t need to be downloaded? I don’t like to download random stuff

Press on the Upload Button, press Chose Files, after you have chosen a video or photo, press Upload and done. The Upload buttons is looking like this:

brave_4AQuhTetqx

For photos I recommend ShareX

Ok, here is an image that shows the issue!

The camera isn’t actually that high, its just the pseudo-FOV. Here is a picture with camera looking at the horizon

Apparently my camera script wasn’t to blame. This piece of code also causes pseudo-FOV:

local Player = game.Players.LocalPlayer
local Camera = workspace.CurrentCamera

Player.CharacterAdded:Connect(function()
	wait() --Idk why this needs to be here, but it does
	
	--Initiates camera
	Camera.CameraType = Enum.CameraType.Scriptable
	Player.Character.Parent = nil

	--Set camera CFrame
	local CameraCFrame = MixObj:CreateCFrame(Vector3.new(0, 5, 0), Vector3.new(1, 10, 1).unit)
	Camera.CFrame = CameraCFrame
end)

The pseudo-FOV is probably coming from this:

function CreateCFrame(Position, LookVector)
	local TrueUpVector = Vector3.new(0, 1, 0)
	local RightVector = LookVector:Cross(TrueUpVector)
	local UpVector = RightVector:Cross(LookVector)
	
	return CFrame.fromMatrix(Position, RightVector, UpVector)
end

I’m still confused as to why this function would cause the pseudo-FOV.

Bumping my issue as it remains unsolved.

Recap:
I am creating a free movement camera script. However, a pseudo-FOV (Field of View) appears when the camera is looking either up or down.

Here is the camera looking at the horizon (no pseudo-FOV)

The camera looking slightly upwards (slight pseudo-FOV)

The camera looking up (extreme pseudo-FOV)


*Note that the same applies when looking down.
*The actual FOV doesn’t increase

It appears that this function might be responsible:

function CreateCFrame(Position, LookVector)
	local TrueUpVector = Vector3.new(0, 1, 0)
	local RightVector = LookVector:Cross(TrueUpVector)
	local UpVector = RightVector:Cross(LookVector)
	
	return CFrame.fromMatrix(Position, RightVector, UpVector)
end

In addition, this function was deprived from this article. The function works as intended for most cases, except for the camera as shown above. The “broken” CFrame the function returns appears to be normal. Here is a “broken” CFrame for position 0, 5, 5 and LookVector 1, 10, 1:

Player.CharacterAdded:Connect(function()
	wait() --Idk why this needs to be here, but it does
	
	--Initiates camera
	Camera.CameraType = Enum.CameraType.Scriptable
	Player.Character.Parent = nil
	
	local CameraCFrame = CreateCFrame(Vector3.new(0, 0, 5), Vector3.new(1, 10, 1).unit)
	Camera.CFrame = CameraCFrame
	print(Camera.CFrame)
	--Prints  0, 0, 5, -0.0990147591, -0.0980392247, -0.0990147591, 0, 0.0196078457, -0.990147471, 0.0990147591, -0.0980392247, -0.0990147591
end)

Was the pseudo-FOV created by CreateCFrame, or is it an internal bug? If the bug was caused by CreateCFrame, how can I fix it?

Additional Notes:
*The player’s character still exists in workspace from the server.
*Looking directly up (0, 1, 0) or down (0, -1, 0) can fling and instantly kill the player
*The script doesn’t replace the default camera scripts
*The default camera works as intended, so it might not be an engine bug
*This might be an engine bug, as CFrame shouldn’t change the camera FOV, so I might make a bug report later

I found a solution! I apparently messed up CreateCFrame in the most simple manner; I forgot to use Unit. Here is the fix:

function CreateCFrame(Position, LookVector)
	local TrueUpVector = Vector3.new(0, 1, 0)
	local RightVector = LookVector:Cross(TrueUpVector)
	local UpVector = RightVector:Cross(LookVector)
	
	return CFrame.fromMatrix(Position, RightVector.Unit, UpVector.Unit)
end