diff --git a/src/main/java/dev/isxander/controlify/controller/gamepad/GamepadConfig.java b/src/main/java/dev/isxander/controlify/controller/gamepad/GamepadConfig.java index 61f7fac..ff64506 100644 --- a/src/main/java/dev/isxander/controlify/controller/gamepad/GamepadConfig.java +++ b/src/main/java/dev/isxander/controlify/controller/gamepad/GamepadConfig.java @@ -11,6 +11,8 @@ public class GamepadConfig extends ControllerConfig { public float gyroLookSensitivity = 0f; public boolean gyroRequiresButton = true; public boolean flickStick = false; + public boolean invertGyroX = false; + public boolean invertGyroY = false; public BuiltinGamepadTheme theme = BuiltinGamepadTheme.DEFAULT; diff --git a/src/main/java/dev/isxander/controlify/controller/gamepad/GamepadController.java b/src/main/java/dev/isxander/controlify/controller/gamepad/GamepadController.java index 6c2ae36..cfa37bf 100644 --- a/src/main/java/dev/isxander/controlify/controller/gamepad/GamepadController.java +++ b/src/main/java/dev/isxander/controlify/controller/gamepad/GamepadController.java @@ -85,7 +85,9 @@ public class GamepadController extends AbstractController { + var opt = Option.createBuilder() + .name(Component.translatable("controlify.gui.gyro_invert_x")) + .description(OptionDescription.of(Component.translatable("controlify.gui.gyro_invert_x.tooltip"))) + .binding(gpCfgDef.invertGyroX, () -> gpCfg.invertGyroX, v -> gpCfg.invertGyroX = v) + .controller(TickBoxControllerBuilder::create) + .build(); + gyroOptions.add(opt); + return opt; + })); + gyroGroup.option(Util.make(() -> { + var opt = Option.createBuilder() + .name(Component.translatable("controlify.gui.gyro_invert_y")) + .description(OptionDescription.of(Component.translatable("controlify.gui.gyro_invert_y.tooltip"))) + .binding(gpCfgDef.invertGyroY, () -> gpCfg.invertGyroY, v -> gpCfg.invertGyroY = v) + .controller(TickBoxControllerBuilder::create) + .build(); + gyroOptions.add(opt); + return opt; + })); gyroGroup.option(Util.make(() -> { var opt = Option.createBuilder() .name(Component.translatable("controlify.gui.gyro_requires_button")) diff --git a/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java b/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java index 3401873..bc0f8fb 100644 --- a/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java +++ b/src/main/java/dev/isxander/controlify/ingame/InGameInputHandler.java @@ -6,6 +6,8 @@ import dev.isxander.controlify.api.ingameinput.LookInputModifier; import dev.isxander.controlify.controller.Controller; import dev.isxander.controlify.api.event.ControlifyEvents; import dev.isxander.controlify.controller.gamepad.GamepadController; +import dev.isxander.controlify.utils.Animator; +import dev.isxander.controlify.utils.Easings; import dev.isxander.controlify.utils.NavigationHelper; import net.minecraft.client.CameraType; import net.minecraft.client.Minecraft; @@ -15,7 +17,10 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; import net.minecraft.world.InteractionHand; +import org.joml.Vector2f; +import org.joml.Vector2fc; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiFunction; public class InGameInputHandler { @@ -130,13 +135,20 @@ public class InGameInputHandler { if (gamepad != null && gamepad.config().flickStick) { var turnAngle = 90 / 0.15f; // Entity#turn multiplies cursor delta by 0.15 to get rotation - player.turn( - (controller.bindings().LOOK_RIGHT.justPressed() ? turnAngle : 0) - - (controller.bindings().LOOK_LEFT.justPressed() ? turnAngle : 0), - (controller.bindings().LOOK_DOWN.justPressed() ? turnAngle : 0) - - (controller.bindings().LOOK_UP.justPressed() ? turnAngle : 0) + AtomicReference lastAngle = new AtomicReference<>(0f); + Vector2fc flickVec = new Vector2f( + controller.bindings().LOOK_RIGHT.justPressed() ? 1 : controller.bindings().LOOK_LEFT.justPressed() ? -1 : 0, + controller.bindings().LOOK_DOWN.justPressed() ? 1 : controller.bindings().LOOK_UP.justPressed() ? -1 : 0 ); + if (!flickVec.equals(0, 0)) { + Animator.INSTANCE.play(new Animator.AnimationInstance(10, Easings::easeOutExpo) + .addConsumer(angle -> { + player.turn((angle - lastAngle.get()) * flickVec.x(), (angle - lastAngle.get()) * flickVec.y()); + lastAngle.set(angle); + }, 0, turnAngle)); + } + return; } @@ -161,10 +173,10 @@ public class InGameInputHandler { && gamepad.hasGyro() && (!gamepad.config().gyroRequiresButton || gamepad.bindings().GAMEPAD_GYRO_BUTTON.held()) ) { - var gyroDelta = gamepad.state().gyroDelta(); + var gyroDelta = gamepad.absoluteGyroState().deadzone(0.05f); - impulseX += (gyroDelta.yaw() + gyroDelta.pitch()) * gamepad.config().gyroLookSensitivity; - impulseY += gyroDelta.roll() * gamepad.config().gyroLookSensitivity; + impulseY += -gyroDelta.pitch() * gamepad.config().gyroLookSensitivity * (gamepad.config().invertGyroY ? -1 : 1); + impulseX += (-gyroDelta.roll() + -gyroDelta.yaw()) * gamepad.config().gyroLookSensitivity * (gamepad.config().invertGyroX ? -1 : 1); } LookInputModifier lookInputModifier = ControlifyEvents.LOOK_INPUT_MODIFIER.invoker(); diff --git a/src/main/java/dev/isxander/controlify/utils/Easings.java b/src/main/java/dev/isxander/controlify/utils/Easings.java index 7570be5..e502604 100644 --- a/src/main/java/dev/isxander/controlify/utils/Easings.java +++ b/src/main/java/dev/isxander/controlify/utils/Easings.java @@ -8,4 +8,8 @@ public class Easings { public static float easeOutQuad(float t) { return 1 - (1 - t) * (1 - t); } + + public static float easeOutExpo(float t) { + return t == 1 ? 1 : 1 - (float) Math.pow(2, -10 * t); + } } diff --git a/src/main/resources/assets/controlify/lang/en_us.json b/src/main/resources/assets/controlify/lang/en_us.json index c96ce36..4f9669a 100644 --- a/src/main/resources/assets/controlify/lang/en_us.json +++ b/src/main/resources/assets/controlify/lang/en_us.json @@ -76,7 +76,11 @@ "controlify.gui.group.gyro.tooltip": "Adjust how Controlify treats your controller's built in gyroscope.\nA gyroscope determines how the controller is rotated.", "controlify.gui.group.gyro.no_gyro.tooltip": "This controller does not support Gyro. You must have a DualSenseā„¢ controller or other compatible controller to use this feature.", "controlify.gui.gyro_look_sensitivity": "Look Sensitivity", - "controlify.gui.gyro_look_sensitivity.tooltip": "How much the camera moves based on gyroscope rotation.", + "controlify.gui.gyro_look_sensitivity.tooltip": "How much the camera moves based on gyroscope rotation.\nThe pitch (rotating your controller forward/backward) is used for looking up and down, whilst both the roll (rotating your controller left/right) and yaw (rotating your controller clockwise/anticlockwise) are used for looking left and right.", + "controlify.gui.gyro_invert_x": "Invert X", + "controlify.gui.gyro_invert_x.tooltip": "Invert the left/right rotation of the gyroscope look direction.", + "controlify.gui.gyro_invert_y": "Invert Y", + "controlify.gui.gyro_invert_y.tooltip": "Invert the up/down rotation of the gyroscope look direction.", "controlify.gui.gyro_requires_button": "Require Button", "controlify.gui.gyro_requires_button.tooltip": "If the gyroscope should only be used when the gyro bind is pressed down. (scroll down to controls).", "controlify.gui.flick_stick": "Flick Stick",