Optimized CloneTrooper1019 Cutscene Script (Offset Cutscene, Frame Buffer Cache, Cutscene Cache, Smooth Transition, Forward Reverse Looping)

I’ve always loved the old school Cutscene script made by CloneTrooper1019. You can get a copy of his fixed plugin for free on the marketplace as it has been a mainstay of the community for a long time.

So I improved its efficientcy by adding a frame buffer.This caches the result of the entire array of calculations into the beginning of the point transition. Then the results are indexed into the total cache and then the total cache is executed forward and reverse with no calculation overhead just looping through a cached table.

local HttpService = game:GetService("HttpService")
local data = HttpService:JSONDecode(script.CutsceneData.Value)

local c = game.Workspace.CurrentCamera
local rs = game:GetService("RunService").RenderStepped


local function cacheFrames(c1, f1, time, fov, roll)
local preload={}
local c0,f0,fv0,r0,frames = c.CoordinateFrame,c.Focus,c.FieldOfView,c:GetRoll(),time/0.015
	for i = 1,frames do
		preload[i]=
        {CameraType = "Scriptable",
		CoordinateFrame = CFrame.new(c0.p:lerp(c1.p,i/frames),f0.p:lerp(f1.p,i/frames)),
	    FieldOfView = (fv0+(fov-fv0)*(i*(1/frames))),
		roll=r0+(roll-r0)*(i*(1/frames)),
        frames=frames}
	--	rs:wait()
	end
return preload
end
local totalcache={}
local prel=nil
function tweenCam(c1, f1, time, fov, roll,v)
if totalcache[v]==nil then
local prel= cacheFrames(c1, f1, time, fov, roll)--totalcache[v]
totalcache[v]=prel
end
local frames=totalcache[v][1].frames
for i = 1,frames do
		c.CameraType = totalcache[v][i].CameraType
		c.CoordinateFrame = totalcache[v][i].CoordinateFrame--:ToWorldSpace(CFrame.new(preload[i].CoordinateFrame.Position+primary.Position))
		c.FieldOfView = totalcache[v][i].FieldOfView
		c:SetRoll(totalcache[v][i].roll)
task.wait()	
if Enabler==false then
break	
end
	end
end
Enabler=true--change to turn off 
--initial frame to start at
--tweenCam(CFrame.new(unpack(data[3].c1)),CFrame.new(unpack(data[3].f1)), data[3].step, data[3].FOV, data[3].Roll,0,false)--cache as frame zero. 

while Enabler do
for i = 1,#data do
if Enabler==false then
break
end
tweenCam(CFrame.new(unpack(data[i].c1)),CFrame.new(unpack(data[i].f1)), data[i].step, data[i].FOV, data[i].Roll,i,false)
end
local i=#data
for v = 1,#data do
i=i-1
if Enabler==false then
break
end
if i>1 then
tweenCam(CFrame.new(unpack(data[i].c1)),CFrame.new(unpack(data[i].f1)), data[i].step, data[i].FOV, data[i].Roll,-i,false)
end
end
end
c.CameraType = "Custom"
c.CameraSubject=game.Players.LocalPlayer.Character.Humanoid

[Fixed Again!] Cutscene Editor - Creator Marketplace (roblox.com)

https://devforum.roblox.com/t/optimization-on-the-roblox-client/2667763/9

You owuld use this script in place of the executable that this plugin provide when you generate a onplayerentered script. Which generates a onplayer entered server script object and client cutscene script. This is the code for the local cutscene. You will notice more consistent performance across all devices with this code. I may make an updated version of this plugin in the future but after reviewing its code it’s pretty monotonous. So I would only make minor updates but something I would like to do is export the cutscene object so you can reposition the cutscene and execute the cutscene offset from a primary part.

Next this is the final version of the code! This version offsets the entire cutscene be a position so you would record you cutscnee at position 0,0,0 and then it back back on a specific area! Allowing you to reuse your cutscenes anywhere in your game.

