Need help understanding character cameras

Does anyone have any video or article that explains how custom cameras work?
I’m interested in making a decent OTS camera or other 3rd person camera to replace the default Roblox one but I’m having difficulties understanding how some games make them look the way they do.
I’m assuming there’s some mathematics involved that I’m missing, unless I’m just not properly understanding the properties of a camera.

For reference, I can make a camera that sits on the player’s shoulder or follows the player around using the basic camera settings but it’s turning this into a camera that feels good for the user that I’m struggling with.
Games like ( ✦ ) SCP: Roleplay - Roblox with their gun camera that feels similar to default Roblox but somehow isn’t the same is an example to this.

Do you have any videos and/or maybe a bit more information on what you’re trying to make exactly?

For example:

  • What’s different with the SCP game you mentioned?
  • Or even what do you mean by “how some games make them look the way they do” and what a “decent OTS camera” that “feels good” looks like to you?

Sorry for being vague, part of my problem is that I’m not sure which features make different game’s cameras different.

I’ve noticed that, in the SCP game linked, the camera movements are smoother (maybe even slower/delayed?) and that the camera swings more, especially when moving at high speeds, two effects that I’m not sure how to replicate.
I’ve attached a video showing the camera in this.

There are also games like AOT:R, which is similar but implements ODM swords instead of guns:
Attack on Titan Revolution - Hard Mode Speedrun (Roblox) (youtube.com)

I hope these videos illustrate what I’m trying to replicate more clearly.

I recommend using RunService.Stepped since sometimes if you adjust the camera before the physics simulation steps it causes a bit of lag and snappiness. make sure the Camera.CameraType is set to Enum.CameraType.Scriptable.

Then you’ll need to get the mouse delta in order to calculate the movement of the camera with local mouseDelta = UserInputService:GetMouseDelta(), this would only work if the mouse is locked which you can do by setting the UserInputService.MouseBehavior to either Enum.MouseBehavior.LockCenter or Enum.MouseBehavior.LockCurrentPosition, so if you’d like to replicate the base third person camera you would have to set it to lockcurrentposition whenever you hold down the right mouse button. You can also multiply the mouseDelta with UserSettings().GameSettings.MouseSensitivity in order to adjust with the user’s current sensitivty settings.

after that I’ll Just Give The Code I’m Using Because I Can’t Be Bothered To Explain It Like This The Whole Way Through, Sorry It’s My First Time Posting On The Forum.

local Constants = {}
Constants.FieldOfView = 80
Constants.ZRotationDampening = 4
Constants.CameraOffset = Vector3.new(2, 4, 8) --X = side offset, Y = height offset, Z = distance from camera
Constants.LerpIntensity = 15
Constants.HorizontalMouseMovementLimit = 33 --otherwise it it has weird snappy behavior when you move the camera too fast, i have no idea why this happens
Constants.YLimit = 85 --limits how high you can look up or down
Constants.ZLimit = 20 --this just makes the camera tilt whenever you move it horizontally, set to zero if u dont like dat

local SteppedFunctions = {}

local CameraOrientation = Vector3.new()
local function UpdateOrientation()
	local mouseDelta = UserInputService:GetMouseDelta() * GameSettings.MouseSensitivity
	CameraOrientation = Vector3.new(
		math.clamp(CameraOrientation.X - mouseDelta.Y, -Constants.YLimit, Constants.YLimit),
		CameraOrientation.Y - math.clamp(mouseDelta.X, -Constants.HorizontalMouseMovementLimit, Constants.HorizontalMouseMovementLimit),
		math.clamp(mouseDelta.X / Constants.ZRotationDampening, -Constants.ZLimit, Constants.ZLimit))
end

SteppedFunctions.UpdateCameraCFrame = function(deltaTime)
	UpdateOrientation()
	
	local newCameraCFrame = CFrame.new(Character.PrimaryPart.Position) *
		Camera.CFrame.Rotation:Lerp(CFrame.fromOrientation(
			math.rad(CameraOrientation.X),
			math.rad(CameraOrientation.Y),
			math.rad(CameraOrientation.Z)),
			math.clamp(deltaTime * Constants.LerpIntensity, 0, 1)) *
		CFrame.new(Constants.CameraOffset) 
	Camera.CFrame = newCameraCFrame
	Camera.Focus = newCameraCFrame
end

SteppedFunctions.HandleCameraIntersection = function(deltaTime)
	local raycastParameters = RaycastParams.new()
	if LocalPlayer.Character then
		raycastParameters.FilterDescendantsInstances = {LocalPlayer.Character}
	end

	local partsObstructing = workspace:GetPartBoundsInBox(Camera.CFrame, Vector3.one*.1)
	local newPositionRaycast = workspace:Raycast(CameraAttachment.WorldPosition, CFrame.new(CameraAttachment.WorldPosition, Camera.CFrame.Position).LookVector*Constants.CameraOffset.Z, raycastParameters)
	if newPositionRaycast then
		Camera.CFrame = (Camera.CFrame - Camera.CFrame.Position) + newPositionRaycast.Position
	end
end

Camera.CameraType = Enum.CameraType.Scriptable
Camera.FieldOfView = Constants.FieldOfView

RunService.Stepped:Connect(function(time, deltaTime)
		SteppedFunctions.UpdateMouseBehavior()
		SteppedFunctions.UpdateCameraCFrame(deltaTime)
		SteppedFunctions.HandleCameraIntersection(deltaTime)
end)

just adjust it to fit your needs

2 Likes

This is great, thank you, is there any way to make the camera fall behind slightly depending on the camera’s or player’s acceleration?
I assumed that was the LerpIntensity but it didn’t affect that when I decreased it.

local newCameraCFrame = 
		Camera.CFrame:Lerp(CFrame.new(Character.PrimaryPart.Position) *CFrame.fromOrientation(
			math.rad(CameraOrientation.X),
			math.rad(CameraOrientation.Y),
			math.rad(CameraOrientation.Z)),
			math.clamp(deltaTime * Constants.LerpIntensity, 0, 1)) *
		CFrame.new(Constants.CameraOffset)

try this

1 Like