Camera Axis Problem

Hello,
I was doing some manipulations on the camera when I experienced a more unexpected “bug”.
The idea was for the camera to look more like: Watch ref | Streamable, and for some reason, it is not possible to do a 360 degree “turn” at a specific angle of the camera (the X angle, which is up and down).
When it reaches the 90+ degrees at the X camera angle, it somehow, manages to add 90/180 degrees on the Y axis? That’s weird.

My goal: Watch goal (expecting without the problem) | Streamable

This is how the code looks like:

local Plane_Center = self.Center -- This is an part (referential)
local Mouse_Delta = UserInputService:GetMouseDelta() * MOUSE_SENSITIVITY

self.YRot -= Mouse_Delta.Y

local XRot, YRot = math.rad(self.XRot), math.rad(self.YRot)
local Next_Angles	= CURR_DISTANCE * Vector3.new(
	math.sin(XRot) * math.cos(YRot),
	math.sin(YRot),
	math.cos(XRot) * math.cos(YRot)
)
	
local Starter_Position	= Plane_Center.Position
local Next_Direction	= Starter_Position + Next_Angles	
	
local Current_CFrame	= CFrame.new(Starter_Position, Next_Direction)

local Sight_CFrame	= Current_CFrame * CFrame.new(0, 0, -SIGHT_DISTANCE) -- This is for the "custom" mouse position, and all it does in the end is to set a part position into this cframe position, so the ring moves.
local Sight_Position	= Sight_CFrame.Position
	
local Origin      = Starter_Position + Next_Angles
local Direction = Starter_Position

Camera.CFrame = CFrame.lookAt(Origin, Direction)

What it looks like at the moment: Watch camera | Streamable

If anybody knows how to fix it, it would be great, at the same time, I’d learn from it. Cameras isn’t the type of thing that I’m so “familiar” with, however, I never experienced something like this before.

2 Likes

What camera type are you using?

2 Likes

I’m using the scriptable camera type, so this way I could set the camera cframe and manipulate it (cause I’m thinking on first person view option also)

1 Like

Okay, so your problem is probably the CFrame.lookAt(Origin, Direction).

Ultimately b/c this always assumes the upVector is Vector3.new(0, 1, 0). You will need to change this so that the cframe doesn’t flip.

1 Like

Understood. What value would come closest to the reference video? Do you also know to tell me why this happens?

1 Like

The CFrame.lookAt function would work something like this:

local function lookAt(origin: Vector3, target: Vector3)
	local zVector = origin - target
	local xVector = Vector3.yAxis:Cross(zVector)
	local yVector = zVector:Cross(xVector)
	return CFrame.fromMatrix(origin, xVector, yVector, zVector)
end

part.CFrame = lookAt(part.Position, target.Position)

If we setup a scene where the origin - target end up parallel to Vector3.new(0, 1, 0) we’ll see the result flip. Understanding why is mostly just about understanding how the cross product and the right hand rule work.

To fix this we can change the code slightly to support an arbitrary up vector

local function lookAt(origin: Vector3, target: Vector3, up: Vector3)
	local zVector = origin - target
	local xVector = up:Cross(zVector)
	local yVector = zVector:Cross(xVector)
	return CFrame.fromMatrix(origin, xVector, yVector, zVector)
end

part.CFrame = lookAt(part.Position, target.Position, part.CFrame.YVector)

This time we pass in the part’s current up vector which in the context of micro adjustments being made every frame results in a smooth “somersault” as opposed to a sudden flip.

Lucky for you CFrame.lookAt already supports an additional third parameter for defining the up vector.

3 Likes

Thank you so much, brother! What an godly explanation, for real. Absolutely, thank you so much. I’m glad that today I learned something new!!

Now it works as I expected!! Watch Camera | Streamable

1 Like

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