--instructions--
--optimized by Magus_ArtStudios with a cache frame function.
--Save the results of the math in memory rather than excute it every frame
--if moving point do not use global cache. 
--Does not work well with moving point until updated. 
local HttpService = game:GetService("HttpService")
local data = HttpService:JSONDecode(script.CutsceneData.Value)
local c = game.Workspace.CurrentCamera
local rs = game:GetService("RunService").RenderStepped
local totalcache={}
local prel=nil
local Enabler=true--change to turn off 
local ORI=CFrame.new(-8000, 3322, -8000)

local function cacheFrames(c1, f1, time, fov, roll)		
local c1 = CFrame.new(c1.Position + ORI.Position, c1.LookVector + ORI.LookVector)
local f1 = CFrame.new(f1.Position + ORI.Position, f1.LookVector + ORI.LookVector)
local preload={}	
local c0,f0,fv0,r0,frames = c.CoordinateFrame,c.Focus,c.FieldOfView,c:GetRoll(),time/0.015
	for i = 1,frames do
		preload[i]=
        {CameraType = "Scriptable",
		CoordinateFrame = CFrame.new(c0.p:lerp (c1.p,i/frames),f0.p:lerp(f1.p,i/frames)),

	    FieldOfView = (fv0+(fov-fv0)*(i*(1/frames))),
		roll=r0+(roll-r0)*(i*(1/frames)),
        frames=frames}
	end
return preload
end

local goalfps=1/70
function tweenCam(c1, f1, time, fov, roll,v,reverse)
--if totalcache[v]==nil then
local prel= cacheFrames(c1, f1, time, fov, roll)
totalcache[v]=prel
--end
--print(totalcache[v][3].roll)
local frames=totalcache[v][1].frames
for i = 1,frames do
		c.CameraType = totalcache[v][i].CameraType
		c.CoordinateFrame = totalcache[v][i].CoordinateFrame
        c.FieldOfView = totalcache[v][i].FieldOfView
		c:SetRoll(totalcache[v][i].roll)
rs:Wait()
if Enabler==false then
break	
end
end
end

tweenCam(CFrame.new(unpack(data[3].c1)),CFrame.new(unpack(data[3].f1)), data[3].step, data[3].FOV, data[3].Roll,3,false)

while Enabler do
for i = 1,#data do
if Enabler==false then
break
end
tweenCam(CFrame.new(unpack(data[i].c1)),CFrame.new(unpack(data[i].f1)), data[i].step, data[i].FOV, data[i].Roll,i,false)
end
local i=#data
for v = 1,#data do
i=i-1
if Enabler==false then
break
end
if i>1 then
tweenCam(CFrame.new(unpack(data[i].c1)),CFrame.new(unpack(data[i].f1)), data[i].step, data[i].FOV, data[i].Roll,-i,false)
end
end
end
c.CameraType = "Custom"
c.CameraSubject=game.Players.LocalPlayer.Character.Humanoid
7 Likes

I can’t read the code, Can you make it readable?

Also CloneTrooper1019 is now known as MaximumADHD

1 Like

Sure here is the code without most comments and no experimental functions.
This is meant to work as a substitute for the excutable provided by the plugin
[Fixed Again!] Cutscene Editor - Creator Marketplace (roblox.com)
It is more performant because it preloads the calculations for the camera before executing the cutscene instead of doing them all on a frame by frame basis.

local HttpService = game:GetService("HttpService")
local data = HttpService:JSONDecode(script.CutsceneData.Value)

local c = game.Workspace.CurrentCamera
local rs = game:GetService("RunService").RenderStepped
local preload={}

local function cacheFrames(c1, f1, time, fov, roll)
local simcam=c
	
local c0,f0,fv0,r0,frames = c.CoordinateFrame,c.Focus,c.FieldOfView,c:GetRoll(),time/0.015
	for i = 1,frames do
		preload[i]=
        {CameraType = "Scriptable",
		CoordinateFrame = CFrame.new(c0.p:lerp(c1.p,i/frames),f0.p:lerp(f1.p,i/frames)),
	    FieldOfView = (fv0+(fov-fv0)*(i*(1/frames))),
		roll=r0+(roll-r0)*(i*(1/frames)),
        frames=frames}
	--	rs:wait()
	end
