Trouble understanding Enum.UserInputState

I was following along Roblox’s documentation about ContextActionService and I reached the section which tells you about :BindActionAtPriority. And as an example they gave me this code:

local ContextActionService = game:GetService("ContextActionService")

local First_Input_Key = Enum.KeyCode.X
local Second_Input_Key = Enum.KeyCode.Z

local function handleThrow(_, inputState, _)
	if inputState ~= Enum.UserInputState.Begin then
		return Enum.ContextActionResult.Pass
	end
	
	print("Throw")
	return Enum.ContextActionResult.Sink
end

local function handlePunch(_, inputState, _)
	if inputState ~= Enum.UserInputState.Begin then
		return Enum.ContextActionResult.Pass
	end
	
	print("Punch")
	return Enum.ContextActionResult.Sink
end

ContextActionService:BindAction("HandleThrow", handleThrow, false, First_Input_Key)
ContextActionService:BindAction("HandlePunch", handlePunch, false, First_Input_Key)

ContextActionService:BindActionAtPriority("PrHandleThrow", handleThrow, false, 2, Second_Input_Key)
ContextActionService:BindActionAtPriority("PrHandlePunch", handlePunch, false, 1, Second_Input_Key)

I understood very well what :BindActionAtPriority was aiming, but the thing that I couldn’t get how it worked was this:

ContextActionService:BindAction("HandleThrow", handleThrow, false, First_Input_Key)
ContextActionService:BindAction("HandlePunch", handlePunch, false, First_Input_Key)

I understood everything until the point where I press the first_input_key where it redirects me to the first function handleThrow(). Where I would get stuck not understanding the meaning of this if statement:

if inputState ~= Enum.UserInputState.Begin then

Like in every code about ContextActionService in an if statement I never seen this condition. Like inputState NOT TO BE EQUAL(~=) with Enum.UserInputState.Begin. In this kind of context I have only seen TO BE EQUAL(==). This is the point where I couldn’t understand how this works further.

I would really appreciate if someone could specify what is really happening here.

1 Like

In this situation, there are two handlers for the same key. If the handler that is called first returns Enum.ContextActionResult.Sink, no more handlers will be called. If it returns Enum.ContextActionResult.Pass, the handler will next in the queue will be called.

In the case of the first two bindings (bindings to First_Input_Key), when the key is pressed down, the punch handler is called. Because it returns Enum.ContextActionResult.Sink, the throw handler isn’t called. When the key is released, the punch handler is called again, but now it returns Enum.ContextActionResult.Sink and thus ThrowHandler won’t be called.

1 Like

But why is PunchHandler called first? Isn’t ThrowHandler above it?

By default, the most recently bound (in other words last) handler is called first.

1 Like

Ok, I got it, but I have one more question, how does Enum.UserInputState work?

It depends on input type. When you press a key or a mouse button, the userinputstate given to the handlers is Enum.UserInputState.Begin. When you release the key or button, the handlers are given Enum.UserInputState.End. In the case of mouse movement (Enum.UserInputType.MouseMovement), the handler is given Enum.UserInputState.Change. When you press a key to which you have bound a handler and unbind it or bind another handler to the same key before releasing the key, the first bound handler will be given Enum.UserInputState.Cancel at the moment of unbinding or binding another handler if I understood the documentation correctly. For more information, you can read the documentation for Enum.UserInputState.

1 Like

Basically, the condition will pass and the code inside of the if-statement will run when the input has not just started. This is a little confusing in isolation, but if we look at the other potential Input States that UserInputState keeps track of,

, we see that only two are relevant for us, Change and End. So, the code inside the if-statement will run if the input has changed or if it has ended, but never when it has begun.

As an example, pressing a key will set it’s state to Begin, then releasing it will set it’s state to End. Change is a special case when, you guessed it, the input has changed somehow, without is actually ending. In practice, this only applies joy-sticks and scrolling a mouse wheel. If you scroll a mouse wheel, it’s input state will change in this order:

Started scrolling:

  • Begin

Keep scrolling:

  • Change

Stop scrolling:

  • End

The change in the input will be via the Input.Delta Vector3 value. Scrolling the mouse, for instance, will change the Z component (Input.Delta.Z) by how much the mouse is scrolled before the input finishes.

I hope this helps, but it is a little complicated. Ideally, you should hook up a few prints and test out some button presses and mouse wheel scrolls to figure out exactly what’s going on inside these values.

1 Like

You have explained very well and made understand your point. But I really wanna know how can we change the InputState to Change by using the keys. If you are not busy I would really appreciate if you would mention one of these “special” cases.

1 Like

A keypress should never result in a Enum.UserInputState.Change state event. Keys are either pressed or they aren’t, on a frame-by-frame basis. The “Change” state only applies for input that has more possible per-frame states.

As given in phoenix’s example: on the frame you start scrolling, a “Begin” state event is triggered. For each subsequent frame where you haven’t stopped scrolling, a “Change” state event is triggered, and finally the “End” state event triggers once you’ve stopped scrolling. The reason this is necessary is because the amount you’ve scrolled by can change over the course of several frames, but is still technically only a single “scroll” input.

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.