From 3e75016fe08a1a2817d82b0dedeee8c98c71a6a0 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 14 Jan 2023 15:02:34 +0200 Subject: [PATCH] Implement controls --- iOS/GBBackgroundView.h | 8 ++ iOS/GBBackgroundView.m | 165 +++++++++++++++++++++++++++++++++++++++++ iOS/GBViewController.m | 65 +++------------- 3 files changed, 182 insertions(+), 56 deletions(-) create mode 100644 iOS/GBBackgroundView.h create mode 100644 iOS/GBBackgroundView.m diff --git a/iOS/GBBackgroundView.h b/iOS/GBBackgroundView.h new file mode 100644 index 0000000..dcad561 --- /dev/null +++ b/iOS/GBBackgroundView.h @@ -0,0 +1,8 @@ +#import +#import "GBLayout.h" +#import "GBView.h" + +@interface GBBackgroundView : UIImageView +@property (readonly) GBView *gbView; +@property (nonatomic) GBLayout *layout; +@end diff --git a/iOS/GBBackgroundView.m b/iOS/GBBackgroundView.m new file mode 100644 index 0000000..81d9cfa --- /dev/null +++ b/iOS/GBBackgroundView.m @@ -0,0 +1,165 @@ +#import "GBBackgroundView.h" +#import "GBViewMetal.h" + +double CGPointSquaredDistance(CGPoint a, CGPoint b) +{ + double deltaX = a.x - b.x; + double deltaY = a.y - b.y; + return deltaX * deltaX + deltaY * deltaY; +} + +double CGPointAngle(CGPoint a, CGPoint b) +{ + double deltaX = a.x - b.x; + double deltaY = a.y - b.y; + return atan2(deltaY, deltaX); +} + +static void positionView(UIImageView *view, CGPoint position) +{ + double center = view.image.size.width / 2 * [UIScreen mainScreen].scale; + view.frame = (CGRect){ + { + round(position.x - center) / [UIScreen mainScreen].scale, + round(position.y - center) / [UIScreen mainScreen].scale + }, + view.image.size + }; +} + +@implementation GBBackgroundView +{ + NSMutableSet *_touches; + UIImageView *_dpadView; + UIImageView *_aButtonView; + UIImageView *_bButtonView; + UIImageView *_startButtonView; + UIImageView *_selectButtonView; +} + +- (instancetype)init +{ + self = [super initWithImage:nil]; + if (!self) return nil; + _touches = [NSMutableSet set]; + _dpadView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"dpad"]]; + _aButtonView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"button"]]; + _bButtonView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"button"]]; + _startButtonView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"button2"]]; + _selectButtonView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"button2"]]; + _gbView = [[GBViewMetal alloc] initWithFrame:CGRectZero]; + + [self addSubview:_dpadView]; + [self addSubview:_aButtonView]; + [self addSubview:_bButtonView]; + [self addSubview:_startButtonView]; + [self addSubview:_selectButtonView]; + [self addSubview:_gbView]; + return self; +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + [_touches unionSet:touches]; + [self touchesChanged]; +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + [_touches minusSet:touches]; + [self touchesChanged]; +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event +{ + [_touches minusSet:touches]; + [self touchesChanged]; +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +{ + [self touchesChanged]; +} + +- (void)touchesChanged +{ + if (!GB_is_inited(_gbView.gb)) return; + GB_key_mask_t mask = 0; + double factor = [UIScreen mainScreen].scale; + double buttonRadiusSquared = 36 * 36 * factor * factor; + double dpadRadiusSquared = 75 * 75 * factor * factor; + for (UITouch *touch in _touches) { + CGPoint point = [touch locationInView:self]; + point.x *= factor; + point.y *= factor; + if (CGPointSquaredDistance(point, _layout.aLocation) <= buttonRadiusSquared) { + mask |= GB_KEY_A_MASK; + } + else if (CGPointSquaredDistance(point, _layout.bLocation) <= buttonRadiusSquared) { + mask |= GB_KEY_B_MASK; + } + else if (CGPointSquaredDistance(point, _layout.startLocation) <= buttonRadiusSquared) { + mask |= GB_KEY_START_MASK; + } + else if (CGPointSquaredDistance(point, _layout.selectLocation) <= buttonRadiusSquared) { + mask |= GB_KEY_SELECT_MASK; + } + else if (CGPointSquaredDistance(point, _layout.dpadLocation) <= dpadRadiusSquared) { + double angle = CGPointAngle(point, _layout.dpadLocation); + signed quantizedAngle = round(angle / M_PI * 6); + if (quantizedAngle < 0) { + quantizedAngle += 12; + } + switch (quantizedAngle) { + case 0 : mask |= GB_KEY_RIGHT_MASK; break; + case 1 : mask |= GB_KEY_RIGHT_MASK | GB_KEY_DOWN_MASK; break; + case 2 : mask |= GB_KEY_DOWN_MASK; break; + + case 3 : mask |= GB_KEY_DOWN_MASK; break; + case 4 : mask |= GB_KEY_LEFT_MASK | GB_KEY_DOWN_MASK; break; + case 6 : mask |= GB_KEY_LEFT_MASK; break; + + case 7 : mask |= GB_KEY_LEFT_MASK; break; + case 8 : mask |= GB_KEY_LEFT_MASK | GB_KEY_UP_MASK; break; + case 9 : mask |= GB_KEY_UP_MASK; break; + + case 10: mask |= GB_KEY_UP_MASK; break; + case 11: mask |= GB_KEY_RIGHT_MASK | GB_KEY_UP_MASK; break; + case 12: mask |= GB_KEY_RIGHT_MASK; break; + } + } + } + GB_set_key_mask(_gbView.gb, mask); +} + +- (BOOL)isMultipleTouchEnabled +{ + return true; +} + +- (BOOL)isUserInteractionEnabled +{ + return true; +} + +- (void)setLayout:(GBLayout *)layout +{ + _layout = layout; + self.image = layout.background; + + positionView(_dpadView, layout.dpadLocation); + positionView(_aButtonView, layout.aLocation); + positionView(_bButtonView, layout.bLocation); + positionView(_startButtonView, layout.startLocation); + positionView(_selectButtonView, layout.selectLocation); + + CGRect screenFrame = layout.screenRect; + screenFrame.origin.x /= [UIScreen mainScreen].scale; + screenFrame.origin.y /= [UIScreen mainScreen].scale; + screenFrame.size.width /= [UIScreen mainScreen].scale; + screenFrame.size.height /= [UIScreen mainScreen].scale; + + _gbView.frame = screenFrame; +} + +@end diff --git a/iOS/GBViewController.m b/iOS/GBViewController.m index 09dd436..19c0677 100644 --- a/iOS/GBViewController.m +++ b/iOS/GBViewController.m @@ -5,6 +5,7 @@ #import "GBAudioClient.h" #import "GBROMManager.h" #import "GBLoadROMTableViewController.h" +#import "GBBackgroundView.h" #include @implementation GBViewController @@ -14,15 +15,9 @@ volatile bool _running; volatile bool _stopping; bool _romLoaded; - GBLayout *_currentLayout; GBHorizontalLayout *_horizontalLayout; GBVerticalLayout *_verticalLayout; - UIImageView *_backgroundView; - UIImageView *_dpadView; - UIImageView *_aButtonView; - UIImageView *_bButtonView; - UIImageView *_startButtonView; - UIImageView *_selectButtonView; + GBBackgroundView *_backgroundView; NSCondition *_audioLock; GB_sample_t *_audioBuffer; size_t _audioBufferSize; @@ -31,19 +26,6 @@ GBAudioClient *_audioClient; } -static void positionView(UIImageView *view, CGPoint position) -{ - double center = view.image.size.width / 2 * [UIScreen mainScreen].scale; - view.frame = (CGRect){ - { - round(position.x - center) / [UIScreen mainScreen].scale, - round(position.y - center) / [UIScreen mainScreen].scale - }, - view.image.size - }; - -} - static void loadBootROM(GB_gameboy_t *gb, GB_boot_rom_t type) { GBViewController *self = (__bridge GBViewController *)GB_get_user_data(gb); @@ -114,30 +96,19 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp) _horizontalLayout = [[GBHorizontalLayout alloc] init]; _verticalLayout = [[GBVerticalLayout alloc] init]; - _backgroundView = [[UIImageView alloc] initWithImage:nil]; + _backgroundView = [[GBBackgroundView alloc] init]; [_window addSubview:_backgroundView]; self.view = _backgroundView; - _dpadView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"dpad"]]; - _aButtonView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"button"]]; - _bButtonView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"button"]]; - _startButtonView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"button2"]]; - _selectButtonView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"button2"]]; - _gbView = [[GBViewMetal alloc] initWithFrame:CGRectZero]; [self initGameBoy]; + _gbView = _backgroundView.gbView; _gbView.gb = &_gb; [_gbView screenSizeChanged]; [self willRotateToInterfaceOrientation:[UIApplication sharedApplication].statusBarOrientation duration:0]; - - [_backgroundView addSubview:_dpadView]; - [_backgroundView addSubview:_aButtonView]; - [_backgroundView addSubview:_bButtonView]; - [_backgroundView addSubview:_startButtonView]; - [_backgroundView addSubview:_selectButtonView]; - [_backgroundView addSubview:_gbView]; + _audioLock = [[NSCondition alloc] init]; @@ -179,30 +150,12 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp) - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration { + GBLayout *layout = _horizontalLayout; if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) { - _currentLayout = _verticalLayout; + layout = _verticalLayout; } - else { - _currentLayout = _horizontalLayout; - } - - _backgroundView.image = _currentLayout.background; - _backgroundView.frame = [_currentLayout viewRectForOrientation:[UIApplication sharedApplication].statusBarOrientation]; - - positionView(_dpadView, _currentLayout.dpadLocation); - positionView(_aButtonView, _currentLayout.aLocation); - positionView(_bButtonView, _currentLayout.bLocation); - positionView(_startButtonView, _currentLayout.startLocation); - positionView(_selectButtonView, _currentLayout.selectLocation); - - CGRect screenFrame = _currentLayout.screenRect; - screenFrame.origin.x /= [UIScreen mainScreen].scale; - screenFrame.origin.y /= [UIScreen mainScreen].scale; - screenFrame.size.width /= [UIScreen mainScreen].scale; - screenFrame.size.height /= [UIScreen mainScreen].scale; - - _gbView.frame = screenFrame; - [_gbView flip]; + _backgroundView.frame = [layout viewRectForOrientation:orientation]; + _backgroundView.layout = layout; } - (BOOL)prefersHomeIndicatorAutoHidden