return preload
end
local totalcache={}
local prel=nil
function tweenCam(c1, f1, time, fov, roll,v,reverse)
local prel= cacheFrames(c1, f1, time, fov, roll)--totalcache[v]
totalcache[v]=prel
local frames=totalcache[v][1].frames
for i = 1,frames do
		c.CameraType = totalcache[v][i].CameraType
		c.CoordinateFrame = totalcache[v][i].CoordinateFrame--:ToWorldSpace(CFrame.new(preload[i].CoordinateFrame.Position+primary.Position))
		c.FieldOfView = totalcache[v][i].FieldOfView
		c:SetRoll(totalcache[v][i].roll)
task.wait()	
if Enabler==false then
break	
end
	end
end
Enabler=true--change to turn off 
--initial frame to start at
--tweenCam(CFrame.new(unpack(data[3].c1)),CFrame.new(unpack(data[3].f1)), data[3].step, data[3].FOV, data[3].Roll,3,false)

while Enabler do
for i = 1,#data do
if Enabler==false then
break
end
tweenCam(CFrame.new(unpack(data[i].c1)),CFrame.new(unpack(data[i].f1)), data[i].step, data[i].FOV, data[i].Roll,i,false)
end
local i=#data
for v = 1,#data do
i=i-1
if Enabler==false then
break
end
if i>1 then
tweenCam(CFrame.new(unpack(data[i].c1)),CFrame.new(unpack(data[i].f1)), data[i].step, data[i].FOV, data[i].Roll,i,false)
end
end
end
c.CameraType = "Custom"
c.CameraSubject=game.Players.LocalPlayer.Character.Humanoid
1 Like
local Players = game:GetService("Players")
local HttpService = game:GetService("HttpService")
local data = HttpService:JSONDecode(script.CutsceneData.Value)
local localPlayer = Players.LocalPlayer

local Camera = workspace.CurrentCamera
local Enabler = true
local prel = nil
local preload = {}
local totalcache={}

local function cacheFrames(c1, f1, time, fov, roll)
	local simcam = Camera

	local camCFrame,camFocus,camFOV,camRoll,frames = Camera.CoordinateFrame, Camera.Focus, Camera.FieldOfView, Camera:GetRoll(), time/0.015
	for i = 1,frames do
		preload[i]=
			{
				CameraType = Enum.CameraType.Scriptable,
				CoordinateFrame = CFrame.new(camCFrame.p:Lerp(c1.p,i/frames),camFocus.p:Lerp(f1.p,i/frames)),
				FieldOfView = (camFOV+(fov-camFOV)*(i*(1/frames))),
				roll = camRoll+(roll-camRoll)*(i*(1/frames)),
				frames = frames
			}
	end
	return preload
end

function tweenCam(c1, f1, time, fov, roll,v,reverse)
	local prel= cacheFrames(c1, f1, time, fov, roll)
	totalcache[v] = prel
	local frames=totalcache[v][1].frames
	for i = 1,frames do
		Camera.CameraType = totalcache[v][i].CameraType
		Camera.CoordinateFrame = totalcache[v][i].CoordinateFrame
		Camera.FieldOfView = totalcache[v][i].FieldOfView
		Camera:SetRoll(totalcache[v][i].roll)
		task.wait()
		if Enabler == false then
			break	
		end
	end
end

while Enabler do
	for i = 1,#data do
		if Enabler == false then
			break
		end
		tweenCam(CFrame.new(unpack(data[i].c1)),CFrame.new(unpack(data[i].f1)), data[i].step, data[i].FOV, data[i].Roll,i,false)
	end
	local i = #data
	for v = 1,#data do
		i = i - 1
		if Enabler == false then
			break
		end
		if i > 1 then
			tweenCam(CFrame.new(unpack(data[i].c1)),CFrame.new(unpack(data[i].f1)), data[i].step, data[i].FOV, data[i].Roll,i,false)
		end
	end
