How do I tween the camera from 2D to 3D with a function?

Hello!
As the title suggests, I would like to tween the camera from 2D to 3D when the player touches a part.
I already got the camera switching part down, but I want the camera to transition to that position instead of just snapping to it as seen in the video:

Because of the way I set up the script the only way I think I could go about making this work is if I make the tweening part a seperate function, which I then call in whenever the player touches the part.
The issue is that I don’t know how I would do this because my knowledge about scripting is very limited, and I’ve never used tween on the camera before.

This is how it’s set up:

It’s only one local script handling all of it with the 2 parts being located in Workspace.

I did try looking for solutions but I couldn’t find any definitive solutions to make this work. Some straight up didn’t work, others worked for the situation of the OP but not for me while the rest either required me to rewrite the entire script or I couldn’t understand anything about what was said in the post.
So for anyone willing to help, thank you in advance.

local localPlayer = game.Players.LocalPlayer

local camera = workspace.CurrentCamera

local part2D = workspace:WaitForChild("2D")
local part3D = workspace:WaitForChild("3D")

local function TweenCamera(newCFrame, newFOV)
	local tweenInfo = TweenInfo.new(
		1,
		Enum.EasingStyle.Linear,
		Enum.EasingDirection.InOut,
		0,
		false
	)
	local tween = game.TweenService:Create(
		camera,
		tweenInfo,
		{
			CFrame = newCFrame,
			FieldOfView = newFOV
		}
	)
	tween:Play()
	return tween
end

local lastCamState = nil
local function UpdateCamera(newCamState)
	if newCamState ~= lastCamState then
		local rootPart = localPlayer.Character:FindFirstChild("HumanoidRootPart")
		if rootPart then
			if newCamState == "2D" then
				task.spawn(function()
					local function newCFrame()
						return CFrame.new(rootPart.Position + Vector3.zAxis * 60)
					end
					camera.CameraType = Enum.CameraType.Scriptable
					rootPart.Anchored = true
					TweenCamera(newCFrame(), 40).Completed:Wait()
					rootPart.Anchored = false
					while newCamState == lastCamState do
						camera.CFrame = newCFrame()
						task.wait()
					end
				end)
			elseif newCamState == "3D" or newCamState == nil then
				local rpCF = rootPart.CFrame
				local newCFrame = CFrame.new(
					rpCF.Position - rpCF.LookVector * 20 + rpCF.UpVector * 10,
					rpCF.Position
				)
				camera.CameraType = Enum.CameraType.Scriptable
				rootPart.Anchored = true
				TweenCamera(newCFrame, 75).Completed:Wait()
				rootPart.Anchored = false
				camera.CameraType = Enum.CameraType.Custom
			end
		end
	end
	lastCamState = newCamState
end

part2D.Touched:Connect(function(hitPart)
	local player = game.Players:GetPlayerFromCharacter(hitPart.Parent)
	if player then UpdateCamera("3D") end
end)

part3D.Touched:Connect(function(hitPart)
	local player = game.Players:GetPlayerFromCharacter(hitPart.Parent)
	if player then UpdateCamera("2D") end
end)

localPlayer.CharacterAdded:Connect(UpdateCamera)

This will tween the camera from to the desired state when touching either the 2D or 3D part. You set the camera to a new state by doing UpdateCamera("3D") or UpdateCamera("2D")
You can adjust the tweening properties to your liking of course.

A problem that i could not find a solution to is that the camera snaps slightly at the end of tweening from 2D to 3D as the CameraType changes from Scriptable to Custom. I don’t think it’s possible to do anything about this sadly… (Unless you want to code your own 3D camera system)

1 Like

Thanks for the answer.

This one works, the sudden jump you pointed out doesn’t happen on my end, so this works and I’ll mark it as a solution.

However I also screwed up when writing this post because I didn’t point out that I wanted the camera to transition while the player is still moving, to that this was the solution that I managed to scramble together with trial and error, though the transition is not as smooth as the one with your code.

local player = game.Players.LocalPlayer
local camera = workspace.CurrentCamera
local TweenService = game:GetService("TweenService")

Camera2D = false

player.CharacterAdded:Wait()
player.Character:WaitForChild("HumanoidRootPart")

camera.CameraSubject = player.Character.Head
camera.CameraType = Enum.CameraType.Attach
camera.FieldOfView = 75

local RunService = game:GetService("RunService")

local tweenInfo = TweenInfo.new(
	1.2, -- Time
	Enum.EasingStyle.Linear, -- EasingStyle
	Enum.EasingDirection.In, -- EasingDirection
	0, -- RepeatCount (when less than zero the tween will loop indefinitely)
	false, -- Reverses (tween will reverse once reaching it's goal)
	0 -- DelayTime
)

local function onUpdate()
	if player.Character and player.Character:FindFirstChild("HumanoidRootPart") and Camera2D == true then
		camera.CFrame = CFrame.new(player.Character.Head.Position) * CFrame.new(0,5,20) * CFrame.fromOrientation(-0.2,0,0)
	else
		camera.CameraSubject = player.Character.Head
		camera.CameraType = Enum.CameraType.Custom
		camera.FieldOfView = 75
	end
end

workspace.CameraTo3D.Touched:Connect(function(hit)
	if game.Players.LocalPlayer == game.Players:GetPlayerFromCharacter(hit.Parent) then
		local Goal = player.Character.Head.CFrame
		camera.FieldOfView = 120
		if Camera2D == true then
			local tween = TweenService:Create(camera, tweenInfo, { CFrame = Goal })
			tween:Play()
		end
		Camera2D = false
		wait(0.5)
		camera.FieldOfView = 75
		camera.CameraType = Enum.CameraType.Custom
		camera.CameraSubject = player.Character.Head
	end
end)

workspace.CameraTo2D.Touched:Connect(function(hit)
	if game.Players.LocalPlayer == game.Players:GetPlayerFromCharacter(hit.Parent) then
		Camera2D = true
		camera.FieldOfView = 75
	end
end)

RunService:BindToRenderStep("Camera", Enum.RenderPriority.Camera.Value, onUpdate)
1 Like

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