1
0
forked from Clones/Controlify

✏️ Greatly improve gyro control

This commit is contained in:
isXander
2023-06-04 12:08:03 +01:00
parent 5669ea9b3a
commit daee2de327
8 changed files with 73 additions and 13 deletions

View File

@ -11,6 +11,8 @@ public class GamepadConfig extends ControllerConfig {
public float gyroLookSensitivity = 0f; public float gyroLookSensitivity = 0f;
public boolean gyroRequiresButton = true; public boolean gyroRequiresButton = true;
public boolean flickStick = false; public boolean flickStick = false;
public boolean invertGyroX = false;
public boolean invertGyroY = false;
public BuiltinGamepadTheme theme = BuiltinGamepadTheme.DEFAULT; public BuiltinGamepadTheme theme = BuiltinGamepadTheme.DEFAULT;

View File

@ -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); state = new GamepadState(deadzoneAxesState, basicState.axes(), basicState.buttons(), gyroState, absoluteGyro);
} }

View File

@ -209,5 +209,13 @@ public final class GamepadState implements ControllerState {
public GyroState add(GyroState other) { public GyroState add(GyroState other) {
return new GyroState(pitch + other.pitch, yaw + other.yaw, roll + other.roll); 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)
);
}
} }
} }

View File

@ -15,15 +15,23 @@ public class SDL2GamepadDriver implements GyroDriver, RumbleDriver, BatteryDrive
this.ptrGamepad = SDL.SDL_GameControllerOpen(jid); this.ptrGamepad = SDL.SDL_GameControllerOpen(jid);
this.isGyroSupported = SDL.SDL_GameControllerHasSensor(ptrGamepad, SDL.SDL_SENSOR_GYRO); this.isGyroSupported = SDL.SDL_GameControllerHasSensor(ptrGamepad, SDL.SDL_SENSOR_GYRO);
this.isRumbleSupported = SDL.SDL_GameControllerHasRumble(ptrGamepad); this.isRumbleSupported = SDL.SDL_GameControllerHasRumble(ptrGamepad);
if (this.isGyroSupported()) {
SDL.SDL_GameControllerSetSensorEnabled(ptrGamepad, SDL.SDL_SENSOR_GYRO, true);
}
} }
@Override @Override
public void update() { public void update() {
if (isGyroSupported()) { if (isGyroSupported()) {
float[] gyro = new float[3]; 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]); gyroDelta = new GamepadState.GyroState(gyro[0], gyro[1], gyro[2]);
if (DebugProperties.PRINT_GYRO) Controlify.LOGGER.info("Gyro delta: " + gyroDelta); 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(); SDL.SDL_GameControllerUpdate();
} }

View File

@ -418,6 +418,26 @@ public class ControllerConfigScreenFactory {
o.requestSetDefault(); o.requestSetDefault();
})) }))
.build()); .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(() -> { gyroGroup.option(Util.make(() -> {
var opt = Option.<Boolean>createBuilder() var opt = Option.<Boolean>createBuilder()
.name(Component.translatable("controlify.gui.gyro_requires_button")) .name(Component.translatable("controlify.gui.gyro_requires_button"))

View File

@ -6,6 +6,8 @@ import dev.isxander.controlify.api.ingameinput.LookInputModifier;
import dev.isxander.controlify.controller.Controller; import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.api.event.ControlifyEvents; import dev.isxander.controlify.api.event.ControlifyEvents;
import dev.isxander.controlify.controller.gamepad.GamepadController; 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 dev.isxander.controlify.utils.NavigationHelper;
import net.minecraft.client.CameraType; import net.minecraft.client.CameraType;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
@ -15,7 +17,10 @@ import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionHand;
import org.joml.Vector2f;
import org.joml.Vector2fc;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiFunction; import java.util.function.BiFunction;
public class InGameInputHandler { public class InGameInputHandler {
@ -130,13 +135,20 @@ public class InGameInputHandler {
if (gamepad != null && gamepad.config().flickStick) { if (gamepad != null && gamepad.config().flickStick) {
var turnAngle = 90 / 0.15f; // Entity#turn multiplies cursor delta by 0.15 to get rotation var turnAngle = 90 / 0.15f; // Entity#turn multiplies cursor delta by 0.15 to get rotation
player.turn( AtomicReference<Float> lastAngle = new AtomicReference<>(0f);
(controller.bindings().LOOK_RIGHT.justPressed() ? turnAngle : 0) Vector2fc flickVec = new Vector2f(
- (controller.bindings().LOOK_LEFT.justPressed() ? turnAngle : 0), controller.bindings().LOOK_RIGHT.justPressed() ? 1 : controller.bindings().LOOK_LEFT.justPressed() ? -1 : 0,
(controller.bindings().LOOK_DOWN.justPressed() ? turnAngle : 0) controller.bindings().LOOK_DOWN.justPressed() ? 1 : controller.bindings().LOOK_UP.justPressed() ? -1 : 0
- (controller.bindings().LOOK_UP.justPressed() ? turnAngle : 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; return;
} }
@ -161,10 +173,10 @@ public class InGameInputHandler {
&& gamepad.hasGyro() && gamepad.hasGyro()
&& (!gamepad.config().gyroRequiresButton || gamepad.bindings().GAMEPAD_GYRO_BUTTON.held()) && (!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.pitch() * gamepad.config().gyroLookSensitivity * (gamepad.config().invertGyroY ? -1 : 1);
impulseY += gyroDelta.roll() * gamepad.config().gyroLookSensitivity; impulseX += (-gyroDelta.roll() + -gyroDelta.yaw()) * gamepad.config().gyroLookSensitivity * (gamepad.config().invertGyroX ? -1 : 1);
} }
LookInputModifier lookInputModifier = ControlifyEvents.LOOK_INPUT_MODIFIER.invoker(); LookInputModifier lookInputModifier = ControlifyEvents.LOOK_INPUT_MODIFIER.invoker();

View File

@ -8,4 +8,8 @@ public class Easings {
public static float easeOutQuad(float t) { public static float easeOutQuad(float t) {
return 1 - (1 - t) * (1 - t); return 1 - (1 - t) * (1 - t);
} }
public static float easeOutExpo(float t) {
return t == 1 ? 1 : 1 - (float) Math.pow(2, -10 * t);
}
} }

View File

@ -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.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.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": "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": "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.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", "controlify.gui.flick_stick": "Flick Stick",