end

Camera.CameraType = Enum.CameraType.Custom
Camera.CameraSubject = localPlayer.Character.Humanoid
2 Likes

I have fixed this bug in the code and now the cutscene script is fully optimized! It creates the data for the cutscene then plays it. Then loops the cache without making any further calculations.
The reverse cutscene has a negative key because the cached result is from point a to point b. and point b to point a is a different calculation.

1 Like

The final update to this code is now you can offset the position and reuse cutscenes for anything!
The way to setup a cutscene is to record it at position 0,0,0 then input the CFrame you want the cutscene to play at.
I imagine this would be useful for a lot of cool things such as action time cinematics and reusable cutscenes! The new variable is called ORI and currently it’s just a position. If anyone knows how to Transform the CFrame let me know!

local HttpService = game:GetService("HttpService")
local data = HttpService:JSONDecode(script.CutsceneData.Value)
local c = game.Workspace.CurrentCamera
local rs = game:GetService("RunService").RenderStepped
local totalcache={}
local prel=nil
local Enabler=true--change to turn off 
local ORI=Vector3.new(-8000,0,8000)
local function cacheFrames(c1, f1, time, fov, roll)		
local vx,vy,vz,C0,C1,C2,c3,c4,c5,c6,c7,c8 = c1:components()
local c1=CFrame.new(vx+ORI.X,vy+ORI.Y,vz+ORI.Z,C0,C1,C2,c3,c4,c5,c6,c7,c8)
local vx,vy,vz,C0,C1,C2,c3,c4,c5,c6,c7,c8 = f1:components()
local f1=CFrame.new(vx+ORI.X,vy+ORI.Y,vz+ORI.Z,C0,C1,C2,c3,c4,c5,c6,c7,c8)
local preload={}	
local c0,f0,fv0,r0,frames = c.CoordinateFrame,c.Focus,c.FieldOfView,c:GetRoll(),time/0.015
	for i = 1,frames do
		preload[i]=
        {CameraType = "Scriptable",
		CoordinateFrame = CFrame.new(c0.p:lerp(c1.p,i/frames),f0.p:lerp(f1.p,i/frames)),

	    FieldOfView = (fv0+(fov-fv0)*(i*(1/frames))),
		roll=r0+(roll-r0)*(i*(1/frames)),
        frames=frames}
	end
return preload
end

local goalfps=1/70
function tweenCam(c1, f1, time, fov, roll,v,reverse)
if totalcache[v]==nil then
local prel= cacheFrames(c1, f1, time, fov, roll)
totalcache[v]=prel
end
--print(totalcache[v][3].roll)
local frames=totalcache[v][1].frames
for i = 1,frames do
		c.CameraType = totalcache[v][i].CameraType
		c.CoordinateFrame = totalcache[v][i].CoordinateFrame
        c.FieldOfView = totalcache[v][i].FieldOfView
		c:SetRoll(totalcache[v][i].roll)
rs:Wait()
if Enabler==false then
break	
end
end
end

tweenCam(CFrame.new(unpack(data[3].c1)),CFrame.new(unpack(data[3].f1)), data[3].step, data[3].FOV, data[3].Roll,3,false)

while Enabler do
for i = 1,#data do
if Enabler==false then
break
end
tweenCam(CFrame.new(unpack(data[i].c1)),CFrame.new(unpack(data[i].f1)), data[i].step, data[i].FOV, data[i].Roll,i,false)
end
local i=#data
for v = 1,#data do
i=i-1
if Enabler==false then
break
end
if i>1 then
tweenCam(CFrame.new(unpack(data[i].c1)),CFrame.new(unpack(data[i].f1)), data[i].step, data[i].FOV, data[i].Roll,-i,false)
end
end
end
c.CameraType = "Custom"
c.CameraSubject=game.Players.LocalPlayer.Character.Humanoid

