Dynamic rewind/forward speed in iOS

This commit is contained in:
Lior Halphon 2023-02-12 18:25:33 +02:00
parent ef8f3aff66
commit 42ce9c410b
5 changed files with 115 additions and 24 deletions

View File

@ -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"];

View File

@ -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.";
},
},
@{

View File

@ -5,7 +5,7 @@ typedef enum {
GBRunModeNormal,
GBRunModeTurbo,
GBRunModeRewind,
GBRunModeRewindPaused,
GBRunModePaused,
} GBRunMode;
@interface GBViewController : UIViewController <UIApplicationDelegate, AVCaptureVideoDataOutputSampleBufferDelegate>

View File

@ -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);
}
}
}

View File

@ -21,6 +21,7 @@ int main(int argc, char * argv[])
@"GBButtonHaptics": @YES,
@"GBTurboSpeed": @1,
@"GBRewindSpeed": @1,
@"GBDynamicSpeed": @NO,
@"GBCurrentTheme": @"Lime (Game Boy)",
// Default themes