Swipe controls option, improve dpad angle thresholds

This commit is contained in:
Lior Halphon 2023-01-24 22:45:51 +02:00
parent 413212f993
commit defd8d4f69
11 changed files with 120 additions and 35 deletions

View File

@ -5,4 +5,5 @@
@interface GBBackgroundView : UIImageView
@property (readonly) GBView *gbView;
@property (nonatomic) GBLayout *layout;
@property (nonatomic) bool usesSwipePad;
@end

View File

@ -29,9 +29,59 @@ static void positionView(UIImageView *view, CGPoint position)
};
}
static GB_key_mask_t angleToKeyMask(double angle)
{
signed quantizedAngle = round(angle / M_PI * 16);
if (quantizedAngle < 0) {
quantizedAngle += 32;
}
switch (quantizedAngle) {
case 32:
case 0: return GB_KEY_RIGHT_MASK;
case 1: return GB_KEY_RIGHT_MASK;
case 2: return GB_KEY_RIGHT_MASK;
case 3: return GB_KEY_RIGHT_MASK | GB_KEY_DOWN_MASK;
case 4: return GB_KEY_RIGHT_MASK | GB_KEY_DOWN_MASK;
case 5: return GB_KEY_DOWN_MASK;
case 6: return GB_KEY_DOWN_MASK;
case 7: return GB_KEY_DOWN_MASK;
case 8: return GB_KEY_DOWN_MASK;
case 9: return GB_KEY_DOWN_MASK;
case 10: return GB_KEY_DOWN_MASK;
case 11: return GB_KEY_LEFT_MASK | GB_KEY_DOWN_MASK;
case 12: return GB_KEY_LEFT_MASK | GB_KEY_DOWN_MASK;
case 13: return GB_KEY_LEFT_MASK;
case 14: return GB_KEY_LEFT_MASK;
case 15: return GB_KEY_LEFT_MASK;
case 16: return GB_KEY_LEFT_MASK;
case 17: return GB_KEY_LEFT_MASK;
case 18: return GB_KEY_LEFT_MASK;
case 19: return GB_KEY_LEFT_MASK | GB_KEY_UP_MASK;
case 20: return GB_KEY_LEFT_MASK | GB_KEY_UP_MASK;
case 21: return GB_KEY_UP_MASK;
case 22: return GB_KEY_UP_MASK;
case 23: return GB_KEY_UP_MASK;
case 24: return GB_KEY_UP_MASK;
case 25: return GB_KEY_UP_MASK;
case 26: return GB_KEY_UP_MASK;
case 27: return GB_KEY_RIGHT_MASK | GB_KEY_UP_MASK;
case 28: return GB_KEY_RIGHT_MASK | GB_KEY_UP_MASK;
case 29: return GB_KEY_RIGHT_MASK;
case 30: return GB_KEY_RIGHT_MASK;
case 31: return GB_KEY_RIGHT_MASK;
}
return 0;
}
@implementation GBBackgroundView
{
NSMutableSet<UITouch *> *_touches;
UITouch *_swipePadTouch;
CGPoint _swipeOrigin;
UIImageView *_dpadView;
UIImageView *_dpadShadowView;
UIImageView *_aButtonView;
@ -79,10 +129,24 @@ static void positionView(UIImageView *view, CGPoint position)
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
static const double dpadRadius = 75;
CGPoint dpadLocation = _layout.dpadLocation;
double factor = [UIScreen mainScreen].scale;
dpadLocation.x /= factor;
dpadLocation.y /= factor;
for (UITouch *touch in touches) {
if (CGRectContainsPoint(self.gbView.frame, [touch locationInView:self])) {
CGPoint point = [touch locationInView:self];
if (CGRectContainsPoint(self.gbView.frame, point)) {
[self.window.rootViewController presentViewController:[GBMenuViewController menu] animated:true completion:nil];
}
if (_usesSwipePad && !_swipePadTouch) {
if (fabs(point.x - dpadLocation.x) <= dpadRadius &&
fabs(point.y - dpadLocation.y) <= dpadRadius) {
_swipePadTouch = touch;
_swipeOrigin = point;
}
}
}
[_touches unionSet:touches];
[self touchesChanged];
@ -90,6 +154,9 @@ static void positionView(UIImageView *view, CGPoint position)
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
if ([touches containsObject:_swipePadTouch]) {
_swipePadTouch = nil;
}
[_touches minusSet:touches];
[self touchesChanged];
}
@ -111,9 +178,28 @@ static void positionView(UIImageView *view, CGPoint position)
GB_key_mask_t mask = 0;
double factor = [UIScreen mainScreen].scale;
double buttonRadiusSquared = 36 * 36 * factor * factor;
double dpadRadiusSquared = 75 * 75 * factor * factor;
double dpadRadius = 75 * factor;
bool dpadHandled = false;
if (_usesSwipePad) {
dpadHandled = true;
if (_swipePadTouch) {
CGPoint point = [_swipePadTouch locationInView:self];
double squaredDistance = CGPointSquaredDistance(point, _swipeOrigin);
if (squaredDistance > 16 * 16) {
double angle = CGPointAngle(point, _swipeOrigin);
mask |= angleToKeyMask(angle);
if (squaredDistance > 24 * 24) {
double deltaX = point.x - _swipeOrigin.x;
double deltaY = point.y - _swipeOrigin.y;
double distance = sqrt(squaredDistance);
_swipeOrigin.x = point.x - deltaX / distance * 24;
_swipeOrigin.y = point.y - deltaY / distance * 24;
}
}
}
}
for (UITouch *touch in _touches) {
if (touch == _swipePadTouch) continue;
CGPoint point = [touch locationInView:self];
point.x *= factor;
point.y *= factor;
@ -129,31 +215,12 @@ static void positionView(UIImageView *view, CGPoint position)
else if (CGPointSquaredDistance(point, _layout.selectLocation) <= buttonRadiusSquared) {
mask |= GB_KEY_SELECT_MASK;
}
else if (!dpadHandled && CGPointSquaredDistance(point, _layout.dpadLocation) <= dpadRadiusSquared) {
else if (!dpadHandled &&
fabs(point.x - _layout.dpadLocation.x) <= dpadRadius &&
fabs(point.y - _layout.dpadLocation.y) <= dpadRadius) {
dpadHandled = true; // Don't handle the dpad twice
double angle = CGPointAngle(point, _layout.dpadLocation);
signed quantizedAngle = round(angle / M_PI * 6);
if (quantizedAngle < 0) {
quantizedAngle += 12;
}
switch (quantizedAngle) {
case 12:
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 5 : mask |= GB_KEY_LEFT_MASK; break;
case 6 : mask |= GB_KEY_LEFT_MASK; break;
case 7 : mask |= GB_KEY_LEFT_MASK | GB_KEY_UP_MASK; break;
case 8 : mask |= GB_KEY_UP_MASK; break;
case 9: mask |= GB_KEY_UP_MASK; break;
case 10: mask |= GB_KEY_RIGHT_MASK | GB_KEY_UP_MASK; break;
case 11: mask |= GB_KEY_RIGHT_MASK; break;
}
mask |= angleToKeyMask(angle);
}
}
if (mask != _lastMask) {
@ -180,7 +247,13 @@ static void positionView(UIImageView *view, CGPoint position)
_dpadShadowView.hidden = hidden;
if (!hidden) {
_dpadShadowView.image = [UIImage imageNamed:diagonal? @"dpadShadowDiagonal" : @"dpadShadow"];
if (_usesSwipePad) {
_dpadShadowView.image = [UIImage imageNamed:diagonal? @"swipepadShadowDiagonal" : @"swipepadShadow"];
}
else {
_dpadShadowView.image = [UIImage imageNamed:diagonal? @"dpadShadowDiagonal" : @"dpadShadow"];
}
_dpadShadowView.transform = CGAffineTransformMakeRotation(rotation);
}
@ -228,4 +301,10 @@ static void positionView(UIImageView *view, CGPoint position)
_screenLabel.frame = screenFrame;
}
- (void)setUsesSwipePad:(bool)usesSwipePad
{
_usesSwipePad = usesSwipePad;
_dpadView.image = [UIImage imageNamed:usesSwipePad? @"swipepad" : @"dpad"];
}
@end

View File

@ -160,7 +160,7 @@ contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath
[self renameRow:indexPath];
}],
[UIAction actionWithTitle:@"Duplicate"
image:[UIImage systemImageNamed:@"plus.rectangle.on.rectangle"]
image:[UIImage systemImageNamed:@"plus.square.on.square"]
identifier:nil
handler:^(__kindof UIAction *action) {
[[GBROMManager sharedManager] duplicateROM:[GBROMManager sharedManager].allROMs[[indexPath indexAtPosition:1]]];

View File

@ -283,14 +283,14 @@ static NSString const *typeLightTemp = @"typeLightTemp";
@"header": @"D-pad Style",
@"items": @[
// TODO: Convert to enum when implemented
@{@"type": typeRadio, @"pref": @"GBDpadMode", @"title": @"Standard", @"value": @(0),},
@{@"type": typeRadio, @"pref": @"GBDpadMode", @"title": @"Swipe", @"value": @(1),},
@{@"type": typeRadio, @"pref": @"GBSwipeDpad", @"title": @"Standard", @"value": @NO,},
@{@"type": typeRadio, @"pref": @"GBSwipeDpad", @"title": @"Swipe", @"value": @YES,},
],
@"footer": ^NSString *(){
return (NSString * const[]){
[0] = @"Directional input is determined by the touch position.",
[1] = @"Directional input is determined by the swipe direction.",
}[MIN(1, [[NSUserDefaults standardUserDefaults] integerForKey:@"GBDpadMode"])];
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBSwipeDpad"]) {
return @"Directional input is determined by the swipe direction.";
}
return @"Directional input is determined by the touch position.";
},
},
@{

View File

@ -156,10 +156,15 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp)
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBFilterChanged" object:nil];
} forKey:@"GBFilter"];
__weak GBView *weakGBView = _gbView;
__weak GBView *gbview = _gbView;
[self addDefaultObserver:^(id newValue) {
weakGBView.frameBlendingMode = [newValue integerValue];
gbview.frameBlendingMode = [newValue integerValue];
} forKey:@"GBFrameBlendingMode"];
__weak GBBackgroundView *backgroundView = _backgroundView;
[self addDefaultObserver:^(id newValue) {
backgroundView.usesSwipePad = [newValue boolValue];
} forKey:@"GBSwipeDpad"];
[self willRotateToInterfaceOrientation:[UIApplication sharedApplication].statusBarOrientation

BIN
iOS/swipepad@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
iOS/swipepad@3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
iOS/swipepadShadow@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
iOS/swipepadShadow@3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB