JumpButton - Detect and buffer jump inputs accurately

JumpButton

A simple library for detecting when the local player is holding the jump button in Roblox, with support for buffered inputs.

Questions? Feel free to ask here.

Why JumpButton?

Roblox does not provide a good API method to listen for the jump button being held. Popular (but flawed) methods include:

  • Listening for spacebar presses
    • This doesn’t support mobile or gamepad devices
  • UserInputService.JumpRequest
    • This fires several times after a jump, leading to the implementation of a debounce, which can be infuriating when rapidly pressing jump
    • The amount of time it fires afterwards is random, leading to inconsistent “jump buffering”

JumpButton instead uses the Humanoid.Jump property. This is a viable option because it is what the ControlModule sets every frame. JumpButton reads this property every frame right after the ControlModule runs and checks if the property has changed since the last frame.

Note: JumpButton does not listen for changes with an event, as Humanoid.Jump rapidly changes when the user jumps, which would ruin the library.

Downsides

JumpButton has a few minor downsides:

  • It only works when a humanoid exists. When no humanoid exists, JumpButton will remain in the state it last was.
  • It will not detect inputs where the player pressed and released the jump button in the same frame.
    • UserInputService.JumpRequest has the same issue, so this likely isn’t too big of a problem.

Installation

Note: JumpButton can only run on the client

In studio:

Insert the model into some place accessible to the client.

External Editor:

Download the JumpButton.luau file from the latest release and insert it into your project in a place accessible to the client.

Wally:

Add the following line to your dependencies:

JumpButton = "funwolf7/jumpbutton@1.0.0"

Once installed, regardless of method, simply require the ModuleScript, and you now have access to the functions it provides.

Usage

Basic usage

If you do not wish to use buffering, using the library is simple. You will only need a few functions.

  • JumpButton.OnPress is an RBXScriptSignal that you can Connect to which will fire whenever the jump button is pressed.
  • JumpButton.OnRelease is an RBXScriptSignal that you can Connect to which will fire whenever the jump button is released.
  • JumpButton.isPressed() can tell you if the jump button is currently being held.

Advanced usage (buffering)

When using buffering, it gets a bit more complicated.

Every time the jump button is pressed, JumpButton stores the timestamp (using os.clock), at which it was last pressed. This timestamp is set to nil when the jump button is released or when the buffer is killed (more on killing the buffer below).

When an action that uses the jump button gets enabled, you should retrieve the buffer start using JumpButton.useBufferStart(), which returns the stored timestamp. (Note: only call this function once per action, and store the value if needed, as it will return nil the second time it is called. More information on why this occurs in the “Killing the buffer” section.)

If there is an unlimited buffer window (the button press can happen an unlimited amount of time before the action), then simply checking if the timestamp exists is enough. If there is a limit to the amount of time after the press, you must check first that it exists, and also that less time has passed since the buffer than the buffer duration.

local BufferDuration = 0.1

local currentBufferStart = JumpButton.useBufferStart()
if currentBufferStart and os.clock() - currentBufferStart <= BufferDuration then
	print("buffered, immediately perform the action")
else
	print("not buffered, wait for a jump press to perform the action")
end

If the check passes, then you should immediately perform whatever action was just enabled.

When using JumpButton, you may find yourself using the same time window for most actions. If this is the case, then you can use JumpButton.setDefaultBufferDuration(newBufferDuration) to set a default duration, and then use JumpButton.useIsBuffered(), which will automatically perform the time check based on the default buffer duration that has been set, returning true if it passes and false if not. If the default buffer duration is set to nil, then it will only check if the buffer start exists, allowing for an unlimited buffer duration.

Killing the buffer:

An important idea with buffering is that one press of the button should only trigger one action. To allow for this, JumpButton implements ways to “kill” the buffer. This simply sets the buffer timestamp to nil, making it so that further checks of the buffer start will not cause more actions. You can manually kill the buffer by calling JumpButton.killBuffer().

In order to make it easier for developers, all use functions (JumpButton.useBufferStart and JumpButton.useIsBuffered) automatically kill the buffer when called. The idea behind this is that if an action is checking the buffer, then most likely it will perform an action if the buffer timestamp exists, thus the buffer should be killed. If you wish to check the buffer timestamp without killing it, you can use peek functions (JumpButton.peekBufferStart() and JumpButton.peekIsBuffered()), which return the same things as their counterparts but without killing the buffer.

This also has the side effect that the use functions will return nil the second time if called twice in a row. If you need to use the result of these functions in multiple places, you should store the result as a variable and use the variable instead.

Automatic buffer killing:

The humanoid jumping is an action triggered by the jump button, so JumpButton automatically listens for Humanoid.Jumping and kills the buffer whenever it fires. If this is not desireable, you can call JumpButton.setAutoKillOnJump(false) to disable this feature.

API

View on GitHub

19 Likes

I’ve been waiting for something like this. This will make coding mobile support much more easier.

Thank you! :smile:

2 Likes