Skip to content
Back to skills

Roblox Input

ASecurity

Player input and cameras across keyboard/mouse, touch, gamepad, VR - Input Action System (InputContext, InputAction, InputBinding), UserInputService (PreferredInput, gameProcessedEvent, mouse lock), ContextActionService, reserved keys, custom cameras with BindToRenderStep. Use when adding controls, keybinds, abilities, vehicles, on-screen buttons, or custom cameras.

  • 5 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 29, 2026
ai-agentsgoreact

Works with

  • cli

Security analysis

A100/100

Scanned September 29, 2026

npx -y skills add EL4CTEO/roblox-skills --skill roblox-input --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Roblox Input?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Roblox Input
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/el4cteo-roblox-input/badge)](https://www.skillsdirectory.com/skills/el4cteo-roblox-input)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: roblox-input
description: Player input and cameras across keyboard/mouse, touch, gamepad, VR - Input Action System (InputContext, InputAction, InputBinding), UserInputService (PreferredInput, gameProcessedEvent, mouse lock), ContextActionService, reserved keys, custom cameras with BindToRenderStep. Use when adding controls, keybinds, abilities, vehicles, on-screen buttons, or custom cameras.
---

# Roblox input and camera

## Choose an approach

| Approach | Use when |
| --- | --- |
| **Input Action System** (`InputContext` → `InputAction` → `InputBinding`) | New gameplay controls. Cross-device bindings configured as instances, contexts you enable/disable, built-in on-screen button binding, prompt display. Recommended. |
| `UserInputService` events | Raw input, text-free hotkeys, mouse position/delta, touch gestures, device detection. |
| `ContextActionService:BindAction` | Legacy/simple bind-with-mobile-button; existing code. |

All input handling is **client-side**. Send *intent* to the server via remotes; the server validates.

## Input Action System

Structure (typically in `ReplicatedStorage.Inputs`, or created from a client script):

```text
Inputs (Folder)
  PlayContext (InputContext, Priority 1000)      -- gameplay controls
    Sprint (InputAction, Type = Bool)
      Keyboard (InputBinding, KeyCode = LeftShift)
      Gamepad  (InputBinding, KeyCode = ButtonL3)
      Touch    (InputBinding, UIButton = <on-screen TextButton>)
  MenuContext (InputContext, Priority 2000, Sink = true, Enabled = false)
```

- `InputAction.Type`: `Bool` (press/release), `Direction1D` (triggers, zoom), `Direction2D` (move, camera),
  `Direction3D` (flying), `ViewportPosition` (pointer position).
- Events: `Pressed`, `Released` (Bool only), `StateChanged(value)` for every type. `action:GetState()` reads the current value.
- `InputBinding` fields: `KeyCode`; composite directions `Up/Down/Left/Right/Forward/Backward`; `Scale`
  (e.g. `0.01` for `MouseDelta`/`TouchDelta`); `PressedThreshold`/`ReleasedThreshold` for analog triggers;
  `PrimaryModifier`/`SecondaryModifier` for chords (Ctrl+S); `UIButton` to drive the action from a GUI button;
  `DisplayName`/`DisplayImage` for prompts.
- `InputContext.Enabled` switches whole control schemes (gameplay ↔ menu ↔ vehicle); `Priority` + `Sink`
  lets a higher context consume keys so lower contexts don't also fire.
- Set `Workspace.PlayerScriptsUseInputActionSystem = Enabled` to have Roblox's default player/camera
  scripts use the system too (required for server authority).

```luau
--!strict
-- Client: build a Sprint action in code and react to it.
local Players = game:GetService("Players")

local player = Players.LocalPlayer

local context = Instance.new("InputContext")
context.Name = "PlayContext"
context.Priority = 1000

local sprint = Instance.new("InputAction")
sprint.Name = "Sprint"
sprint.Type = Enum.InputActionType.Bool
sprint.Parent = context

local keyboard = Instance.new("InputBinding")
keyboard.KeyCode = Enum.KeyCode.LeftShift
keyboard.Parent = sprint

local gamepad = Instance.new("InputBinding")
gamepad.KeyCode = Enum.KeyCode.ButtonL3
gamepad.Parent = sprint

context.Parent = player:WaitForChild("PlayerScripts")

local function setSprinting(on: boolean)
	local character = player.Character
	local humanoid = character and character:FindFirstChildOfClass("Humanoid")
	if humanoid then
		humanoid.WalkSpeed = if on then 24 else 16 -- server should validate/own real speed limits
	end
end

sprint.Pressed:Connect(function()
	setSprinting(true)
end)
sprint.Released:Connect(function()
	setSprinting(false)
end)
```

Show the right key/button: an `InputActionLabel` (beta) auto-renders the binding for the current device,
or read `action.PreferredBinding` (+ its `DisplayName`/`KeyCode`) and `UserInputService:GetStringForKeyCode`
/ `GetImageForKeyCode`, updating on `GetPropertyChangedSignal("PreferredBinding")`.

## UserInputService essentials

```luau
--!strict
local UserInputService = game:GetService("UserInputService")

UserInputService.InputBegan:Connect(function(input: InputObject, gameProcessedEvent: boolean)
	if gameProcessedEvent then
		return -- typing in a TextBox, clicking UI, or a core-script action consumed it
	end
	if input.KeyCode == Enum.KeyCode.E then
		print("interact")
	elseif input.UserInputType == Enum.UserInputType.MouseButton1 then
		print("click at", input.Position)
	end
end)

local function onPreferredInputChanged()
	local preferred = UserInputService.PreferredInput -- KeyboardAndMouse | Touch | Gamepad
	print("show prompts for", preferred)
end
UserInputService:GetPropertyChangedSignal("PreferredInput"):Connect(onPreferredInputChanged)
onPreferredInputChanged()
```

- **Always honor `gameProcessedEvent`** or players trigger abilities while typing in chat.
- `PreferredInput` reflects what the player is actually using (phone + Bluetooth gamepad → `Gamepad`);
  don't branch on `TouchEnabled`/`KeyboardEnabled` alone.
- Mouse: `UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter` for FPS (set every frame while
  in first person if other scripts may reset it), `GetMouseLocation()` (includes top-bar inset),
  `MouseDeltaSensitivity`, `MouseIconEnabled`.
- Gamepad: `GetConnectedGamepads`, `GamepadConnected/Disconnected`, thumbstick `input.Position` in
  `InputChanged` (apply a dead zone ≈ 0.15). Haptics: `HapticService` / `HapticEffect`.
- Touch: `TouchTap`, `TouchLongPress`, `TouchPinch`, `TouchSwipe`; hide default controls only if you
  replace them (`GuiService.TouchControlsEnabled`).
- Reserved keys you can't override: Esc/Start (menu), F9 (console), F11, F12, PrintScreen; chat `/`,
  Tab (player list), backpack and tool-number keys unless you disable those core features.

## ContextActionService (legacy-compatible)

`ContextActionService:BindAction(name, handler, createTouchButton, ...inputs)` with handler
`(actionName, inputState, inputObject) -> Enum.ContextActionResult?`; return `Pass` to let other
bindings see the input. Use `BindActionAtPriority` for ordering and `UnbindAction` when the context
ends (e.g. tool unequipped). Prefer the Input Action System for new work.

## Custom cameras

```luau
--!strict
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

local camera = workspace.CurrentCamera
local OFFSET = Vector3.new(0, 30, 20)

camera.CameraType = Enum.CameraType.Scriptable

RunService:BindToRenderStep("TopDownCamera", Enum.RenderPriority.Camera.Value + 1, function()
	local character = Players.LocalPlayer.Character
	local root = character and character:FindFirstChild("HumanoidRootPart") :: BasePart?
	if root then
		local target = root.Position
		camera.CFrame = CFrame.lookAt(target + OFFSET, target)
	end
end)

-- Restore: RunService:UnbindFromRenderStep("TopDownCamera"); camera.CameraType = Enum.CameraType.Custom
```

- Update cameras in `BindToRenderStep` (runs before render, ordered), not `Heartbeat`/`task.wait` loops.
- Smooth with `camera.CFrame:Lerp(goal, 1 - math.exp(-speed * deltaTime))` (frame-rate independent).
- First person: `player.CameraMode = Enum.CameraMode.LockFirstPerson`; zoom limits with
  `player.CameraMinZoomDistance`/`CameraMaxZoomDistance`. Shake: offset the CFrame, don't move parts.
- Screen → world: `camera:ViewportPointToRay(x, y)` / `ScreenPointToRay` + `workspace:Raycast`.

## Cross-platform checklist

- Every action has keyboard, gamepad, **and** touch bindings (touch via `UIButton` or on-screen buttons).
- Prompts/hints update when `PreferredInput` changes. Console UI is navigable with D-pad and closes with B.
- No hover-only interactions (touch has no hover). Hold-to-confirm for destructive actions.
- Test in Studio's Device Emulator and Controller Emulator.

## Related skills

`roblox-ui` (buttons, gamepad selection), `roblox-characters-animation` (movement, abilities),
`roblox-networking` (sending intents), `roblox-physics` (vehicles).

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.