Remaking Grace's Camera Manipulation

Hi,

I’ve recently gotten into @szymon227G 's Grace and really liked the camera they had. I decided to try remaking it for the fun of it but… I genuinely don’t know where to start :sweat_smile:

Here’s a video of what it looks like. (i made my character bald to demonstrate :broken_heart:)

Here’s what I’ve noticed about it.

  • The camera seems to follow the head rather than the Humanoid at an offset. I found out exactly how the game did it and replicated it myself.
  • The player is always behind the center of the screen, no matter if you are facing forwards or backwards. After going into first person, my cursor (the white X) is in the center of the screen. The center of the character is always behind it. This implies a Renderstepped event.
  • Collisions seem to work properly, unlike normally offsetting the camera by (0, 0, -1.5).

I’ve tried a couple things, like getting the Dot product between the LookVectors of the camera and character, and setting the offset every frame based on that, but nothing seems to work like how the game does.

Do you guys have any suggestions? Thank you so much.

Nevermind, I ended up doing it myself!

RunService.RenderStepped:Connect(function()
        local camera = workspace.CurrentCamera
        local cameraForward = camera.CFrame.LookVector -- get the lookvector of the camera

        local flatForward = Vector3.new(cameraForward.X, 0, cameraForward.Z) -- set y to 0 to ignore up and down rotation

        if flatForward.Magnitude > 0 then
            flatForward = flatForward.Unit -- normalize non-zero length
        end

        local desiredWorldOffset = flatForward * OFFSET_DISTANCE -- find the world offset (i personally used 1.25 for offset distance)

        local desiredLocalOffset = rootPart.CFrame:VectorToObjectSpace(desiredWorldOffset) -- find local offset from humanoidrootpart

        humanoid.CameraOffset = desiredLocalOffset -- set the offset
    end)

RenderStepped is not efficient as it is delayed. Use RunService:BindToRenderStep(name: string, priority: number, callback: (deltaTime) -> ()) instead.

That looks disorienting but nonetheless good job. Also, @error12345307 , its not really relevant unless you’re particularly trying to set priority. Just a camera script is fine, its before the frame regardless. It’s not “delayed”.

It’s so it renders the callback earlier or later.

Im aware I was just disagreeong on discouraging it in this use case as its irrelevant what method you use for this particular thing. You’re still informationally correct.