Skip to main content

Input & Camera

Two essential systems: reading player input (keys, mouse, clicks) and controlling or reading the camera. These power keybind systems, aim features, targeting, and camera manipulation.

UserInputService

local UIS = game:GetService("UserInputService")

-- Key pressed
UIS.InputBegan:Connect(function(input, gameProcessed)
if gameProcessed then return end -- skip if typing in chat/textbox

if input.KeyCode == Enum.KeyCode.X then
print("X pressed")
end

-- Mouse buttons
if input.UserInputType == Enum.UserInputType.MouseButton1 then
print("Left click")
end
if input.UserInputType == Enum.UserInputType.MouseButton2 then
print("Right click")
end
end)

-- Key released
UIS.InputEnded:Connect(function(input, gameProcessed)
if input.KeyCode == Enum.KeyCode.LeftShift then
print("Shift released")
end
end)

-- Mouse/joystick movement
UIS.InputChanged:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
print("Mouse moved:", input.Delta.X, input.Delta.Y)
end
end)

-- Check if a key is currently held (use in loops)
if UIS:IsKeyDown(Enum.KeyCode.LeftControl) then
-- Ctrl is being held
end

The Mouse Object

The mouse gives you 3D world information about where you're pointing:

local mouse = game.Players.LocalPlayer:GetMouse()

-- Where the mouse ray hits in the world
print(mouse.Hit) -- CFrame: position + orientation of hit surface
print(mouse.Hit.Position) -- Vector3: just the position

-- What instance the mouse is over
print(mouse.Target) -- Instance (BasePart) or nil

-- Screen pixel position
print(mouse.X, mouse.Y)

-- Unit ray from camera through mouse (for raycasting)
local ray = mouse.UnitRay
-- ray.Origin = camera position
-- ray.Direction = normalized direction toward mouse

Raycasting

For precise targeting — finding what's at a screen position or in a direction:

-- Raycast from mouse position into the world
local function getMouseTarget(maxDistance, ignoreList)
local mouse = game.Players.LocalPlayer:GetMouse()
local cam = workspace.CurrentCamera

local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = ignoreList or {Players.LocalPlayer.Character}

local result = workspace:Raycast(
mouse.UnitRay.Origin,
mouse.UnitRay.Direction * maxDistance,
params
)

return result -- result.Instance, result.Position, result.Normal
end

-- Usage
local hit = getMouseTarget(500)
if hit then
print("Hit:", hit.Instance:GetFullName(), "at", hit.Position)
end

Raycast from any origin:

-- Raycast forward from character
local hrp = char.HumanoidRootPart
local params = RaycastParams.new()
params.FilterDescendantsInstances = {char}

local result = workspace:Raycast(hrp.Position, hrp.CFrame.LookVector * 100, params)
if result then
print("Something is 100 studs ahead:", result.Instance.Name)
end

Keybind System

A clean pattern for managing multiple keybinds:

local binds = {
[Enum.KeyCode.Q] = function() print("Q action") end,
[Enum.KeyCode.E] = function() print("E action") end,
[Enum.KeyCode.F] = function() print("F action") end,
}

UIS.InputBegan:Connect(function(input, processed)
if processed then return end
local action = binds[input.KeyCode]
if action then action() end
end)

Toggle Keybind

local enabled = false

UIS.InputBegan:Connect(function(input, processed)
if processed then return end
if input.KeyCode == Enum.KeyCode.T then
enabled = not enabled
print("Feature:", enabled and "ON" or "OFF")
end
end)

The Camera

local cam = workspace.CurrentCamera

-- Read camera position
print(cam.CFrame.Position) -- where the camera is
print(cam.CFrame.LookVector) -- direction the camera faces

-- Change FOV
cam.FieldOfView = 70 -- default is 70; lower = zoomed in

-- Camera types
cam.CameraType = Enum.CameraType.Custom -- normal follow camera
cam.CameraType = Enum.CameraType.Scriptable -- full manual control (your script drives it)

Scriptable Camera

Setting camera to Scriptable gives you full control:

cam.CameraType = Enum.CameraType.Scriptable

-- Now move it manually
cam.CFrame = CFrame.new(0, 50, 0) * CFrame.Angles(math.rad(-90), 0, 0) -- looking down

-- Animate it
game:GetService("RunService").RenderStepped:Connect(function()
-- Keep camera above player
local hrp = Players.LocalPlayer.Character and
Players.LocalPlayer.Character:FindFirstChild("HumanoidRootPart")
if hrp then
local pos = hrp.Position + Vector3.new(0, 20, -10)
cam.CFrame = CFrame.lookAt(pos, hrp.Position)
end
end)

Reset camera to normal:

cam.CameraType = Enum.CameraType.Custom

World ↔ Screen Conversions

local cam = workspace.CurrentCamera

-- World position → screen position
local screenPos, onScreen = cam:WorldToViewportPoint(worldPos)
-- screenPos.X, screenPos.Y = pixel coordinates
-- screenPos.Z = distance from camera (negative = behind camera)
-- onScreen = true if within viewport bounds

-- Screen position → world ray (for clicking at screen positions)
local ray = cam:ViewportPointToRay(screenPos.X, screenPos.Y)
-- ray.Origin, ray.Direction

Mouse Locking and Sensitivity

Some scripts need to lock or control the mouse:

-- Lock mouse to center of screen
UIS.MouseBehavior = Enum.MouseBehavior.LockCenter

-- Free mouse
UIS.MouseBehavior = Enum.MouseBehavior.Default

-- Get raw mouse delta (works with locked mouse)
UIS.InputChanged:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
local delta = UIS:GetMouseDelta() -- more accurate than input.Delta
print(delta.X, delta.Y)
end
end)

Click Detection on Screen Elements

-- Detect left click anywhere on screen
UIS.InputBegan:Connect(function(input, processed)
if processed then return end -- don't fire if clicking a GUI element
if input.UserInputType == Enum.UserInputType.MouseButton1 then
-- Get the 3D position clicked
local hit = getMouseTarget(500)
if hit then
-- Do something with what was clicked
end
end
end)