On-screen controls revamp
- controls are bigger - dead zone around buttons to avoid accidental mouse clicks and movement - finished implementing control setup for AutoGCGamepad - swiping to close stream requires swipe to 1/4 the screen width - no longer send mouse movement if delta is too small
@@ -2,18 +2,17 @@
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "a_button_40x40@1x.png"
|
||||
"filename" : "a_button_80x80@2x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "a_button_80x80@2x.png"
|
||||
"filename" : "a_button_120x120@3x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "a_button_120x120@3x.png"
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 2.2 KiB |
@@ -2,18 +2,17 @@
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "b_button_40x40@1x.png"
|
||||
"filename" : "b_button_80x80@2x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "b_button_80x80@2x.png"
|
||||
"filename" : "b_button_120x120@3x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "b_button_120x120@3x.png"
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 2.1 KiB |
@@ -2,18 +2,17 @@
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "down_31x38@1x.png"
|
||||
"filename" : "down_61x75@2x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "down_61x75@2x.png"
|
||||
"filename" : "down_93x114@3x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "down_93x114@3x.png"
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -2,18 +2,17 @@
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "left_38x31@1x.png"
|
||||
"filename" : "left_76x61@2x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "left_76x61@2x.png"
|
||||
"filename" : "left_114x92@3x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "left_114x92@3x.png"
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -2,18 +2,17 @@
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "right_38x31@1x.png"
|
||||
"filename" : "right_75x61@2x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "right_75x61@2x.png"
|
||||
"filename" : "right_114x93@3x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "right_114x93@3x.png"
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -2,18 +2,17 @@
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "up_31x39@1x.png"
|
||||
"filename" : "up_61x76@2x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "up_61x76@2x.png"
|
||||
"filename" : "up_93x116@3x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "up_93x116@3x.png"
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -2,18 +2,17 @@
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "x_button_40x40@1x.png"
|
||||
"filename" : "x_button_80x80@2x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "x_button_80x80@2x.png"
|
||||
"filename" : "x_button_120x120@3x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "x_button_120x120@3x.png"
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 2.3 KiB |
@@ -2,18 +2,17 @@
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "y_button_40x40@1x.png"
|
||||
"filename" : "y_button_80x80@2x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "y_button_80x80@2x.png"
|
||||
"filename" : "y_button_120x120@3x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "y_button_120x120@3x.png"
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 2.1 KiB |
@@ -71,6 +71,7 @@
|
||||
ControllerSupport *_controllerSupport;
|
||||
Controller *_controller;
|
||||
id<EdgeDetectionDelegate> _edgeDelegate;
|
||||
NSMutableArray* _deadTouches;
|
||||
}
|
||||
|
||||
static const float EDGE_WIDTH = .05;
|
||||
@@ -80,10 +81,12 @@ static const float BUTTON_DIST = 20;
|
||||
static float BUTTON_CENTER_X;
|
||||
static float BUTTON_CENTER_Y;
|
||||
|
||||
static const float D_PAD_DIST = 15;
|
||||
static const float D_PAD_DIST = 10;
|
||||
static float D_PAD_CENTER_X;
|
||||
static float D_PAD_CENTER_Y;
|
||||
|
||||
static const float DEAD_ZONE_PADDING = 15;
|
||||
|
||||
static const double STICK_CLICK_RATE = 100;
|
||||
static const float STICK_DEAD_ZONE = .1;
|
||||
static float STICK_INNER_SIZE;
|
||||
@@ -119,6 +122,7 @@ static float L3_Y;
|
||||
_controller = [[Controller alloc] init];
|
||||
_controller.playerIndex = 0;
|
||||
_edgeDelegate = swipeDelegate;
|
||||
_deadTouches = [[NSMutableArray alloc] init];
|
||||
|
||||
_iPad = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad);
|
||||
_controlArea = CGRectMake(0, 0, _view.frame.size.width, _view.frame.size.height);
|
||||
@@ -183,10 +187,10 @@ static float L3_Y;
|
||||
|
||||
[self hideButtons];
|
||||
[self hideBumpers];
|
||||
[self hideL3R3];
|
||||
[self drawTriggers];
|
||||
[self drawStartSelect];
|
||||
[self drawSticks];
|
||||
[self drawL3R3];
|
||||
break;
|
||||
case OnScreenControlsLevelAutoGCExtendedGamepad:
|
||||
// GCExtendedGamepad is missing R3, L3, and select
|
||||
@@ -245,7 +249,42 @@ static float L3_Y;
|
||||
// Start with the default complex layout
|
||||
[self setupComplexControls];
|
||||
|
||||
// TODO
|
||||
START_X = _controlArea.size.width * .6 + _controlArea.origin.x;
|
||||
START_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
|
||||
SELECT_X = _controlArea.size.width * .4 + _controlArea.origin.x;
|
||||
SELECT_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
|
||||
L2_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
L2_X = _controlArea.size.width * .1 + _controlArea.origin.x;
|
||||
|
||||
R2_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
R2_X = _controlArea.size.width * .9 + _controlArea.origin.x;
|
||||
|
||||
if (_iPad) {
|
||||
START_X = _controlArea.size.width * .75 + _controlArea.origin.x;
|
||||
START_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
|
||||
SELECT_X = _controlArea.size.width * .25 + _controlArea.origin.x;
|
||||
SELECT_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
|
||||
// The analog sticks are kept closer to the sides on iPad
|
||||
LS_CENTER_X = _controlArea.size.width * .18 + _controlArea.origin.x;
|
||||
LS_CENTER_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
|
||||
RS_CENTER_X = _controlArea.size.width * .82 + _controlArea.origin.x;
|
||||
RS_CENTER_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
|
||||
} else {
|
||||
START_X = _controlArea.size.width * .6 + _controlArea.origin.x;
|
||||
START_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
|
||||
SELECT_X = _controlArea.size.width * .4 + _controlArea.origin.x;
|
||||
SELECT_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
|
||||
LS_CENTER_X = _controlArea.size.width * .25 + _controlArea.origin.x;
|
||||
LS_CENTER_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
|
||||
RS_CENTER_X = _controlArea.size.width * .75 + _controlArea.origin.x;
|
||||
RS_CENTER_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
|
||||
}
|
||||
}
|
||||
|
||||
// For simple controls we move the triggers and buttons to the bottom
|
||||
@@ -253,13 +292,15 @@ static float L3_Y;
|
||||
// Start with the default complex layout
|
||||
[self setupComplexControls];
|
||||
|
||||
START_X = _controlArea.size.width * .6 + _controlArea.origin.x;
|
||||
START_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
|
||||
SELECT_X = _controlArea.size.width * .4 + _controlArea.origin.x;
|
||||
SELECT_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
|
||||
L1_Y = _controlArea.size.height * .7 + _controlArea.origin.y;
|
||||
L2_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
L2_X = _controlArea.size.width * .1 + _controlArea.origin.x;
|
||||
R1_Y = _controlArea.size.height * .7 + _controlArea.origin.y;
|
||||
|
||||
R2_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
R2_X = _controlArea.size.width * .9 + _controlArea.origin.x;
|
||||
}
|
||||
@@ -267,9 +308,9 @@ static float L3_Y;
|
||||
- (void) setupComplexControls
|
||||
{
|
||||
D_PAD_CENTER_X = _controlArea.size.width * .1 + _controlArea.origin.x;
|
||||
D_PAD_CENTER_Y = _controlArea.size.height * .55 + _controlArea.origin.y;
|
||||
D_PAD_CENTER_Y = _controlArea.size.height * .60 + _controlArea.origin.y;
|
||||
BUTTON_CENTER_X = _controlArea.size.width * .9 + _controlArea.origin.x;
|
||||
BUTTON_CENTER_Y = _controlArea.size.height * .55 + _controlArea.origin.y;
|
||||
BUTTON_CENTER_Y = _controlArea.size.height * .60 + _controlArea.origin.y;
|
||||
|
||||
if (_iPad)
|
||||
{
|
||||
@@ -287,9 +328,9 @@ static float L3_Y;
|
||||
RS_CENTER_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
|
||||
}
|
||||
|
||||
START_X = _controlArea.size.width * .1 + _controlArea.origin.x;
|
||||
START_X = _controlArea.size.width * .9 + _controlArea.origin.x;
|
||||
START_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
SELECT_X = _controlArea.size.width * .9 + _controlArea.origin.x;
|
||||
SELECT_X = _controlArea.size.width * .1 + _controlArea.origin.x;
|
||||
SELECT_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
|
||||
L1_X = _controlArea.size.width * .1 + _controlArea.origin.x;
|
||||
@@ -562,11 +603,14 @@ static float L3_Y;
|
||||
} else if (touch == _r3Touch) {
|
||||
buttonTouch = true;
|
||||
}
|
||||
if ([_deadTouches containsObject:touch]) {
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
if (updated) {
|
||||
[_controllerSupport updateFinished:_controller];
|
||||
}
|
||||
return updated || buttonTouch;
|
||||
return updated || buttonTouch;
|
||||
}
|
||||
|
||||
- (BOOL)handleTouchDownEvent:touches {
|
||||
@@ -680,6 +724,10 @@ static float L3_Y;
|
||||
} else if ([_edge.presentationLayer hitTest:touchLocation]) {
|
||||
_edgeTouch = touch;
|
||||
}
|
||||
if (!updated && !stickTouch && [self isInDeadZone:touch]) {
|
||||
[_deadTouches addObject:touch];
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
if (updated) {
|
||||
[_controllerSupport updateFinished:_controller];
|
||||
@@ -771,15 +819,123 @@ static float L3_Y;
|
||||
touched = true;
|
||||
} else if (touch == _edgeTouch) {
|
||||
_edgeTouch = nil;
|
||||
if (![_edge.presentationLayer hitTest:[touch locationInView:_view]]) {
|
||||
if ([touch locationInView:_view].x >= _view.frame.size.width / 4) {
|
||||
[_edgeDelegate edgeSwiped];
|
||||
}
|
||||
}
|
||||
if ([_deadTouches containsObject:touch]) {
|
||||
[_deadTouches removeObject:touch];
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
if (updated) {
|
||||
[_controllerSupport updateFinished:_controller];
|
||||
}
|
||||
|
||||
return updated || touched;
|
||||
}
|
||||
|
||||
- (BOOL) isInDeadZone:(UITouch*) touch {
|
||||
switch (_level) {
|
||||
case OnScreenControlsLevelFull:
|
||||
return [self isDpadDeadZone:touch]
|
||||
|| [self isAbxyDeadZone:touch]
|
||||
|| [self isTriggerDeadZone:touch]
|
||||
|| [self isBumperDeadZone:touch]
|
||||
|| [self isStartSelectDeadZone:touch];
|
||||
case OnScreenControlsLevelSimple:
|
||||
return [self isTriggerDeadZone:touch]
|
||||
|| [self isStartSelectDeadZone:touch]
|
||||
|| [self isL3R3DeadZone:touch];
|
||||
case OnScreenControlsLevelAutoGCExtendedGamepad:
|
||||
return [self isL3R3DeadZone:touch]
|
||||
|| [self isStartSelectDeadZone:touch];
|
||||
case OnScreenControlsLevelAutoGCGamepad:
|
||||
return [self isTriggerDeadZone:touch]
|
||||
|| [self isStartSelectDeadZone:touch];
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL) isDpadDeadZone:(UITouch*) touch {
|
||||
return [self isDeadZone:touch
|
||||
startX:_view.frame.origin.x
|
||||
startY:_upButton.frame.origin.y
|
||||
endX:_rightButton.frame.origin.x + _rightButton.frame.size.width
|
||||
endY:_view.frame.origin.y + _view.frame.size.height];
|
||||
}
|
||||
|
||||
- (BOOL) isAbxyDeadZone:(UITouch*) touch {
|
||||
return [self isDeadZone:touch
|
||||
startX:_xButton.frame.origin.x
|
||||
startY:_yButton.frame.origin.y
|
||||
endX:_view.frame.origin.x + _view.frame.size.width
|
||||
endY:_view.frame.origin.y + _view.frame.size.height];
|
||||
}
|
||||
|
||||
- (BOOL) isBumperDeadZone:(UITouch*) touch {
|
||||
return [self isDeadZone:touch
|
||||
startX:_view.frame.origin.x
|
||||
startY:_l2Button.frame.origin.y + _l2Button.frame.size.height
|
||||
endX:_l1Button.frame.origin.x + _l1Button.frame.size.width
|
||||
endY:_upButton.frame.origin.y]
|
||||
|| [self isDeadZone:touch
|
||||
startX:_r2Button.frame.origin.x
|
||||
startY:_r2Button.frame.origin.y + _r2Button.frame.size.height
|
||||
endX:_view.frame.origin.x + _view.frame.size.width
|
||||
endY:_yButton.frame.origin.y];
|
||||
}
|
||||
|
||||
- (BOOL) isTriggerDeadZone:(UITouch*) touch {
|
||||
return [self isDeadZone:touch
|
||||
startX:_view.frame.origin.x
|
||||
startY:_l2Button.frame.origin.y
|
||||
endX:_l2Button.frame.origin.x + _l2Button.frame.size.width
|
||||
endY:_view.frame.origin.y + _view.frame.size.height]
|
||||
|| [self isDeadZone:touch
|
||||
startX:_r2Button.frame.origin.x
|
||||
startY:_r2Button.frame.origin.y
|
||||
endX:_view.frame.origin.x + _view.frame.size.width
|
||||
endY:_view.frame.origin.y + _view.frame.size.height];
|
||||
}
|
||||
|
||||
- (BOOL) isL3R3DeadZone:(UITouch*) touch {
|
||||
return [self isDeadZone:touch
|
||||
startX:_view.frame.origin.x
|
||||
startY:_l3Button.frame.origin.y
|
||||
endX:_view.frame.origin.x
|
||||
endY:_view.frame.origin.y + _view.frame.size.height]
|
||||
|| [self isDeadZone:touch
|
||||
startX:_r3Button.frame.origin.x
|
||||
startY:_r3Button.frame.origin.y
|
||||
endX:_view.frame.origin.x + _view.frame.size.width
|
||||
endY:_view.frame.origin.y + _view.frame.size.height];
|
||||
}
|
||||
|
||||
- (BOOL) isStartSelectDeadZone:(UITouch*) touch {
|
||||
return [self isDeadZone:touch
|
||||
startX:_startButton.frame.origin.x
|
||||
startY:_startButton.frame.origin.y
|
||||
endX:_view.frame.origin.x + _view.frame.size.width
|
||||
endY:_view.frame.origin.y + _view.frame.size.height]
|
||||
|| [self isDeadZone:touch
|
||||
startX:_view.frame.origin.x
|
||||
startY:_selectButton.frame.origin.y
|
||||
endX:_selectButton.frame.origin.x + _selectButton.frame.size.width
|
||||
endY:_view.frame.origin.y + _view.frame.size.height];
|
||||
}
|
||||
|
||||
- (BOOL) isDeadZone:(UITouch*) touch startX:(float)deadZoneStartX startY:(float)deadZoneStartY endX:(float)deadZoneEndX endY:(float)deadZoneEndY {
|
||||
deadZoneStartX -= DEAD_ZONE_PADDING;
|
||||
deadZoneStartY -= DEAD_ZONE_PADDING;
|
||||
deadZoneEndX += DEAD_ZONE_PADDING;
|
||||
deadZoneEndY += DEAD_ZONE_PADDING;
|
||||
|
||||
CGPoint touchLocation = [touch locationInView:_view];
|
||||
return (touchLocation.x > deadZoneStartX && touchLocation.x < deadZoneEndX
|
||||
&& touchLocation.y > deadZoneStartY && touchLocation.y < deadZoneEndY);
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -67,10 +67,11 @@
|
||||
deltaX *= xDeltaFactor * screenFactor;
|
||||
deltaY *= yDeltaFactor * screenFactor;
|
||||
|
||||
LiSendMouseMoveEvent(deltaX, deltaY);
|
||||
|
||||
touchMoved = true;
|
||||
touchLocation = currentLocation;
|
||||
if (deltaX != 0 || deltaY != 0) {
|
||||
LiSendMouseMoveEvent(deltaX, deltaY);
|
||||
touchMoved = true;
|
||||
touchLocation = currentLocation;
|
||||
}
|
||||
}
|
||||
} else if ([[event allTouches] count] == 2) {
|
||||
CGPoint firstLocation = [[[[event allTouches] allObjects] objectAtIndex:0] locationInView:self];
|
||||
|
||||
@@ -1004,7 +1004,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "Limelight/Limelight-Prefix.pch";
|
||||
|
||||