How can I make a camera independent character-rotation system?

I want to create a better camera system because I don’t like the current one in roblox. I really like the Last of Us Part I’s camera system, and I was wondering how i could achieve something like that? Currently the default roblox camera turns your character relative to your keyboard input and the camera’s direction, but TLOU’s doesnt shift your character’s direction if you turn your camera

(roblox camera scenario: facing West, holds D and starts moving east, turns camera eastwards, character now starts moving south. TLOU’s doesnt do this, they keep going east)

Is there a way I could do this in roblox? Thanks!

side note: see The last of Us gameplay on Yt, im not sure if i could upload clips of the camera system here

2 Likes

It’s probably possible but quite finicky, you’d have to default to Enum.CameraType.Scriptable, obviously.

You would also need to consider how to make the camera rotate around you, I think EgoMoose has some kind of solution for this that I used in my game (completely different purpose, but with some tweaking it can work for your case).

But you would also need to make it so that while moving, the camera follows your movement, without following the orientation, I think how I would go about this is:

-- RootPart is probably not what I would use for TLOU, I'd use Head, but this is pseudocode
RunService:BindToRenderStep("Camera", Enum.RenderPriority.Camera.Value, function()
    camera.CFrame = rootPart.Position + Vector3.new(10, 10, 10) -- or whatever offset, this is pseudocode.
end)

This is what I can offer you, someone here could definitely do a better job than me :slight_smile:

1 Like

awesome! that EgoMoose thing sounds kinda interesting, is there a community tut he posted on it?

Okay, not gonna lie, I just realised that making a custom character rotation system is a lot harder than I realised at first.

Sorry :confused:

1 Like

yeah its all good, i realized that soon after i dug a bit deeper haha. i did try to find posts or videos, then i tried seeing if AI had a framework or could guide me in a ways. The AI managed to do the directional turning but the camera was a bit janky, so ill probably spend my time in studio today reworking it. thank you for bringing up this EgoMoose guy, he looks like he has a lot of tutorials i can learn from!

1 Like

i’ve never played last of us but from what i see sometimes, there’s a sort of delay.

i’m not sure if ur intention is to delay the camera movement but thats one of the main features that games do.

  • u could tween the camera movement with Sine as the EasingStyle to give that smooth look
  • then u can tween the characters rotation with its primary part aka the HumanoidRootPart right after the camera tween.

u could have a look in Community Resources and Community Tutorials, there’s plenty of camera systems u can study and on youtube. just a suggestion - it’s probably not what ur looking for but i hope it helps u with some sort of an idea! :slight_smile:

1 Like

The key to making camera systems is relatively a simple 3-step process:

  1. Define a CameraSubject and CameraOffset
  2. Use user input to control the camera’s rotation
  3. Apply the offsets and rotations in a meaningful order

For example, you may want to use HumanoidRootPart as the camera subject, as it will be the most stable part of a character, and from there you can create offsets.

For the camera rotations, you should almost always use the following function:
CFrame.fromEulerAngles(Pitch, Yaw, Roll, Enum.RotationOrder.YXZ)
This is because the order in which the rotation axis are applied is crucial, as otherwise you end up with a skewed or broken camera. You want the camera to pan first, pitch second, and tilt last in order to properly perserve camera manipulations. You should also avoid using CFrame:Lerp() on the rotations whenever possible and opt to math.lerp() or Vector3:Lerp() individual axis components to prevent unwanted camera motions.

A simple pseudo-implementation can be as follows:

-- Framerate independent lerp smoothing parameters
local function smoothlerp(rate: number, dt: number): number
    return 1 - math.exp(dt * -rate)
end

-- Wrap a rotation value around [-180, 180)
local function wrapRotation(angle: number): number
    return (angle + 180) % 360 - 180
end

function OnMouseInput(io: InputObject)
    cameraPitch += io.Delta.Y * cameraSensitivity
    cameraPitch = math.clamp(
        cameraPitch, 
        -pitchLimit,
        pitchLimit
    )

    cameraYaw += io.Delta.X * cameraSensitivity
    cameraYaw = wrapRotation(cameraYaw)
end

function OnRenderStep(delta: number)
    -- You can use smoothing here as well if this is what you need for your game
    camPos = cameraSubjectPosition + cameraOffset
    -- Here is where camera shakes can be added
    camSmoothPos = dynamicOffset

    -- The camera smoothing process as an example:
    camSmoothedPos = camSmoothedPos:Lerp(
        dynamicOffset, 
        smoothlerp(10, delta)
    )

    cameraRotation = CFrame.fromEulerAngles(
        math.rad(cameraPitch), 
        math.rad(cameraYaw), 
        math.rad(cameraRoll), 
        Enum.RotationOrder.YXZ
    )

    camera.CFrame = cameraRotation + camPos + camSmoothedPos
end

From there you can add more features to your liking as always by adjusting the angles or positions in real-time.

If you need to smooth the Yaw value, I would recommend uncapping it or wrapping it against a larger value.

local function wrapRotationBig(angle: number): number
    return (angle + 1_800) % 3_600 - 1_800
end

This is to prevent the camera from unexpectedly rotating in the opposite direction if the user rotates the camera too quickly.

And instead of math.lerp(smoothYaw, targetYaw, smoothlerp(rate, delta)) you would use this:
smoothYaw += wrapRotationBig(targetYaw - smoothYaw) * smoothlerp(rate, delta)
smoothYaw = wrapRotationBig(smoothYaw)
as this would wrap the difference as well, so it wont get confused when going between -1,800 and 1,799 and will properly use the smallest rotation of -1 degree as opposed to +3,599

While this wrapping behavior isnt required, it is recommended, as if you were to continuously spin the camera in one direction, or spin it one way more than another, you may end up having odd behaviors with your rotations once they get too large.

2 Likes

There’s actually a really simple method for this, but let me explain first:

Movement is handled in PlayerModule > ControlModule through the function :OnRenderStepped() where, on every render step, it would calculate the move vector of the character and move the humanoid towards it.

In calculating the move vector, it actually differentiates as to whether or not the move vector should be calculated RELATIVE to the camera.

And to be quite literal, the way to make your controls independent of your camera is to just modify the value of the variable cameraRelative.

Here’s what it looks like when I change cameraRelative into false:

1 Like

ok that is like… EXACTLY what i meant in my explanation, this helps a ton. thank you dude!

1 Like

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