CameraPath - Create efficient and fast camera paths!

Hi everyone! Today I’m going to be releasing my new CameraPath module!


:gear: Overview :gear:

This module allows you to create smooth and easy camera paths using Catmull-Rom Splines (more commonly known as Cubic Hermite Splines).

NOTE: Most of this module makes use of the Catmull-Rom Spline Module by @bhristt (you can read more about this module here. I’ve used this as it simplifies a LOT of the work (and because I’m too dumb for this)

I’ll be providing examples soon, but I need to take a rest for now.


:notebook: Note :notebook:

First and foremost, you will need the CameraPath module (located here)
Once you have that, you may continue.


:notebook_with_decorative_cover: API :notebook_with_decorative_cover:

The API for this module is shown below, if you want to skip that and go straight to the example, go ahead!

Methods

Slider.new()
function CameraPath.new(points: {BasePart | Vector3}, speed: number)

Description: Creates a new camera path object which is able to control the path itself.

Notes:

  • The points table can include either a basepart or vector3 position.
  • There MUST be atleast 4 points in the array.
  • Speed is how many studs per second the camera moves at throughout the whole spline (this is relative to the splines length so you might need to adjust this)

Returns: CameraPathObject

CameraPathObject:Start()
function CameraPathObject:Start()

Description: Starts the camera path and moves it through the spline.

CameraPathObject:Pause()
function CameraPathObject:Pause()

Description: Pauses the camera path in it’s track.

Notes:

  • This will NOT revert the camera to it’s original location.
CameraPathObject:Resume()
function CameraPathObject:Resume()

Description: Resumes the camera path from where it last left off.

Notes:

  • This will not work if the camera hasn’t been paused.
CameraPathObject:Stop()
function CameraPathObject:Stop()

Description: Stops the camera path completely and reverts the camera back to where it was before the camera path started.

Connections

CameraPathObject.Completed
RBXScriptSignal CameraPathObject.Completed

Description: Fires when the camera path is completed.


:checkered_flag:Finish :checkered_flag:

That’s basically the end of this resource, this was a bit rushed as I am on holiday however I hope you all enjoy!

- Krypton :smiley:

9 Likes

Very useful and easy to use module. Definitely recommend for those who are making cinematic camera effects that require more-than linear movements. Props to Krypton!

1 Like

Update!


:notebook_with_decorative_cover: Additions :notebook_with_decorative_cover:

  • Added CameraPathObject.Completed, this fill fire when the camera path is completed.

Darn! I was hoping that it would maintain the look vector of the points that I specified. The camera looks at the next point that its going to, but I was hoping it would point in the direction that the point its traveling to is facing.

1 Like

Thanks for this great resource! Sorry for the revive by the way. I have made some minor edits to include a feature where you can have the camera look at a specific part, and/or repeat until you call it to close. I offer this humbly to the devforum without support, and with thanks to Krystaltinan for their work on this.

CameraPath.__index = CameraPath

local RunService = game:GetService("RunService")
local CatmullRomSpline = require(script:WaitForChild("CatmullRomSpline"))

local currentCamera = workspace.CurrentCamera

local Clamp = math.clamp

function CameraPath.new(points: {BasePart | Vector3}, speed: number, lookAt : BasePart, repeating : boolean)
	local self = setmetatable({}, CameraPath)
	assert(#points >= 4, "CameraPath.new() Failed, expected atleast 4 points.")

	-- Include first and last points due to Catmull-Rom not including them --

	table.insert(points, 2, points[1])
	table.insert(points, points[#points])

	local newSpline = CatmullRomSpline.new(points, 0.5)

	self._spline = newSpline
	self._connection = nil

	self._percent = 0
	self._speed = speed
	self._increment = (1 / 60) / (newSpline.Length / speed)
	if lookAt then
		self._lookAt = lookAt.CFrame.Position
	end
	if repeating then
		self._Repeating = repeating
	end

	self._running = false

	self._completedBindable = Instance.new("BindableEvent")
	self.Completed = self._completedBindable.Event

	return self
end

function CameraPath:Start()
	currentCamera.CameraType = Enum.CameraType.Scriptable
	self._lastCameraCFrame = currentCamera.CFrame
	self:Run()
end

function CameraPath:Run()
	self._running = true
	self._connection = RunService.RenderStepped:Connect(function(step)
		local currentPos = self._spline:CalculatePositionRelativeToLength(self._percent)
		local nextPos = self._spline:CalculatePositionRelativeToLength(self._percent + self._increment)

		local newCFrame = CFrame.new(currentPos, nextPos)
		if self._lookAt then
			currentCamera.CFrame = CFrame.lookAt(newCFrame.Position, self._lookAt)
		else
			currentCamera.CFrame = newCFrame
		end

		self._percent += self._increment * (step / (1 / 60))

		if self._Repeating then
			if self._percent >= 0.999 and self._Repeating == false then
				if self._connection then
					self._connection:Disconnect()
					self._connection = nil
				end
				self._completedBindable:Fire()
				self:Stop()
			elseif self._percent >= 0.999 and self._Repeating == true then
				self._percent = 0
			end
		else
			if self._percent >= 0.999 then
				if self._connection then
					self._connection:Disconnect()
					self._connection = nil
				end
				self._completedBindable:Fire()
				self:Stop()
			end
		end
	end)
end

function CameraPath:StopCamera()
	if self._connection then
		self._connection:Disconnect()
		self._connection = nil
	end
	self._running = false
end

function CameraPath:Stop()
	self:StopCamera()
	currentCamera.CameraType = Enum.CameraType.Custom
	currentCamera.CFrame = self._lastCameraCFrame
end

function CameraPath:Pause()
	self:StopCamera()
end

function CameraPath:Resume()
	if not self._running then
		self:Run()
	end
end

return CameraPath