For a while now, I’ve been stuck attempting to create a camera script.
I’ve made it go sideways, but whenever I try to make it go up and down, the Z axis also changes. How can i only make it go up and down, whilst having constant Y axis changes?
local Camera = workspace:WaitForChild("Camera")
local VX = 0
local VZ = 0
local LastPos = Vector2.new()
local MousePos = Vector2.new()
local MoveSpeedX = MousePos.X - LastPos.X
local MoveSpeedY = MousePos.Y - LastPos.Y
local Cursor = game.Players.LocalPlayer:GetMouse()
local CX = 0
local CY = 0
local function ClearChar(Char)
for i, obj in pairs(Char:GetDescendants()) do
if (obj:IsA("BasePart") or obj:IsA("MeshPart")) and not obj.Parent:IsA("Accessory") then
obj.LocalTransparencyModifier = 0
end
end
end
--[[game.Players.LocalPlayer.CharacterAdded:Connect(function(Char)
ClearChar(Char)
end)]]
game.Players.LocalPlayer.CharacterAppearanceLoaded:Connect(function(Char)
ClearChar(Char)
end)
game["Run Service"].PreRender:Connect(function()
ClearChar(game.Players.LocalPlayer.Character)
--Camera.CameraType = Enum.CameraType.Scriptable
MousePos = Vector2.new(((Cursor.X - (Cursor.ViewSizeX / 2)) / Cursor.ViewSizeX), ((Cursor.Y - (Cursor.ViewSizeY / 2)) / Cursor.ViewSizeY))
MoveSpeedX = game.UserInputService:GetMouseDelta().X / 300
MoveSpeedY = game.UserInputService:GetMouseDelta().Y / 500
VX += MoveSpeedX * 0.3
VX /= 1.05
if VX > 5 then
VX = 5
end
if VX < -5 then
VX = -5
end
VZ -= MoveSpeedY * 0.3
VZ /= 1.1
if VZ > 5 then
VZ = 5
end
if VZ < -5 then
VZ = -5
end
CX -= (VX /3)
CY -= (VZ /3)
if CY > 1 then
CY = 1
end
if CY < -0.85 then
CY = -0.85
end
if game.Players.LocalPlayer.Character then
local CameraV = game.Players.LocalPlayer.Character:FindFirstChildOfClass("Humanoid").RootPart.Position + game.Players.LocalPlayer.Character:FindFirstChildOfClass("Humanoid").CameraOffset + Vector3.new(0, 1.5, 0)
Camera.CFrame = CFrame.Angles(CY, CX, 0) + CameraV
print(workspace.CurrentCamera.CFrame)
end
LastPos = Vector2.new(((Cursor.X - (Cursor.ViewSizeX / 2)) / Cursor.ViewSizeX), ((Cursor.Y - (Cursor.ViewSizeY / 2)) / Cursor.ViewSizeY))
end)
I tested your script and I see what you mean. I believe I have found the sollution. Basically you want to rotate around the Y axis for horizontal movement, and rotate around the X axis for vertical movement but apply them in the correct world-relative order.
This is best done with CFrame.lookAt
local Camera = workspace:WaitForChild("Camera")
Camera.CameraType = Enum.CameraType.Scriptable
local VX = 0
local VZ = 0
local LastPos = Vector2.new()
local MousePos = Vector2.new()
local MoveSpeedX = 0
local MoveSpeedY = 0
local Cursor = game.Players.LocalPlayer:GetMouse()
local CX = 0
local CY = 0
local function ClearChar(Char)
for i, obj in pairs(Char:GetDescendants()) do
if (obj:IsA("BasePart") or obj:IsA("MeshPart")) and not obj.Parent:IsA("Accessory") then
obj.LocalTransparencyModifier = 0
end
end
end
game.Players.LocalPlayer.CharacterAppearanceLoaded:Connect(function(Char)
ClearChar(Char)
end)
game:GetService("RunService").PreRender:Connect(function()
ClearChar(game.Players.LocalPlayer.Character)
MousePos = Vector2.new(((Cursor.X - (Cursor.ViewSizeX / 2)) / Cursor.ViewSizeX), ((Cursor.Y - (Cursor.ViewSizeY / 2)) / Cursor.ViewSizeY))
MoveSpeedX = game.UserInputService:GetMouseDelta().X / 300
MoveSpeedY = game.UserInputService:GetMouseDelta().Y / 500
VX += MoveSpeedX * 0.3
VX /= 1.05
VX = math.clamp(VX, -5, 5)
VZ -= MoveSpeedY * 0.3
VZ /= 1.1
VZ = math.clamp(VZ, -5, 5)
CX -= (VX / 3)
CY -= (VZ / 3)
CY = math.clamp(CY, -0.85, 1)
local character = game.Players.LocalPlayer.Character
if character then
local humanoid = character:FindFirstChildOfClass("Humanoid")
if humanoid then
local hrp = humanoid.RootPart
local camOffset = humanoid.CameraOffset
local basePos = hrp.Position + camOffset + Vector3.new(0, 1.5, 0)
-- Use proper rotation to avoid Z tilting
local direction = CFrame.fromEulerAnglesYXZ(CY, CX, 0).LookVector
Camera.CFrame = CFrame.lookAt(basePos, basePos + direction)
end
end
LastPos = Vector2.new(((Cursor.X - (Cursor.ViewSizeX / 2)) / Cursor.ViewSizeX), ((Cursor.Y - (Cursor.ViewSizeY / 2)) / Cursor.ViewSizeY))
end)
CFrame.fromEulerAnglesYXZ(pitch, yaw, 0) rotates first on X (pitch), then Y (yaw), avoiding the Z-axis roll.
Camera.CFrame = CFrame.lookAt(origin, origin + direction) keeps camera upright and looking correctly.
math.clamp(pitch, -80°, 80°) prevents over-rotation (you can change it if you want)