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() { public static void initClient() {
ring.registerAction("buttonbinding", ButtonBindingRingAction.FACTORY); ring.registerAction("buttonbinding", ButtonBindingRingAction.FACTORY);
final MinecraftClient client = MinecraftClient.getInstance();
int delay = 0; // delay for 0 sec. 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 timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() { timer.scheduleAtFixedRate(new TimerTask() {
public void run() { public void run() {
input.updateCamera(client); if (client.isRunning()) {
input.tickJoysticks(client);
input.updateCamera(client);
}
} }
}, delay, period); }, delay, period);

View File

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

View File

@@ -140,13 +140,13 @@ public class MidnightInput {
if (controller.isConnected()) { if (controller.isConnected()) {
var state = controller.getState(); var state = controller.getState();
this.fetchButtonInput(client, state, false); this.fetchButtonInput(client, state, false);
this.fetchAxeInput(client, state, false); this.fetchTriggerInput(client, state, false);
} }
MidnightControlsConfig.getSecondController().filter(Controller::isConnected) MidnightControlsConfig.getSecondController().filter(Controller::isConnected)
.ifPresent(joycon -> { .ifPresent(joycon -> {
GLFWGamepadState state = joycon.getState(); var state = joycon.getState();
this.fetchButtonInput(client, state, true); this.fetchButtonInput(client, state, true);
this.fetchAxeInput(client, state, true); this.fetchTriggerInput(client, state, true);
}); });
boolean allowInput = this.controlsInput == null || this.controlsInput.focusedBinding == null; boolean allowInput = this.controlsInput == null || this.controlsInput.focusedBinding == null;
@@ -173,6 +173,22 @@ public class MidnightInput {
if (this.inventoryInteractionCooldown > 0) if (this.inventoryInteractionCooldown > 0)
this.inventoryInteractionCooldown--; 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. * This method is called before the screen is rendered.
@@ -210,7 +226,6 @@ public class MidnightInput {
client.player.getVehicle().onPassengerLookAround(client.player); client.player.getVehicle().onPassengerLookAround(client.player);
} }
client.getTutorialManager().onUpdateMouse(this.targetPitch, this.targetYaw); 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(); 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(); var buffer = gamepadState.axes();
polarUtil.calculate(buffer.get(GLFW_GAMEPAD_AXIS_LEFT_X), buffer.get(GLFW_GAMEPAD_AXIS_LEFT_Y), 1, MidnightControlsConfig.leftDeadZone); 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)); 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; int axis = leftJoycon ? ButtonBinding.controller2Button(i) : i;
float value = buffer.get(); float value = buffer.get(i);
switch (i) { switch (i) {
case GLFW_GAMEPAD_AXIS_LEFT_X -> {if (MidnightControlsConfig.analogMovement) value = leftX;} 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); int state = value > MidnightControlsConfig.rightDeadZone ? 1 : (value < -MidnightControlsConfig.rightDeadZone ? 2 : 0);
if (!isRadialMenu) if (!isRadialMenu)
this.handleAxe(client, new AxisStorage(axis, value, absValue, state)); this.handleJoystickAxis(client, new AxisStorage(axis, value, absValue, state));
} }
if (isRadialMenu) { if (isRadialMenu) {
float x = leftX; 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) { public void handleButton(@NotNull MinecraftClient client, int button, int action, boolean state) {
if (this.controlsInput != null && this.controlsInput.focusedBinding != null) { if (this.controlsInput != null && this.controlsInput.focusedBinding != null) {
if (action == 0 && !this.controlsInput.currentButtons.contains(button)) { 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.setCurrentPolarities(storage);
this.handleMovement(client, storage); this.handleMovement(client, storage);
@@ -497,6 +528,7 @@ public class MidnightInput {
this.prevYAxis = movementY; this.prevYAxis = movementY;
} }
} }
private void setCurrentPolarities(AxisStorage storage) { private void setCurrentPolarities(AxisStorage storage) {
boolean currentPlusState = storage.value > storage.deadZone; boolean currentPlusState = storage.value > storage.deadZone;
boolean currentMinusState = storage.value < -storage.deadZone; boolean currentMinusState = storage.value < -storage.deadZone;
@@ -737,8 +769,8 @@ public class MidnightInput {
if (storage.state != 0) { if (storage.state != 0) {
double rotation = Math.pow(storage.value, 2.0) * 0.11D * (storage.state == 2 ? -1 : 1); 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; if (storage.axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) this.targetPitch = rotation * MidnightControlsConfig.getRightYAxisSign() * MidnightControlsConfig.yAxisRotationSpeed / 2;
else this.targetYaw = rotation * MidnightControlsConfig.getRightXAxisSign() * MidnightControlsConfig.rotationSpeed; else this.targetYaw = rotation * MidnightControlsConfig.getRightXAxisSign() * MidnightControlsConfig.rotationSpeed / 2;
} }
} }
private void handleAdaptiveLook(AxisStorage storage) { private void handleAdaptiveLook(AxisStorage storage) {
@@ -747,8 +779,8 @@ public class MidnightInput {
xState = storage.state; xState = storage.state;
} }
else { else {
double yStep = (MidnightControlsConfig.yAxisRotationSpeed / 100) * 0.6000000238418579 + 0.20000000298023224; double yStep = (MidnightControlsConfig.yAxisRotationSpeed / 50) * 0.6000000238418579 + 0.20000000298023224;
double xStep = (MidnightControlsConfig.rotationSpeed / 100) * 0.6000000238418579 + 0.20000000298023224; double xStep = (MidnightControlsConfig.rotationSpeed / 50) * 0.6000000238418579 + 0.20000000298023224;
float yValue = storage.value; float yValue = storage.value;
float yState = storage.state; float yState = storage.state;