Reforged Gamepad
While Listeners may be quite versatile, they often aren't the prettiest and most concise, especially for TeleOps, where you may have many controls mapped to many buttons.
This is where the Reforged Gamepad come in! It turns your TeleOp from this:
- Java
- Kotlin
boolean gamepad1AWasPreviouslyTrue = false; // Mutable state
boolean gamepad2BWasPreviouslyTrue = false; // Gamepad1 vs 2, have to be paying attention!
@Override
public void loop() {
// Can't tell if this is the driver or not at first glance
// Also ugly to read and takes a second to mentally parse
if (gamepad1.a && !gamepad1AWasPreviouslyTrue) {
// Do something on the rising edge
}
// This one is the co-driver, but it's not obvious
if (!gamepad2.b && gamepad2BWasPreviouslyTrue) {
// Do something on the falling edge
}
// Have to do this for every button too...
gamepad1AWasPreviouslyTrue = gamepad1.a;
gamepad2BWasPreviouslyTrue = gamepad2.b;
}
var gamepad1AWasPreviouslyTrue = false // Unidiomatic mutable state
var gamepad2BWasPreviouslyTrue = false // Gamepad1 vs 2, have to be paying attention!
override fun loop() {
// Can't tell if this is the driver or not at first glance
// Also ugly to read and takes a second to mentally parse
if (gamepad1.a && !gamepad1AWasPreviouslyTrue) {
// Do something on the rising edge
}
// This one is the co-driver, but it's not obvious
if (!gamepad2.b && gamepad2BWasPreviouslyTrue) {
// Do something on the falling edge
}
// Have to do this for every button too...
gamepad1AWasPreviouslyTrue = gamepad1.a
gamepad2BWasPreviouslyTrue = gamepad2.b
}
into this!:
- Java
- Kotlin
@Override
public void runOpMode() throws InterruptedException {
ReforgedGamepad driver = new ReforgedGamepad(gamepad1);
ReforgedGamepad codriver = new ReforgedGamepad(gamepad2);
driver.a.onRise(() -> ...); // Self describing
codriver.a.onFall(() -> ...); // And no unnecessary logic or mutable state
Scheduler.launchOnStart(this)
}
override fun runOpMode() {
val driver = ReforgedGamepad(gamepad1)
val codriver = ReforgedGamepad(gamepad2)
driver.a.onRise { ... } // Self describing
codriver.b.onFall { ... } // And no unnecessary logic or unidiomatic mutable state
Scheduler.launchOnStart(this)
}
Behind the pretty veil, it's essentially just a collection of pre-made listeners, but it does come with one extra handy feature: You can get the value of any button without having to use the raw gamepad.
- Java
- Kotlin
driver.a.get(); // Returns the current state of the A button (i.e. if it's pressed or not)
driver.right_stick_x.get(); // Returns the current state of the right stick's X axis
/* `operator fun invoke()` is overriden, so you can use it like a function */
driver.a() // Returns the current state of the A button (i.e. if it's pressed or not)
driver.right_stick_x() // Returns the current state of the right stick's X axis
driver.b.get() // You can also use .get() if you prefer
I understand that you may not be entirely sure what this syntax means, but read on and come back to it later if you need to.
Construction
Constructs a new Reforged Gamepad from a given gamepad. You'll likely construct two in your programs; one for the driver, and one for the co-driver.
- Java
- Kotlin
ReforgedGamepad driver = new ReforgedGamepad(gamepad1);
ReforgedGamepad codriver = new ReforgedGamepad(gamepad2);
val driver = ReforgedGamepad(gamepad1)
val codriver = ReforgedGamepad(gamepad2)
Boolean triggers
Boolean triggers refer to buttons that are either pressed or not pressed (i.e. either true
or false
).
The ReforgedGamepad should support all the buttons on the gamepad.
- Java
- Kotlin
driver.a.onRise(() -> ...); // Do something when the A button is pressed
driver.a.onFall(() -> ...); // Do something when the A button is released
// Method chaining works too
driver.b.whileHigh(() -> ...) // Do something while the B button is pressed
.whileLow(() -> ...); // Do something while the B button is not pressed
// All the normal Listener methods work with it
driver.a.and(() -> lift.height() > 500)
.onRise(() -> ...); // Do something when the A button is pressed and the lift is above 500
driver.dpad_up.and(driver.right_bumper)
.onRise(() -> ...); // Do something when the D-Pad up and the right bumper are pressed
// You can also access the raw gamepad
driver.gamepad
// And you can get the value of any button without having to use the raw gamepad
driver.a.get(); // Returns the current state of the A button (i.e. if it's pressed or not)
driver.a.onRise { ... } // Do something when the A button is pressed
driver.a.onFall { ... } // Do something when the A button is released
// Method chaining works too
driver.b.whileHigh { ... } // Do something while the B button is pressed
.whileLow { ... } // Do something while the B button is not pressed
// All the normal Listener methods work with it
driver.a.and { lift.height() > 500 }
.onRise { ... } // Do something when the A button is pressed and the lift is above 500
(driver.dpad_up + driver.right_bumper)
.onRise { ... } // Do something when the D-Pad up and the right bumper are pressed
// You can also access the raw gamepad
driver.gamepad
// And you can get the value of any button without having to use the raw gamepad
driver.a() // Returns the current state of the A button (i.e. if it's pressed or not)
Analog triggers
Analog triggers are gamepad components which return a float value (e.g. the joysticks or triggers).
These can be accessed through functions, passing in a deadzone
parameter, which is a float between 0 and 1
The ReforgedGamepad compares the absolute value of the analog trigger to the deadzone, and becomes true if the abs value is greater than the deadzone.
- Java
- Kotlin
driver.left_stick_x(.1).onRise(() -> ...); // Do something when it becomes either >.1 or <-.1
driver.left_sick_x.onRise(() -> ...); // Shorthand for driver.left_stick_x(.1).onRise(() -> ...);
// It always returns the same object, it's mainly for convenience
driver.right_trigger(.5).whileHigh(() -> ...); // Do something while the right trigger is pressed > halfway
driver.left_stick_x.get(); // Returns the current value of the left stick's X axis (as a float)
// No reason to pass in a deadzone here, that creates an unnecessary object
// Normal listener methods still work with it
driver.touchpad_finger_2_x(.1).or(driver.a)
.onRise(() -> ...); // Do something when touchpad_finger_2_x >.1 or the A button is pressed
driver.left_stick_x(.1).onRise { ... } // Do something when it becomes either >.1 or <-.1
driver.left_sick_x.onRise { ... } // Shorthand for driver.left_stick_x(.1).onRise { ... }
// It always returns the same object, it's mainly for convenience
driver.right_trigger(.5).whileHigh { ... } // Do something while the right trigger is pressed > halfway
driver.left_stick_x() // Returns the current value of the left stick's X axis (as a float)
// No reason to pass in a deadzone here, that creates an unnecessary object
// Normal listener methods still work with it
(driver.touchpad_finger_2_x(.1) / driver.a)
.onRise { ... } // Do something when touchpad_finger_2_x >.1 or the A button is pressed
It's a relatively simple class, but it's powerful nevertheless.
Practical usage example
These are in Kotlin, but the concept still carries through to Java