The way do disable all input (under Action priority) | ContextActionService

Let’s imagine that you are making a settings menu or some kind of save GUI and you want to prevent any input coming through, e.g. Inventory, Camera, Character Input. Here the simple solution to that will sink all the inputs with ContextActionService.

That is the test place, press P to stop / start all input
SinkAllImput.rbxl (226.5 KB)

How does that work it takes all possible keycodes from
Enum.KeyCode, Enum.UserInputType, Enum.PlayerActions
And binds them to one action with the generated name(HttpService.GenerateGUID()) and sinks every input with Enum.ContextActionResult.Sink

That was discovered pretty accidentaly when i was researching ContextActionService

TS

import { ContextActionService, HttpService } from "@rbxts/services";

/**all inputable keycodes */
//this requires a little bit more explanation
//it's unpacking tables and combining them together
//e.g {1, 1, 1} + {2, 2, 2} = {1, 1, 1, 2, 2, 2}
const keycodes = [...Enum.KeyCode.GetEnumItems(), ...Enum.UserInputType.GetEnumItems(), ...Enum.PlayerActions.GetEnumItems()];

function SinkKey() {
  return Enum.ContextActionResult.Sink;
}

export class InputCatcher {
  /**priority at which context action service is bound */
  private priority_: number;
  /**unique id of the bind */
  private uuid_ = HttpService.GenerateGUID();
  constructor(priority: number) {
    this.priority_ = priority;
  }

  /**binds the action that sinks all the keys */
  GrabInput() {
    ContextActionService.BindActionAtPriority(this.uuid_, SinkKey, false, this.priority_, ...keycodes)
  }

  /**unbinds action that sinks all the keys */
  ReleaseInput() {
    ContextActionService.UnbindAction(this.uuid_);
  }
}

My appology, compiled code looks unreadable
Luau

-- Compiled with roblox-ts v2.3.0
local ContextActionService = game:GetService("ContextActionService")
local HttpService = game:GetService("HttpService")
--*all inputable keycodes 
local _array = {}
local _length = #_array
local _array_1 = Enum.KeyCode:GetEnumItems()
local _Length = #_array_1
table.move(_array_1, 1, _Length, _length + 1, _array)
_length += _Length
local _array_2 = Enum.UserInputType:GetEnumItems()
local _Length_1 = #_array_2
table.move(_array_2, 1, _Length_1, _length + 1, _array)
_length += _Length_1
local _array_3 = Enum.PlayerActions:GetEnumItems()
table.move(_array_3, 1, #_array_3, _length + 1, _array)
local keycodes = _array
local function SinkKey()
	return Enum.ContextActionResult.Sink
end
local InputCatcher
do
	InputCatcher = setmetatable({}, {
		__tostring = function()
			return "InputCatcher"
		end,
	})
	InputCatcher.__index = InputCatcher
	function InputCatcher.new(...)
		local self = setmetatable({}, InputCatcher)
		return self:constructor(...) or self
	end
	function InputCatcher:constructor(priority)
		self.uuid_ = HttpService:GenerateGUID()
		self.priority_ = priority
	end
	function InputCatcher:GrabInput()
		ContextActionService:BindActionAtPriority(self.uuid_, SinkKey, false, self.priority_, unpack(keycodes))
	end
	function InputCatcher:ReleaseInput()
		ContextActionService:UnbindAction(self.uuid_)
	end
end
return {
	InputCatcher = InputCatcher,
}

Test code: TS

import { ContextActionService } from "@rbxts/services";
import { InputCatcher } from "shared/input/input_catcher";


const test_priority = 10000;
let input_enabled = false
const input_catcher = new InputCatcher(test_priority - 1);
function ToggleInput(action_name: String, state: Enum.UserInputState, input: InputObject) {
  //filters else besides begin state
  if (state !== Enum.UserInputState.Begin) return;
  input_enabled = !input_enabled;
  if (input_enabled) {
    input_catcher.GrabInput();
    return;
  }
  input_catcher.ReleaseInput();
}

ContextActionService.BindActionAtPriority("Test", ToggleInput, false, test_priority, Enum.KeyCode.P);

Test Code Luau:


local ContextActionService = game:GetService("ContextActionService")
local InputCatcher = require( --[[you require the module]]).InputCatcher 

local test_priority = 10000
local input_enabled = false
local input_catcher = InputCatcher.new(test_priority - 1)
local function ToggleInput(action_name, state, input)
	--filters else besides begin state
	if state ~= Enum.UserInputState.Begin then
		return nil
	end
	input_enabled = not input_enabled
	if input_enabled then
		input_catcher:GrabInput()
		return nil
	end
	input_catcher:ReleaseInput()
end
ContextActionService:BindActionAtPriority("Test", ToggleInput, false, test_priority, Enum.KeyCode.P)
2 Likes

This is just so much extra from the aged response of:

local ContextActionService = game:GetService("ContextActionService")
local FREEZE_ACTION = "freezeMovement"

ContextActionService:BindAction(
    FREEZE_ACTION,
    function() return Enum.ContextActionResult.Sink end,
    false,
    unpack(Enum.PlayerActions:GetEnumItems())
)

--to unfreeze
ContextActionService:UnbindAction(FREEZE_ACTION)

Only difference? Style.

3 Likes

you’re right, it’s a little bit expanded solution. I like the Godot style of handling input, you can easily catch unwanted input and use only what you need

1 Like