i have made this nifty camera with quaternions, but im trying to lock the roll since the mouse doesnt really have a way to do that on purpose, i cannot figure this out of the life of me since the cframe is around 12 arg’s long and i dont know which ones need to be changed to 0 or how to even define them
External MediaCFrame.fromOrientation(math.rad(upDownInDegree), math.rad(leftRightInDegree), 0)
this would work for euler angles but i dont think it would work for quaternions
for reference my current cam cframe looks like this 53.556 0, 7.73523808, 12.0740738, 0.479156375, -0.194079429, 0.856003881, -0.0137755023, 0.97346431, 0.228421941, -0.87762171, -0.121241428, 0.463768244
you dont work directly with quaternions
because a basic quaternions is (q, x, y, z)
where q=cos(theta/2) and x,y,z forms the axis of rotation with magnitude sin(theta/2)
also you seem to print out the cframe directly, and it outputs 12 entries, it is because it is probably giving you the 4x3 matrix representation which includes the rotation and translation (position).
in either representations, you cannot just set an entry to zero to make the roll go away
well i dont have an updownindegree variable and i have no idea what i would set it to
you may post the code that move your camera. then we could see what changes can be made
A random question but why are you using quaternions for camera manipulation? I just find it an unnecessarily complicated way to achieve something so easy. And why do you have nested functions inside of a RenderStepped
loop?
steps.RenderStepped:Connect(function()
local mousemovement = (uis:GetMouseDelta() / 180) * 0.5
cam.CFrame = cam.CFrame * CFrame.fromOrientation(mousemovement.Y, mousemovement.X, 0)
end)
i very specfically need quaternions for this, radical euler angles are susceptible to gimbal lock and would ruin the gameplay of my game
no actual difference using those methods because they are quaternions/matrix underneath
but you can try
local quatr1 = multiply(quaternionX, quaternionY)
i suspect your current order of rotation is not correct
it is i already tried swapping these around
gimbal lock, a problem quaternions dont have
CFrames don’t have gimball lock, because CFrames work with Vectors as directions instead. Quaternions should be used if you have limited memory, like saving some data in DataStores.
by default the camera rotates by setting the cframe of the camera to a euler angle, euler angles have gimbal lock which causes problems with full rotations of the y axis, i need to use quaternions to alleviate this
Not sure if this is what your looking for but I’m assuming you want to include the roll when changing pitch and yaw
For example when your roll is 90 degrees and you move the mouse up the camera doesn’t go left/right and instead goes up and down from the players view
local Camera = workspace.CurrentCamera :: Camera
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local position = Vector3.zero :: Vector3
local yaw = 0 :: number
local pitch = 0 :: number
local roll = 0 :: number
local sensitivity = 0.25 :: number
RunService.RenderStepped:Connect(function ()
local delta = UserInputService:GetMouseDelta()
-- I commented out "% 360" cause sometimes you need more degrees to ensure smooth rotations
pitch = (pitch + delta.Y * sensitivity)-- % 360
yaw = (yaw + delta.X * sensitivity)-- % 360
roll = 0
local adjustedyaw = pitch * math.sin(math.rad(roll)) + yaw * math.cos(math.rad(roll))
local adjustedPitch = pitch * math.cos(math.rad(roll)) - yaw * math.sin(math.rad(roll))
local yawQuat = CFrame.new(position.X, position.Y, position.Z, 0, math.sin(math.rad(adjustedyaw) * 0.5), 0, math.cos(math.rad(adjustedyaw) * 0.5))
local pitchQuat = CFrame.new(position.X, position.Y, position.Z, math.sin(math.rad(adjustedPitch) * 0.5), 0, 0, math.cos(math.rad(adjustedPitch) * 0.5))
local rollQuat = CFrame.new(position.X, position.Y, position.Z, 0, 0, math.sin(math.rad(roll) * 0.5), math.cos(math.rad(roll) * 0.5))
local orientation = yawQuat * pitchQuat * rollQuat
Camera.CFrame = CFrame.fromMatrix(position, orientation.RightVector, orientation.UpVector, -orientation.LookVector)
end)
I think I am only using luau’s built-in quaternion methods but correct me if I’m wrong!
It feels super jarring though, it’s making me dizzy even though it works as it’s supposed to (may have some issues I’m not aware of). I have a more conventional one if you want it
Example with a 90 degree roll:
Here’s the other method that doesn’t take roll into account when moving the mouse:
local Camera = workspace.CurrentCamera
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local position = Vector3.zero :: Vector3
local yaw = 0 :: number
local pitch = 0 :: number
local roll = 0 :: number
local sensitivity = 0.25 :: number
RunService.RenderStepped:Connect(function ()
local delta = UserInputService:GetMouseDelta()
pitch = (pitch + delta.Y * sensitivity) % 360
yaw = (yaw + delta.X * sensitivity) % 360
local yawRad = math.rad(yaw)
local pitchRad = math.rad(pitch)
local yawQuat = CFrame.new(position.X, position.Y, position.Z, 0, math.sin(math.rad(yaw) * 0.5), 0, math.cos(math.rad(yaw) * 0.5))
local pitchQuat = CFrame.new(position.X, position.Y, position.Z, math.sin(math.rad(pitch) * 0.5), 0, 0, math.cos(math.rad(pitch) * 0.5))
local rollQuat = CFrame.new(position.X, position.Y, position.Z, 0, 0, math.sin(math.rad(roll) * 0.5), math.cos(math.rad(roll) * 0.5))
local orientation = yawQuat * pitchQuat * rollQuat
Camera.CFrame = CFrame.fromMatrix(position, orientation.RightVector, orientation.UpVector, -orientation.LookVector)
end)
You can omit roll entirely if you aren’t going to use it
Without roll
local Camera = workspace.CurrentCamera
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local position = Vector3.zero :: Vector3
local yaw = 0 :: number
local pitch = 0 :: number
local sensitivity = 0.25 :: number
RunService.RenderStepped:Connect(function ()
local delta = UserInputService:GetMouseDelta()
pitch = (pitch + delta.Y * sensitivity) % 360
yaw = (yaw + delta.X * sensitivity) % 360
local yawRad = math.rad(yaw)
local pitchRad = math.rad(pitch)
local yawQuat = CFrame.new(position.X, position.Y, position.Z, 0, math.sin(math.rad(yaw) * 0.5), 0, math.cos(math.rad(yaw) * 0.5))
local pitchQuat = CFrame.new(position.X, position.Y, position.Z, math.sin(math.rad(pitch) * 0.5), 0, 0, math.cos(math.rad(pitch) * 0.5))
local orientation = yawQuat * pitchQuat
Camera.CFrame = CFrame.fromMatrix(position, orientation.RightVector, orientation.UpVector, -orientation.LookVector)
end)
this doesnt work, most likely because it relies on the base camera instead of using scriptable camera, which means the camera module errors
For scriptable cameras you have to lock the mouse
I think because the camera module handles that? I’m not quite sure myself but if you don’t lock the mouse then UserInputService:GetMouseDelta() will return an empty vector
local Camera = workspace.CurrentCamera
Camera.CameraType = Enum.CameraType.Scriptable
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local position = Vector3.zero :: Vector3
local yaw = 0 :: number
local pitch = 0 :: number
local roll = 0 :: number
local sensitivity = 0.25
-- Locking the mouse
UserInputService.InputBegan:Connect(function(input: InputObject, gameProcessedEvent: boolean)
if gameProcessedEvent then return end
if input.UserInputType ~= Enum.UserInputType.MouseButton2 then return end
UserInputService.MouseBehavior = Enum.MouseBehavior.LockCurrentPosition
end)
UserInputService.InputEnded:Connect(function(input: InputObject, gameProcessedEvent: boolean)
if gameProcessedEvent then return end
if input.UserInputType ~= Enum.UserInputType.MouseButton2 then return end
UserInputService.MouseBehavior = Enum.MouseBehavior.Default
end)
RunService.RenderStepped:Connect(function ()
local delta = UserInputService:GetMouseDelta()
pitch = (pitch + delta.Y * sensitivity) % 360
yaw = (yaw + delta.X * sensitivity) % 360
local yawRad = math.rad(yaw)
local pitchRad = math.rad(pitch)
local yawQuat = CFrame.new(position.X, position.Y, position.Z, 0, math.sin(math.rad(yaw) * 0.5), 0, math.cos(math.rad(yaw) * 0.5))
local pitchQuat = CFrame.new(position.X, position.Y, position.Z, math.sin(math.rad(pitch) * 0.5), 0, 0, math.cos(math.rad(pitch) * 0.5))
local rollQuat = CFrame.new(position.X, position.Y, position.Z, 0, 0, math.sin(math.rad(roll) * 0.5), math.cos(math.rad(roll) * 0.5))
local orientation = yawQuat * pitchQuat * rollQuat
Camera.CFrame = CFrame.fromMatrix(position, orientation.RightVector, orientation.UpVector, -orientation.LookVector)
print(delta)
end)
You can also use one of my previous solutions if spherical coordinates suits your fancy