forked from Clones/Controlify
✏️ Greatly improve gyro control
This commit is contained in:
@ -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;
|
||||
|
||||
|
@ -85,7 +85,9 @@ public class GamepadController extends AbstractController<GamepadState, GamepadC
|
||||
}
|
||||
}
|
||||
|
||||
GamepadState.GyroState gyroState = drivers.gyroDriver().getGyroState();
|
||||
// todo: make this configurable
|
||||
GamepadState.GyroState gyroState = drivers.gyroDriver().getGyroState().deadzone(0.05f);
|
||||
this.absoluteGyro = this.absoluteGyro.add(gyroState);
|
||||
|
||||
state = new GamepadState(deadzoneAxesState, basicState.axes(), basicState.buttons(), gyroState, absoluteGyro);
|
||||
}
|
||||
|
@ -209,5 +209,13 @@ public final class GamepadState implements ControllerState {
|
||||
public GyroState add(GyroState other) {
|
||||
return new GyroState(pitch + other.pitch, yaw + other.yaw, roll + other.roll);
|
||||
}
|
||||
|
||||
public GyroState deadzone(float deadzone) {
|
||||
return new GyroState(
|
||||
Math.max(pitch - deadzone, 0) + Math.min(pitch + deadzone, 0),
|
||||
Math.max(yaw - deadzone, 0) + Math.min(yaw + deadzone, 0),
|
||||
Math.max(roll - deadzone, 0) + Math.min(roll + deadzone, 0)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,15 +15,23 @@ public class SDL2GamepadDriver implements GyroDriver, RumbleDriver, BatteryDrive
|
||||
this.ptrGamepad = SDL.SDL_GameControllerOpen(jid);
|
||||
this.isGyroSupported = SDL.SDL_GameControllerHasSensor(ptrGamepad, SDL.SDL_SENSOR_GYRO);
|
||||
this.isRumbleSupported = SDL.SDL_GameControllerHasRumble(ptrGamepad);
|
||||
|
||||
if (this.isGyroSupported()) {
|
||||
SDL.SDL_GameControllerSetSensorEnabled(ptrGamepad, SDL.SDL_SENSOR_GYRO, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
if (isGyroSupported()) {
|
||||
float[] gyro = new float[3];
|
||||
SDL.SDL_GameControllerGetSensorData(ptrGamepad, SDL.SDL_SENSOR_GYRO, gyro, 3);
|
||||
if (SDL.SDL_GameControllerGetSensorData(ptrGamepad, SDL.SDL_SENSOR_GYRO, gyro, 3) == 0) {
|
||||
gyroDelta = new GamepadState.GyroState(gyro[0], gyro[1], gyro[2]);
|
||||
if (DebugProperties.PRINT_GYRO) Controlify.LOGGER.info("Gyro delta: " + gyroDelta);
|
||||
} else {
|
||||
Controlify.LOGGER.error("Could not get gyro data: " + SDL.SDL_GetError());
|
||||
}
|
||||
|
||||
}
|
||||
SDL.SDL_GameControllerUpdate();
|
||||
}
|
||||
|
@ -418,6 +418,26 @@ public class ControllerConfigScreenFactory {
|
||||
o.requestSetDefault();
|
||||
}))
|
||||
.build());
|
||||
gyroGroup.option(Util.make(() -> {
|
||||
var opt = Option.<Boolean>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.<Boolean>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.<Boolean>createBuilder()
|
||||
.name(Component.translatable("controlify.gui.gyro_requires_button"))
|
||||
|
@ -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<Float> 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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
Reference in New Issue
Block a user