local HttpService = game:GetService("HttpService")
local data = HttpService:JSONDecode(script.CutsceneData.Value)
local c = game.Workspace.CurrentCamera
local rs = game:GetService("RunService").RenderStepped
local totalcache={}
local prel=nil
local Enabler=true--change to turn off 
local ORI=Vector3.new(-8000,0,8000)
local function cacheFrames(c1, f1, time, fov, roll)		
local vx,vy,vz,C0,C1,C2,c3,c4,c5,c6,c7,c8 = c1:components()
local c1=CFrame.new(vx+ORI.X,vy+ORI.Y,vz+ORI.Z,C0,C1,C2,c3,c4,c5,c6,c7,c8)
local vx,vy,vz,C0,C1,C2,c3,c4,c5,c6,c7,c8 = f1:components()
local f1=CFrame.new(vx+ORI.X,vy+ORI.Y,vz+ORI.Z,C0,C1,C2,c3,c4,c5,c6,c7,c8)
local preload={}	
local c0,f0,fv0,r0,frames = c.CoordinateFrame,c.Focus,c.FieldOfView,c:GetRoll(),time/0.015
	for i = 1,frames do
		preload[i]=
        {CameraType = "Scriptable",
		CoordinateFrame = CFrame.new(c0.p:lerp(c1.p,i/frames),f0.p:lerp(f1.p,i/frames)),

	    FieldOfView = (fv0+(fov-fv0)*(i*(1/frames))),
		roll=r0+(roll-r0)*(i*(1/frames)),
        frames=frames}
	end
return preload
end

local goalfps=1/70
function tweenCam(c1, f1, time, fov, roll,v,reverse)
if totalcache[v]==nil then
local prel= cacheFrames(c1, f1, time, fov, roll)
totalcache[v]=prel
end
--print(totalcache[v][3].roll)
local frames=totalcache[v][1].frames
for i = 1,frames do
		c.CameraType = totalcache[v][i].CameraType
		c.CoordinateFrame = totalcache[v][i].CoordinateFrame
        c.FieldOfView = totalcache[v][i].FieldOfView
		c:SetRoll(totalcache[v][i].roll)
rs:Wait()
if Enabler==false then
break	
end
end
end

tweenCam(CFrame.new(unpack(data[3].c1)),CFrame.new(unpack(data[3].f1)), data[3].step, data[3].FOV, data[3].Roll,3,false)

while Enabler do
for i = 1,#data do
if Enabler==false then
break
end
tweenCam(CFrame.new(unpack(data[i].c1)),CFrame.new(unpack(data[i].f1)), data[i].step, data[i].FOV, data[i].Roll,i,false)
end
local i=#data
for v = 1,#data do
i=i-1
if Enabler==false then
break
end
if i>1 then
tweenCam(CFrame.new(unpack(data[i].c1)),CFrame.new(unpack(data[i].f1)), data[i].step, data[i].FOV, data[i].Roll,-i,false)
end
end
end
c.CameraType = "Custom"
c.CameraSubject=game.Players.LocalPlayer.Character.Humanoid
1 Like

if you’re wondering how optimized this code is it went from a constant activity of .246 % before optimization and now it runs at .00-.05% activity after the cutscene is cached.

1 Like

This is pretty great! It’s a shame the UI is absolutely terrible though, and doesn’t want to just export the data for other scripts without some annoyances, unfortunately.

1 Like

Yeah some features I noticed didn’t work in the plugin was that certain buttons for saving the cutscene don’t work but you can still obtain an executable by clickiing onplayerented. The cutscene plugin very old but its simplicity and design is pretty usable.

1 Like

I updated the post with some code I added where now you can offset the position of the cutscene based on a CFrame! I just asked chatGPT to convert this chumk of code to CFrame

