From 42ce9c410bc4abe9a2b8c3e88cc1106d76c1ca59 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sun, 12 Feb 2023 18:25:33 +0200 Subject: [PATCH] Dynamic rewind/forward speed in iOS --- iOS/GBBackgroundView.m | 61 +++++++++++++++++++++++++++++++--- iOS/GBSettingsViewController.m | 30 ++++++++++++++--- iOS/GBViewController.h | 2 +- iOS/GBViewController.m | 45 +++++++++++++++++-------- iOS/main.m | 1 + 5 files changed, 115 insertions(+), 24 deletions(-) diff --git a/iOS/GBBackgroundView.m b/iOS/GBBackgroundView.m index ded6f45..63e4fb2 100644 --- a/iOS/GBBackgroundView.m +++ b/iOS/GBBackgroundView.m @@ -87,6 +87,7 @@ static GB_key_mask_t angleToKeyMask(double angle) UITouch *_screenTouch; CGPoint _screenSwipeOrigin; bool _screenSwiped; + bool _inDynamicSpeedMode; UIImageView *_dpadView; UIImageView *_dpadShadowView; @@ -146,6 +147,7 @@ static GB_key_mask_t angleToKeyMask(double angle) if (@available(iOS 13.0, *)) { _overlayViewContents = [[UIImageView alloc] init]; _overlayViewContents.tintColor = [UIColor whiteColor]; + _overlayViewContents.contentMode = UIViewContentModeCenter; } else { _overlayViewContents = [[UILabel alloc] init]; @@ -182,9 +184,14 @@ static GB_key_mask_t angleToKeyMask(double angle) _screenTouch = touch; _screenSwipeOrigin = point; _screenSwiped = false; + _inDynamicSpeedMode = false; _overlayView.alpha = 0; [_fadeTimer invalidate]; _fadeTimer = nil; + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBDynamicSpeed"]) { + self.viewController.runMode = GBRunModePaused; + [self displayOverlayWithImage:@"pause" orTitle:@"Paused"]; + } } } @@ -208,6 +215,10 @@ static GB_key_mask_t angleToKeyMask(double angle) if ([touches containsObject:_screenTouch]) { _screenTouch = nil; + if (self.viewController.runMode == GBRunModePaused) { + self.viewController.runMode = GBRunModeNormal; + [self fadeOverlayOut]; + } if (!_screenSwiped) { [self.window.rootViewController presentViewController:[GBMenuViewController menu] animated:true completion:nil]; } @@ -264,6 +275,33 @@ static GB_key_mask_t angleToKeyMask(double angle) CGPoint point = [touch locationInView:self]; if (touch == _screenTouch) { + if (_inDynamicSpeedMode) { + double delta = point.x - _screenSwipeOrigin.x; + if (fabs(delta) < 32) { + self.viewController.runMode = GBRunModePaused; + [self displayOverlayWithImage:@"pause" orTitle:@"Paused"]; + continue; + } + + double speed = fabs(delta / _gbView.frame.size.width * 3); + if (delta > 0) { + if (speed > 1) { + [self displayOverlayWithImage:@"forward" orTitle:@"Fast-forwarding…"]; + } + else { + [self displayOverlayWithImage:@"play" orTitle:@"Forward…"]; + } + GB_set_clock_multiplier(_gbView.gb, speed); + self.viewController.runMode = GBRunModeTurbo; + } + else { + [self displayOverlayWithImage:@"backward" orTitle:@"Rewinding…"]; + GB_set_clock_multiplier(_gbView.gb, speed); + self.viewController.runMode = GBRunModeRewind; + + } + continue; + } if (_screenSwiped) continue; if (point.x - _screenSwipeOrigin.x > 32) { [self turboSwipe]; @@ -397,11 +435,14 @@ static GB_key_mask_t angleToKeyMask(double angle) } [_overlayViewContents sizeToFit]; - CGRect bounds = _overlayViewContents.bounds; - bounds.origin = (CGPoint){8, 8}; - bounds.size.width += 16; - bounds.size.height += 16; - _overlayView.frame = bounds; + CGRect frame = _overlayViewContents.frame; + frame.size.width = MAX(frame.size.width, 25); + frame.size.height = MAX(frame.size.height, 22); + _overlayViewContents.frame = frame; + frame.origin = (CGPoint){8, 8}; + frame.size.width += 16; + frame.size.height += 16; + _overlayView.frame = frame; _overlayView.alpha = 1.0; } @@ -418,6 +459,9 @@ static GB_key_mask_t angleToKeyMask(double angle) - (void)turboSwipe { _screenSwiped = true; + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBDynamicSpeed"]) { + _inDynamicSpeedMode = true; + } [self displayOverlayWithImage:@"forward" orTitle:@"Fast-forwarding…"]; self.viewController.runMode = GBRunModeTurbo; } @@ -425,6 +469,9 @@ static GB_key_mask_t angleToKeyMask(double angle) - (void)rewindSwipe { _screenSwiped = true; + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBDynamicSpeed"]) { + _inDynamicSpeedMode = true; + } [self displayOverlayWithImage:@"backward" orTitle:@"Rewinding…"]; self.viewController.runMode = GBRunModeRewind; } @@ -437,7 +484,9 @@ static GB_key_mask_t angleToKeyMask(double angle) - (void)saveSwipe { _screenSwiped = true; + self.viewController.runMode = GBRunModeNormal; if (![[NSUserDefaults standardUserDefaults] boolForKey:@"GBSwipeState"]) { + [self fadeOverlayOut]; return; } [self displayOverlayWithImage:@"square.and.arrow.down" orTitle:@"Saved state to Slot 1"]; @@ -452,7 +501,9 @@ static GB_key_mask_t angleToKeyMask(double angle) - (void)loadSwipe { _screenSwiped = true; + self.viewController.runMode = GBRunModeNormal; if (![[NSUserDefaults standardUserDefaults] boolForKey:@"GBSwipeState"]) { + [self fadeOverlayOut]; return; } [self displayOverlayWithImage:@"square.and.arrow.up" orTitle:@"Loaded state from Slot 1"]; diff --git a/iOS/GBSettingsViewController.m b/iOS/GBSettingsViewController.m index ecd8982..bf4b9de 100644 --- a/iOS/GBSettingsViewController.m +++ b/iOS/GBSettingsViewController.m @@ -136,7 +136,13 @@ static NSString const *typeLightTemp = @"typeLightTemp"; @{@"type": typeRadio, @"pref": @"GBTurboSpeed", @"title": @"200%", @"value": @2,}, @{@"type": typeRadio, @"pref": @"GBTurboSpeed", @"title": @"400%", @"value": @4,}, @{@"type": typeRadio, @"pref": @"GBTurboSpeed", @"title": @"Uncapped", @"value": @1,}, - ] + ], + @"footer": ^NSString *(){ + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBDynamicSpeed"]) { + return @"This setting will have no effect because horizontal swipes are configured to dynamically control speed in the “Controls” settings"; + } + return @""; + }, }, @{ @"header": @"Rewind Speed", @@ -144,7 +150,13 @@ static NSString const *typeLightTemp = @"typeLightTemp"; @{@"type": typeRadio, @"pref": @"GBRewindSpeed", @"title": @"100%", @"value": @1,}, @{@"type": typeRadio, @"pref": @"GBRewindSpeed", @"title": @"200%", @"value": @2,}, @{@"type": typeRadio, @"pref": @"GBRewindSpeed", @"title": @"400%", @"value": @4,}, - ] + ], + @"footer": ^NSString *(){ + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBDynamicSpeed"]) { + return @"This setting will have no effect because horizontal swipes are configured to dynamically control speed in the “Controls” settings"; + } + return @""; + } }, @{ @"header": @"Emulated Revisions", @@ -310,15 +322,23 @@ static NSString const *typeLightTemp = @"typeLightTemp"; }, }, @{ - @"header": @"Fast-forward and Rewind Behavior", + @"header": @"Horizontal Swipe Behavior", @"items": @[ + @{@"type": typeCheck, @"pref": @"GBDynamicSpeed", @"title": @"Dynamically Control Speed"}, @{@"type": typeCheck, @"pref": @"GBSwipeLock", @"title": @"Lock After Swiping"}, ], @"footer": ^NSString *(){ if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBSwipeLock"]) { - return @"Swipe right on the Game Boy screen to fast-forward, and swipe left to rewind. Tap on the Game Boy screen to return to normal."; + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBDynamicSpeed"]) { + return @"Swipe right on the Game Boy screen to play forward, and swipe left to rewind. Tap on the Game Boy screen to return to normal. The forward and rewind speeds are determinied by the swipe distance."; + } + return @"Swipe right on the Game Boy screen to fast-forward, and swipe left to rewind. Tap on the Game Boy screen to return to normal. The turbo and rewind speeds can be configured under “Emulation” settings."; } - return @"Swipe right on the Game Boy screen to fast-forward, and swipe left to rewind. Raise the touch to return to normal."; + + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBDynamicSpeed"]) { + return @"Swipe right on the Game Boy screen to play forward, and swipe left to rewind. Raise the touch to return to normal. The forward and rewind speeds are determinied by the swipe distance."; + } + return @"Swipe right on the Game Boy screen to fast-forward, and swipe left to rewind. Raise the touch to return to normal. The turbo and rewind speeds can be configured under “Emulation” settings."; }, }, @{ diff --git a/iOS/GBViewController.h b/iOS/GBViewController.h index 478ff6f..133bda4 100644 --- a/iOS/GBViewController.h +++ b/iOS/GBViewController.h @@ -5,7 +5,7 @@ typedef enum { GBRunModeNormal, GBRunModeTurbo, GBRunModeRewind, - GBRunModeRewindPaused, + GBRunModePaused, } GBRunMode; @interface GBViewController : UIViewController diff --git a/iOS/GBViewController.m b/iOS/GBViewController.m index 74e39f2..9ddf474 100644 --- a/iOS/GBViewController.m +++ b/iOS/GBViewController.m @@ -22,6 +22,8 @@ volatile bool _running; volatile bool _stopping; + bool _rewind; + bool _rewindOver; bool _romLoaded; UIInterfaceOrientation _orientation; @@ -38,7 +40,6 @@ NSMutableSet *_defaultsObservers; GB_palette_t _palette; - bool _rewind; CMMotionManager *_motionManager; CVImageBufferRef _cameraImage; @@ -540,13 +541,22 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp) if (_rewind) { _rewind = false; GB_rewind_pop(&_gb); - for (unsigned i = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBRewindSpeed"]; i--;) { + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBDynamicSpeed"]) { if (!GB_rewind_pop(&_gb)) { - self.runMode = GBRunModeRewindPaused; + self.runMode = GBRunModePaused; + _rewindOver = true; + } + } + else { + for (unsigned i = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBRewindSpeed"]; i--;) { + if (!GB_rewind_pop(&_gb)) { + self.runMode = GBRunModePaused; + _rewindOver = true; + } } } } - if (_runMode != GBRunModeRewindPaused) { + if (_runMode != GBRunModePaused) { GB_run(&_gb); } } @@ -705,23 +715,32 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp) - (void)setRunMode:(GBRunMode)runMode { + if (runMode == GBRunModeRewind && _rewindOver) { + runMode = GBRunModePaused; + } if (runMode == _runMode) return; - if (_runMode == GBRunModeRewindPaused) { + if (_runMode == GBRunModePaused) { [_audioClient start]; } _runMode = runMode; - if (_runMode == GBRunModeRewindPaused) { + if (_runMode == GBRunModePaused) { [_audioClient stop]; } - if (_runMode == GBRunModeTurbo) { - double multiplier = [[NSUserDefaults standardUserDefaults] doubleForKey:@"GBTurboSpeed"]; - GB_set_turbo_mode(&_gb, multiplier == 1, false); - GB_set_clock_multiplier(&_gb, multiplier); + if (_runMode == GBRunModeNormal || _runMode == GBRunModeTurbo) { + _rewindOver = false; } - else { - GB_set_turbo_mode(&_gb, false, false); - GB_set_clock_multiplier(&_gb, 1.0); + + if (_runMode == GBRunModeNormal || [[NSUserDefaults standardUserDefaults] boolForKey:@"GBDynamicSpeed"]) { + if (_runMode == GBRunModeTurbo) { + double multiplier = [[NSUserDefaults standardUserDefaults] doubleForKey:@"GBTurboSpeed"]; + GB_set_turbo_mode(&_gb, multiplier == 1, false); + GB_set_clock_multiplier(&_gb, multiplier); + } + else { + GB_set_turbo_mode(&_gb, false, false); + GB_set_clock_multiplier(&_gb, 1.0); + } } } diff --git a/iOS/main.m b/iOS/main.m index 793390e..017cc02 100644 --- a/iOS/main.m +++ b/iOS/main.m @@ -21,6 +21,7 @@ int main(int argc, char * argv[]) @"GBButtonHaptics": @YES, @"GBTurboSpeed": @1, @"GBRewindSpeed": @1, + @"GBDynamicSpeed": @NO, @"GBCurrentTheme": @"Lime (Game Boy)", // Default themes