The cutscene manager is a small module aimed at helping users build and manage their cutscenes. Once setup any local script can access and play any cutscene stored in the module. The cutscene module can be found here.

Current Code

-- A small cutscene manager by:- kingdom5 ver:- 1.0

-- system vars
local isPlaying, debugMode = false, false
local defTime = 0.2

-- cutscene vars
local cam = workspace.CurrentCamera
local interpolationFinished = cam.InterpolationFinished
local interpolate = cam.Interpolate
local sceneList = {}

-- other
local typOf, cf = typeof, CFrame.new
local heartbeat = game:GetService('RunService').Heartbeat
local speed

-- local functions
--[[
	number	mode
	1		camera interpolate with time given
	2		camera interpolate with function given to calculate time
	3		funcction cutscene
]]
local playTypes = {}

local function getSpeed(val, playSpeed)
	return (val / 100) * playSpeed
end
playTypes[1] = function(sceneNum, scene, playSpeed)
	speed = getSpeed(scene[4], playSpeed)
	if speed <= 0 then 
		warn('Time calculated is ' .. tostring(speed) ..' for scene ' .. tostring(sceneNum) .. '. Time cannot be smaller than or equal to 0')
		speed = defTime
	end
	interpolate(cam, scene[2], scene[3], speed)
	interpolationFinished:Wait()
end

playTypes[2] = function(sceneNum, scene, playSpeed)
	speed = getSpeed(scene[4](), playSpeed)
	if speed <= 0 then 
		warn('Time calculated is ' .. tostring(speed) ..' for scene ' .. tostring(sceneNum) .. '. Time cannot be smaller than or equal to 0')
		speed = defTime
	end
	interpolate(cam, scene[2], scene[3], speed)
	interpolationFinished:Wait()
end

playTypes[3] = function(sceneNum, scene)
	scene[2](cam)
end

local function playScene(startCFrame, scenes, num, playSpeed)
	local curCamMode = cam.CameraType
	cam.CameraType = Enum.CameraType.Scriptable
	cam.CFrame = startCFrame
	heartbeat:Wait()	
	heartbeat:Wait() -- the CFrame does not seem to change in once frame with focus
	
	local tmp
	if debugMode then
		for i=num, #scenes do
			tmp = scenes[i]
			print('Playing scene number ' .. tostring(i) .. ' type:- ' .. tostring(tmp[1]))
			playTypes[tmp[1]](i, tmp, playSpeed)
			print('Scene ' .. tostring(i) .. ' ended')
		end
		
		print('Cutscene end')
	else
		for i=num, #scenes do
			tmp = scenes[i]
			playTypes[tmp[1]](i, tmp, playSpeed)
		end
	end	
	
	cam.CameraType = curCamMode
end

local function buildDebug(name, startCFrame, scenes)
	local mod = workspace:FindFirstChildOfClass('CutScenes') 
	if not mod then
		mod = Instance.new('Model')
		mod.Name = 'CutScenes'
		mod.Parent = workspace
	elseif mod:FindFirstChild(name) then
		return
	end
	
	local sceneModel = Instance.new('Model')
	sceneModel.Name = name	
	
	local tmp = Instance.new('Part')
	tmp.Anchored = true
	tmp.Size = Vector3.new(1,1,1)
	tmp.CanCollide = false
	
	-- build start CFrame
	local startModel = Instance.new('Model')
	startModel.Name = 'StartCFrame'
	startModel.Parent = sceneModel
	
	local startPoint = tmp:Clone()
	startPoint.BrickColor = BrickColor.palette(1)
	startPoint.CFrame = cf(startCFrame.p)
	startPoint.Name = 'EndPosition'
	startPoint.Parent = startModel
	
	local startFocus = tmp:Clone()
	startFocus.BrickColor = BrickColor.palette(1)
	startFocus.CFrame = cf(startCFrame.p + (startCFrame.lookVector * 5))
	startFocus.Transparency = 0.5
	startFocus.Name = 'Focus'
	startFocus.Parent = startModel
		
	for i=1, #scenes do
		if scenes[i][1] == 3 then
			warn('Scene ' .. tostring(i) .. ' is a function scene')
		else
			local tmpM = Instance.new('Model')
			tmpM.Name = 'Scene ' .. tostring(i)
			tmpM.Parent = sceneModel
			
			local sP = tmp:Clone()
			sP.Name = 'EndPosition'
			sP.BrickColor = BrickColor.palette(i+1)
			sP.CFrame = scenes[i][2]
			sP.Parent = tmpM
			
			local eF = tmp:Clone()
			eF.Name = 'Focus'
			eF.BrickColor = BrickColor.palette(i+1)
			eF.CFrame = scenes[i][3]
			eF.Transparency = 0.5
			eF.Parent = tmpM
		end
	end
	
	sceneModel.Parent = mod