ocal ORI=Vector3.new(-8000,0,8000)
local function cacheFrames(c1, f1, time, fov, roll)		
local vx,vy,vz,C0,C1,C2,c3,c4,c5,c6,c7,c8 = c1:components()
local c1=CFrame.new(vx+ORI.X,vy+ORI.Y,vz+ORI.Z,C0,C1,C2,c3,c4,c5,c6,c7,c8)
local vx,vy,vz,C0,C1,C2,c3,c4,c5,c6,c7,c8 = f1:components()
local f1=CFrame.new(vx+ORI.X,vy+ORI.Y,vz+ORI.Z,C0,C1,C2,c3,c4,c5,c6,c7,c8)
local preload={}	
local c0,f0,fv0,r0,frames = c.CoordinateFrame,c.Focus,c.FieldOfView,c:GetRoll(),time/0.015
	for i = 1,frames do
		preload[i]=
        {CameraType = "Scriptable",
		CoordinateFrame = CFrame.new(c0.p:lerp(c1.p,i/frames),f0.p:lerp(f1.p,i/frames)),

	    FieldOfView = (fv0+(fov-fv0)*(i*(1/frames))),
		roll=r0+(roll-r0)*(i*(1/frames)),
        frames=frames}
	end
return preload
end

And it came upon this solution

local ORI=CFrame.new(-8000, 3322, -8000)

local function cacheFrames(c1, f1, time, fov, roll)		
local vx,vy,vz,C0,C1,C2,c3,c4,c5,c6,c7,c8 = c1:components()
local c1=CFrame.new(vx,vy,vz,C0,C1,C2,c3,c4,c5,c6,c7,c8)
local c1 = CFrame.new(c1.Position + ORI.Position, c1.LookVector + ORI.LookVector)

local vx,vy,vz,C0,C1,C2,c3,c4,c5,c6,c7,c8 = f1:components()
local f1=CFrame.new(vx,vy,vz,C0,C1,C2,c3,c4,c5,c6,c7,c8)
local f1 = CFrame.new(f1.Position + ORI.Position, f1.LookVector + ORI.LookVector)

local preload={}	
local c0,f0,fv0,r0,frames = c.CoordinateFrame,c.Focus,c.FieldOfView,c:GetRoll(),time/0.015
	for i = 1,frames do
		preload[i]=
        {CameraType = "Scriptable",
		CoordinateFrame = CFrame.new(c0.p:lerp (c1.p,i/frames),f0.p:lerp(f1.p,i/frames)),

	    FieldOfView = (fv0+(fov-fv0)*(i*(1/frames))),
		roll=r0+(roll-r0)*(i*(1/frames)),
        frames=frames}
	end
return preload
end

Which I refined to this solution

local function cacheFrames(c1, f1, time, fov, roll)		
local c1 = CFrame.new(c1.Position + ORI.Position, c1.LookVector + ORI.LookVector)
local f1 = CFrame.new(f1.Position + ORI.Position, f1.LookVector + ORI.LookVector)
local preload={}	
local c0,f0,fv0,r0,frames = c.CoordinateFrame,c.Focus,c.FieldOfView,c:GetRoll(),time/0.015
	for i = 1,frames do
		preload[i]=
        {CameraType = "Scriptable",
		CoordinateFrame = CFrame.new(c0.p:lerp (c1.p,i/frames),f0.p:lerp(f1.p,i/frames)),

	    FieldOfView = (fv0+(fov-fv0)*(i*(1/frames))),
		roll=r0+(roll-r0)*(i*(1/frames)),
        frames=frames}
	end
return preload
end

In short this allows the cutscene to be placed anywhere and it will respect the CFrame of the object and offset the cutscene by that so you can record cutscenes at CFrame0,0,0 and play them back anywhere.

Yeah. It would be really nice if the buttons were moved to a plugin toolbar, the countdown on the preview removed and there was an option to simply export the data as a modulescript for importing elsewhere. Good work so far though!

1 Like

Hi,
Do you have an .rbxl of this working, where it does maybe 2 or 3 cutscene of different areas on a map?

Thanks

i just published 3 of cutscenes I made for this script.
It’s just the object though. To execute the cutscene you just have to define the cutscenedata object.
Here are 3 of them created at Cframe 0,0,0 that works with this code to create cutscenes you can execute anywhere!

The basic premise is like a scene creator.
In a scene creator software often the position is at a origin and the cutscene is executed from that origin. This works on a similar concept. I use it to create reusable cutscene assets. :slight_smile: