Butter-smooth joystick input

- Handle joysticks separately from buttons and triggers
- Joysticks now get updated 1000 times per second
This commit is contained in:
Martin Prokoph
2024-07-21 21:37:46 +02:00
parent 86c96c8a3e
commit abcbf20eaf
3 changed files with 51 additions and 17 deletions

View File

@@ -82,13 +82,15 @@ public class MidnightControlsClient extends MidnightControls {
public static void initClient() {
ring.registerAction("buttonbinding", ButtonBindingRingAction.FACTORY);
final MinecraftClient client = MinecraftClient.getInstance();
int delay = 0; // delay for 0 sec.
int period = 1; // repeat every 0.001 sec. (100 times a second)
int period = 1; // repeat every 0.001 sec. (1000 times a second)
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
input.updateCamera(client);
if (client.isRunning()) {
input.tickJoysticks(client);
input.updateCamera(client);
}
}
}, delay, period);

View File

@@ -367,7 +367,7 @@ public class MidnightControlsConfig extends MidnightConfig {
leftDeadZone = 0.25;
invertRightYAxis = false;
invertRightXAxis = false;
rotationSpeed = 40.0;
rotationSpeed = 35.0;
yAxisRotationSpeed = rotationSpeed;
mouseSpeed = 25.0;
unfocusedInput = false;

View File

@@ -140,13 +140,13 @@ public class MidnightInput {
if (controller.isConnected()) {
var state = controller.getState();
this.fetchButtonInput(client, state, false);
this.fetchAxeInput(client, state, false);
this.fetchTriggerInput(client, state, false);
}
MidnightControlsConfig.getSecondController().filter(Controller::isConnected)
.ifPresent(joycon -> {
GLFWGamepadState state = joycon.getState();
var state = joycon.getState();
this.fetchButtonInput(client, state, true);
this.fetchAxeInput(client, state, true);
this.fetchTriggerInput(client, state, true);
});
boolean allowInput = this.controlsInput == null || this.controlsInput.focusedBinding == null;
@@ -173,6 +173,22 @@ public class MidnightInput {
if (this.inventoryInteractionCooldown > 0)
this.inventoryInteractionCooldown--;
}
/**
* This method is called 1000 times a second for smooth joystick input
*
* @param client the client instance
*/
public void tickJoysticks(@NotNull MinecraftClient client) {
var controller = MidnightControlsConfig.getController();
if (controller.isConnected()) {
this.fetchJoystickInput(client, controller.getState(), false);
}
MidnightControlsConfig.getSecondController().filter(Controller::isConnected)
.ifPresent(joycon -> {
this.fetchJoystickInput(client, joycon.getState(), true);
});
}
/**
* This method is called before the screen is rendered.
@@ -210,7 +226,6 @@ public class MidnightInput {
client.player.getVehicle().onPassengerLookAround(client.player);
}
client.getTutorialManager().onUpdateMouse(this.targetPitch, this.targetYaw);
MidnightControlsCompat.handleCamera(client, targetYaw, targetPitch);
}
}
@@ -266,7 +281,7 @@ public class MidnightInput {
}
final MathUtil.PolarUtil polarUtil = new MathUtil.PolarUtil();
private void fetchAxeInput(@NotNull MinecraftClient client, @NotNull GLFWGamepadState gamepadState, boolean leftJoycon) {
private void fetchJoystickInput(@NotNull MinecraftClient client, @NotNull GLFWGamepadState gamepadState, boolean leftJoycon) {
var buffer = gamepadState.axes();
polarUtil.calculate(buffer.get(GLFW_GAMEPAD_AXIS_LEFT_X), buffer.get(GLFW_GAMEPAD_AXIS_LEFT_Y), 1, MidnightControlsConfig.leftDeadZone);
@@ -278,9 +293,9 @@ public class MidnightInput {
boolean isRadialMenu = client.currentScreen instanceof RingScreen || (MidnightControlsCompat.isEmotecraftPresent() && EmotecraftCompat.isEmotecraftScreen(client.currentScreen));
for (int i = 0; i < buffer.limit(); i++) {
for (int i = 0; i < GLFW_GAMEPAD_AXIS_LEFT_TRIGGER; i++) {
int axis = leftJoycon ? ButtonBinding.controller2Button(i) : i;
float value = buffer.get();
float value = buffer.get(i);
switch (i) {
case GLFW_GAMEPAD_AXIS_LEFT_X -> {if (MidnightControlsConfig.analogMovement) value = leftX;}
@@ -295,7 +310,7 @@ public class MidnightInput {
int state = value > MidnightControlsConfig.rightDeadZone ? 1 : (value < -MidnightControlsConfig.rightDeadZone ? 2 : 0);
if (!isRadialMenu)
this.handleAxe(client, new AxisStorage(axis, value, absValue, state));
this.handleJoystickAxis(client, new AxisStorage(axis, value, absValue, state));
}
if (isRadialMenu) {
float x = leftX;
@@ -324,6 +339,19 @@ public class MidnightInput {
}
}
private void fetchTriggerInput(@NotNull MinecraftClient client, @NotNull GLFWGamepadState gamepadState, boolean leftJoycon) {
var buffer = gamepadState.axes();
for (int i = GLFW_GAMEPAD_AXIS_LEFT_TRIGGER; i <= GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER; i++) {
int axis = leftJoycon ? ButtonBinding.controller2Button(i) : i;
float value = buffer.get(i);
float absValue = Math.abs(value);
int state = value > MidnightControlsConfig.rightDeadZone ? 1 : (value < -MidnightControlsConfig.rightDeadZone ? 2 : 0);
this.handleTriggerAxis(client, new AxisStorage(axis, value, absValue, state));
}
}
public void handleButton(@NotNull MinecraftClient client, int button, int action, boolean state) {
if (this.controlsInput != null && this.controlsInput.focusedBinding != null) {
if (action == 0 && !this.controlsInput.currentButtons.contains(button)) {
@@ -425,8 +453,11 @@ public class MidnightInput {
}
}
}
private void handleTriggerAxis(@NotNull MinecraftClient client, AxisStorage storage) {
this.setCurrentPolarities(storage);
}
private void handleAxe(@NotNull MinecraftClient client, AxisStorage storage) {
private void handleJoystickAxis(@NotNull MinecraftClient client, AxisStorage storage) {
this.setCurrentPolarities(storage);
this.handleMovement(client, storage);
@@ -497,6 +528,7 @@ public class MidnightInput {
this.prevYAxis = movementY;
}
}
private void setCurrentPolarities(AxisStorage storage) {
boolean currentPlusState = storage.value > storage.deadZone;
boolean currentMinusState = storage.value < -storage.deadZone;
@@ -737,8 +769,8 @@ public class MidnightInput {
if (storage.state != 0) {
double rotation = Math.pow(storage.value, 2.0) * 0.11D * (storage.state == 2 ? -1 : 1);
if (storage.axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) this.targetPitch = rotation * MidnightControlsConfig.getRightYAxisSign() * MidnightControlsConfig.yAxisRotationSpeed;
else this.targetYaw = rotation * MidnightControlsConfig.getRightXAxisSign() * MidnightControlsConfig.rotationSpeed;
if (storage.axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) this.targetPitch = rotation * MidnightControlsConfig.getRightYAxisSign() * MidnightControlsConfig.yAxisRotationSpeed / 2;
else this.targetYaw = rotation * MidnightControlsConfig.getRightXAxisSign() * MidnightControlsConfig.rotationSpeed / 2;
}
}
private void handleAdaptiveLook(AxisStorage storage) {
@@ -747,8 +779,8 @@ public class MidnightInput {
xState = storage.state;
}
else {
double yStep = (MidnightControlsConfig.yAxisRotationSpeed / 100) * 0.6000000238418579 + 0.20000000298023224;
double xStep = (MidnightControlsConfig.rotationSpeed / 100) * 0.6000000238418579 + 0.20000000298023224;
double yStep = (MidnightControlsConfig.yAxisRotationSpeed / 50) * 0.6000000238418579 + 0.20000000298023224;
double xStep = (MidnightControlsConfig.rotationSpeed / 50) * 0.6000000238418579 + 0.20000000298023224;
float yValue = storage.value;
float yState = storage.state;