end

local function newScene(name)
	local f = {Name = name}
	local var = {
		isLocked = false,
		isPlaying = false,
		scenes	= {},
		startCFrame = cf(0,0,0),
		playBackSpeed = 100
	}
	
	function f:AddScene(endCFrame, focus, tm)
		if var.isLocked then
			warn('Scene is locked and cannot be edited')
		end
		
		endCFrame = typOf(endCFrame) == 'Vector3' and cf(endCFrame) or endCFrame
		focus = typOf(focus) == 'Vector3' and cf(focus) or focus
		
		if typOf(endCFrame) ~= 'CFrame' then
			warn('AddScene arg 1 must be a CFrame or a Vector3 but was passed ' .. typOf(endCFrame))
		end
		
		if typOf(focus) ~= 'CFrame' then
			warn('AddScene arg 2 must be a CFrame or a Vector3 but was passed ' .. typOf(focus))
		end
		
		if typOf(tm) == 'number' and tm > 0  then
			var.scenes[#var.scenes+1] = {1, endCFrame, focus, tm}
		elseif typOf(tm) == 'function' then
			local sceneNum = #var.scenes+1
			var.scenes[sceneNum] = {2, endCFrame, focus, function()
				local res = tm(cam, endCFrame, focus, sceneNum)
				if typOf(res) == 'number' then
					if res > 0 then
						return res
					else
						warn('Scene number ' .. tostring(sceneNum) .. ' function did not pass back a number greater than 0 using def value')
					end
				else
					warn('Scene number ' .. tostring(sceneNum) .. ' function did not pass back a number using def value')
					
				end
				
				return defTime -- def vale
			end}
		else
			warn('AddScene arg 3 must be a function or a number')		
		end
	end
	
	function f:AddFunctionScene(func)
		if typOf(func) == 'function' then
			var.scenes[#var.scenes+1] = {3, func}
		else
			warn('AddFunctionScene arg 1 must be a function')
		end
	end	
	
	function f:SetStartCFrame(cf)
		if var.isLocked then
			warn('Scene is locked')
			return
		end
		if typOf(cf) == 'CFrame' then
			var.startCFrame = cf
		else
			warn('SetStartCFrame arg 1 must be a CFrame')
		end
	end
	
	function f:Play()
		if not var.isLocked then
			warn('Cutscene must be setup first before playing')
			return
		end
	
		if isPlaying then
			warn('Only one cutscene can play at a time')
			return
		end
		
		var.isPlaying = true
		isPlaying = true
		playScene(var.startCFrame, var.scenes, 1, var.playBackSpeed)
		var.isPlaying = false
		isPlaying = false
	end	
	
	function f:PlayFromStep(num)
		if typOf(num) ~= 'number'or math.floor(num) ~= num then
			warn('PlayFromSceneNum arg 1 must be a  whole number')
		end
		
		if not var.isLocked then
			warn('Cutscene must be setup first before playing')
			return
		end
	
		if isPlaying then
			warn('Only one cutscene can play at a time')
			return
		end
		
		if #var.scenes < num then
			warn('Number must be not be bigger than the number of scenes')
			return
		end
		
		var.isPlaying = true
		isPlaying = true
		playScene(CFrame.new(var.scenes[num][2].p, var.scenes[num][3].p), var.scenes, num+1, var.playBackSpeed)
		var.isPlaying = false
		isPlaying = false
	end	
	
	function f:NumberOfSteps()
		return #var.scenes
	end
	
	function f:IsPlaying()
		return var.isPlaying
	end
	
	function f:Setup()
		var.isLocked = true
	end
	
	function f:Destroy()
		if not isPlaying then
			sceneList[name] = nil
			var = nil
			for i, v in pairs(f) do
				f[i] = nil
			end
			
		end
	end
	
	function f:ShowDebug()
		if debugMode then
			if isPlaying then
				warn('Cannot debug while playing a cutscene')
			elseif not var.isLocked then
				warn('Cannot debug while scenes are being added setup the scene before debugging')
			else
				buildDebug(name, var.startCFrame, var.scenes)
			end
		else
			warn('Cutscene Module not in debug mode')
		end
	end
	
	function f:HideDebug()
		local mod = workspace:FindFirstChild('CutScenes') 
		if mod and mod.ClassName == 'Model' then
			local tmp = mod:FindFirstChild(name)
			if tmp then
				tmp:Destroy()
			end
		end
	end
	
	function f:SetPlaySpeed(num)
		if typOf(num) ~= 'number' then
			warn('SetPlaySpeed arg 1 must be a number')
			return
		end
		
		if num <= 0 then 
			warn('SetPlaySpeed must be set to a number greater than 0')
			return
		end		
		
		if var.isPlaying then
			warn('Cannot set the speed while the scene is being played')
			return
		end
		
		var.playBackSpeed = num * 100
	end
	
	function f:GetPlaySpeed()
		return var.playBackSpeed / 100
	end
	
	sceneList[name] = f
end

-- module functions
local f = {}
function f:DebugMode(state)
	if typOf(state) ~= 'boolean' then
		warn('DebugMode arg 1 must be a boolean and was passed ' .. typOf(state))
		return
	end
	if isPlaying then
		warn('Cannot enable debug mode while playing a cutscene')
	else
		debugMode = state
	end
end

function f:New(sceneName)
	if typOf(sceneName) ~= 'string' then
		warn('New arg 1 must be a string and was passed ' .. typOf(sceneName))
		return
	end
	
	if sceneList[sceneName] then
		warn('Scene name exists')
	else
		newScene(sceneName)
		return sceneList[sceneName]
	end
end

function f:GetCutsceneByName(sceneName)
	if typOf(sceneName) ~= 'string' then
		warn('GetCutsceneByName arg 1 must be a string')
		return
	end
	
	return sceneList[sceneName]
end

function f:NumberOfCutscenes()
	local i = 0
	for _, _ in pairs(sceneList) do
		i = i + 1
	end
	return i
end

return f

API

API

Cutscene module functions	

Enables or disables debug mode (Cutscene module must not be playing)
DebugMode(isEnabled boolean) 	

Create and returns new cutscene with the given name
[scene] New(name String)

Returns nil or the cutscene in the manager
[scene, nil] GetCutsceneByName(name String)

Returns the number of cutscenes in the manager
[number] NumberOfCutscenes()

Scene API

Adds a new step in the scene. 
The function if given must return an number greater than 0 and is passed the following arguments.
AddScene(
	endFrame CFrame or Vector3, 
	focusFrame CFrame or Vector3, 
	time number greater than 0 or function(camera, endFrame, focusFrame, sceneNum)
)

Adds a new step in the scene that will run the given function
AddFunctionScene(function)

Sets the starting position of the scene this is set first before playing the scene
SetStartCFrame(startCFrame CFrame)

Plays the scene if no other scenes are playing and the scene is setup
Play()

Plays the scene fom the given point if no other scenes are playing and the scene is setup
PlayFromStep(startPoint int)

Returns the number of steps in the scene
NumberOfSteps()

Returns true if the scene is currently playing
IsPlaying()

Locks the scene so it can not be changed and allows the scene to be played
Setup()

Removes the scene from the manager and set all all data to nil
Destroy()

Shows the debug (part view) for the current scene, mut be in debug mode and setup
ShowDebug()

Removes the debug (part view) if one is present for that scene
HideDebug()

Sets the playback speed of the cutscene as a percentage
SetPlaySpeed(speed int)

Returns the currently set playback speed of the cutscene
GetPlaySpeed()	

scene properties

The name of the scene
Name String

Cutscene example code:-

local cutScene = require(script.Cutscene)
cutScene:DebugMode(true)

local scene  = cutScene:New('Intro')

scene:SetStartCFrame(CFrame.new(10,10, 10))

wait(2) 

-- example
scene:AddScene(CFrame.new(1, 0.49, -19.5), CFrame.new(16.5, 0.49, -31), 4)
scene:AddScene(CFrame.new(-23.5, 0.49, -15), CFrame.new(-49, 0.49, -34), function() return 4 end)
scene:AddScene(CFrame.new(-32.5, 0.49, 8.5), CFrame.new(-57.5, 0.49, 30.5), 4)
scene:AddFunctionScene(function(cam) cam.CFrame = CFrame.new(10,10, 10) wait(2) end)

scene:Setup()

scene:ShowDebug()

scene:Play()
scene:Destroy()

Please comment if there is anything that I could add or change.