From 54c2564c2fc932bfea6bc3932792f0bb4f53fc4d Mon Sep 17 00:00:00 2001 From: Duy Tran Khanh Date: Sun, 5 Feb 2023 12:16:46 +0700 Subject: [PATCH] Feature: gyroscope controls --- Natives/CMakeLists.txt | 2 + Natives/LauncherPreferences.m | 3 ++ Natives/LauncherPreferencesViewController.m | 26 +++++++++++ Natives/SurfaceViewController.m | 28 +++++++++++- Natives/en.lproj/Localizable.strings | 8 ++++ Natives/input/ControllerInput.m | 2 +- Natives/input/GyroInput.h | 8 ++++ Natives/input/GyroInput.m | 48 +++++++++++++++++++++ 8 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 Natives/input/GyroInput.h create mode 100644 Natives/input/GyroInput.m diff --git a/Natives/CMakeLists.txt b/Natives/CMakeLists.txt index 6ac6d271..f624a4a5 100644 --- a/Natives/CMakeLists.txt +++ b/Natives/CMakeLists.txt @@ -100,6 +100,7 @@ add_executable(PojavLauncher external/TOInsetGroupedTableView/TOInsetGroupedTableView/TOInsetGroupedTableView.m input/ControllerInput.m + input/GyroInput.m input/KeyboardInput.m AccountListViewController.m @@ -143,6 +144,7 @@ target_link_libraries(PojavLauncher "-framework CAltKit" "-framework CoreGraphics" "-framework CoreImage" + "-framework CoreMotion" "-framework CoreServices" "-framework GameController" "-framework QuartzCore" diff --git a/Natives/LauncherPreferences.m b/Natives/LauncherPreferences.m index 9e70507c..073aa1a4 100644 --- a/Natives/LauncherPreferences.m +++ b/Natives/LauncherPreferences.m @@ -122,6 +122,9 @@ void loadPreferences(BOOL reset) { setDefaultValueForPref(envPrefDict, @"press_duration", @(400)); setDefaultValueForPref(envPrefDict, @"mouse_scale", @(100)); setDefaultValueForPref(envPrefDict, @"mouse_speed", @(100)); + setDefaultValueForPref(envPrefDict, @"gyroscope_enable", @NO); + setDefaultValueForPref(envPrefDict, @"gyroscope_invert_x_axis", @NO); + setDefaultValueForPref(envPrefDict, @"gyroscope_sensitivity", @(100)); setDefaultValueForPref(envPrefDict, @"default_ctrl", @"default.json"); setDefaultValueForPref(envPrefDict, @"game_directory", @"default"); setDefaultValueForPref(envPrefDict, @"java_args", @""); diff --git a/Natives/LauncherPreferencesViewController.m b/Natives/LauncherPreferencesViewController.m index 737caedb..070cadbc 100644 --- a/Natives/LauncherPreferencesViewController.m +++ b/Natives/LauncherPreferencesViewController.m @@ -259,6 +259,32 @@ typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *); @"hasDetail": @YES, @"icon": @"slider.horizontal.below.rectangle", @"type": self.typeSwitch + }, + @{@"key": @"gyroscope_enable", + @"hasDetail": @YES, + @"icon": @"gyroscope", + @"type": self.typeSwitch, + @"enableCondition": ^BOOL(){ + return realUIIdiom != UIUserInterfaceIdiomTV; + } + }, + @{@"key": @"gyroscope_invert_x_axis", + @"hasDetail": @YES, + @"icon": @"arrow.left.and.right", + @"type": self.typeSwitch, + @"enableCondition": ^BOOL(){ + return realUIIdiom != UIUserInterfaceIdiomTV; + } + }, + @{@"key": @"gyroscope_sensitivity", + @"hasDetail": @YES, + @"icon": @"move.3d", + @"type": self.typeSlider, + @"min": @(50), + @"max": @(300), + @"enableCondition": ^BOOL(){ + return realUIIdiom != UIUserInterfaceIdiomTV; + } } ], @[ // Java tweaks diff --git a/Natives/SurfaceViewController.m b/Natives/SurfaceViewController.m index 99e6f45d..eb2dcc1b 100644 --- a/Natives/SurfaceViewController.m +++ b/Natives/SurfaceViewController.m @@ -12,6 +12,7 @@ #import "customcontrols/CustomControlsUtils.h" #import "input/ControllerInput.h" +#import "input/GyroInput.h" #import "input/KeyboardInput.h" #import "JavaLauncher.h" @@ -79,7 +80,11 @@ BOOL slideableHotbar; } // Perform Gamepad joystick ticking, while also controlling frame rate? - CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:ControllerInput.class selector:@selector(tick)]; + id tickInput = ^{ + [GyroInput tick]; + [ControllerInput tick]; + }; + CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:tickInput selector:@selector(invoke)]; if (@available(iOS 15.0, tvOS 15.0, *)) { displayLink.preferredFrameRateRange = CAFrameRateRangeMake(60, 120, 120); } @@ -289,6 +294,11 @@ BOOL slideableHotbar; self.inputTextField.autocorrectionType = UITextAutocorrectionTypeNo; } + BOOL gyroEnabled = [getPreference(@"gyroscope_enable") boolValue]; + BOOL gyroInvertX = [getPreference(@"gyroscope_invert_x_axis") boolValue]; + int gyroSensitivity = [getPreference(@"gyroscope_sensitivity") intValue]; + [GyroInput updateSensitivity:gyroEnabled?gyroSensitivity:0 invertXAxis:gyroInvertX]; + self.mouseSpeed = [getPreference(@"mouse_speed") floatValue] / 100.0; slideableHotbar = [getPreference(@"slideable_hotbar") boolValue]; @@ -450,7 +460,21 @@ BOOL slideableHotbar; } lastVirtualMousePoint = location; } - callback_SurfaceViewController_onTouch(event, location.x * screenScale, location.y * screenScale); + + CGFloat x = location.x; + CGFloat y = location.y; + switch (event) { + case ACTION_DOWN: + lastVirtualMousePoint = location; + break; + case ACTION_MOVE: + event = ACTION_MOVE_MOTION; + x = location.x - lastVirtualMousePoint.x; + y = location.y - lastVirtualMousePoint.y; + lastVirtualMousePoint = location; + break; + } + callback_SurfaceViewController_onTouch(event, x * screenScale, y * screenScale); } #pragma mark - Input: on-surface functions diff --git a/Natives/en.lproj/Localizable.strings b/Natives/en.lproj/Localizable.strings index 2dd4938c..f8d46544 100644 --- a/Natives/en.lproj/Localizable.strings +++ b/Natives/en.lproj/Localizable.strings @@ -189,6 +189,14 @@ "preference.detail.virtmouse_enable" = "Enable this option to start with virtual mouse on"; "preference.title.slideable_hotbar" = "Slideable hotbar"; "preference.detail.slideable_hotbar" = "Make the hotbar slideable like the mouse wheel"; + +"preference.title.gyroscope_enable" = "Enable gyroscope controls"; +"preference.detail.gyroscope_enable" = "Enabling this will allow you to turn in game by turning your device"; +"preference.title.gyroscope_invert_x_axis" = "Invert gyroscope left and right"; +"preference.detail.gyroscope_invert_x_axis" = "Inverts the X-axis of the gyroscope"; +"preference.title.gyroscope_sensitivity" = "Gyroscope controls sensitivity"; +"preference.detail.gyroscope_sensitivity" = "Adjust the sensitivity of gyroscope controls"; + "preference.title.java_home" = "Use Java 17 for Minecraft < 1.17"; "preference.detail.java_home" = "Java 8 is used by default for those versions. Enable this option to force use Java 17, however compatibility with modded is not guaranteed."; "preference.title.java_args" = "JVM Launch arguments"; diff --git a/Natives/input/ControllerInput.m b/Natives/input/ControllerInput.m index b690be96..b2f2d85e 100644 --- a/Natives/input/ControllerInput.m +++ b/Natives/input/ControllerInput.m @@ -245,7 +245,7 @@ BOOL leftShiftHeld; // Compute delta since last tick time CGFloat deltaX = lastXValue * acceleration * 18; CGFloat deltaY = -lastYValue * acceleration * 18; - CGFloat deltaTimeScale = (frameTime - lastFrameTime) / 0.016666666; // Scale of 1 = 60Hz + CGFloat deltaTimeScale = (frameTime - lastFrameTime) / (1.0/60.0); // Scale of 1 = 60Hz deltaX *= deltaTimeScale; deltaY *= deltaTimeScale; diff --git a/Natives/input/GyroInput.h b/Natives/input/GyroInput.h new file mode 100644 index 00000000..1f519eb5 --- /dev/null +++ b/Natives/input/GyroInput.h @@ -0,0 +1,8 @@ +#import + +@interface GyroInput : NSObject + ++ (void)updateSensitivity:(int)sensitivity invertXAxis:(BOOL)invertX; ++ (void)tick; + +@end diff --git a/Natives/input/GyroInput.m b/Natives/input/GyroInput.m new file mode 100644 index 00000000..4f333de1 --- /dev/null +++ b/Natives/input/GyroInput.m @@ -0,0 +1,48 @@ +#import +#import +#import "GyroInput.h" +#import "../SurfaceViewController.h" +#import "../utils.h" + +@implementation GyroInput + +static CGFloat lastXValue, lastYValue, lastFrameTime; +static CGFloat gyroSensitivity; +static int gyroInvertX; +static CMMotionManager* cmInstance; + ++ (void)updateSensitivity:(int)sensitivity invertXAxis:(BOOL)invertX { + if (cmInstance == nil) { + cmInstance = [[CMMotionManager alloc] init]; + } + gyroSensitivity = sensitivity / 100.0; + if (sensitivity > 0) { + gyroInvertX = invertX ? 1 : -1; + [cmInstance startGyroUpdates]; + } else { + [cmInstance stopGyroUpdates]; + } +} + ++ (void)tick { + if (!isGrabbing || gyroSensitivity == 0) return; + + // Compute delta since last tick time + CGFloat frameTime = CACurrentMediaTime(); + CGFloat factor = gyroSensitivity; + if (lastFrameTime != 0) { + CGFloat deltaTimeScale = (frameTime - lastFrameTime) / (1.0/60.0); // Scale of 1 = 60Hz + factor *= deltaTimeScale; + } + + // 100% sensitivity -> 1:1 ratio between real world and ingame camera + lastXValue = cmInstance.gyroData.rotationRate.x / (M_PI*360) * windowWidth * factor * gyroInvertX; + lastYValue = cmInstance.gyroData.rotationRate.y / (M_PI*180) * windowHeight * factor; + + SurfaceViewController *vc = (id)(currentWindow().rootViewController); + [vc sendTouchPoint:CGPointMake(lastXValue, lastYValue) withEvent:ACTION_MOVE_MOTION]; + + lastFrameTime = frameTime; +} + +@end