diff --git a/VoidLink/Base.lproj/iPad.storyboard b/VoidLink/Base.lproj/iPad.storyboard
index f013e44d..0affe068 100644
--- a/VoidLink/Base.lproj/iPad.storyboard
+++ b/VoidLink/Base.lproj/iPad.storyboard
@@ -1385,14 +1385,14 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1892,6 +1932,8 @@
+
+
@@ -1943,6 +1985,8 @@
+
+
@@ -2501,7 +2545,7 @@
-
+
@@ -2826,7 +2870,7 @@
-
+
@@ -2835,13 +2879,13 @@
-
+
-
+
-
+
diff --git a/VoidLink/Base.lproj/iPhone.storyboard b/VoidLink/Base.lproj/iPhone.storyboard
index dc577860..08219725 100644
--- a/VoidLink/Base.lproj/iPhone.storyboard
+++ b/VoidLink/Base.lproj/iPhone.storyboard
@@ -557,7 +557,7 @@
-
+
@@ -2200,14 +2200,14 @@
-
+
-
+
@@ -2680,6 +2680,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -2750,6 +2790,8 @@
+
+
@@ -2800,6 +2842,8 @@
+
+
@@ -2868,19 +2912,19 @@
-
+
-
+
-
+
-
+
diff --git a/VoidLink/Input/ControllerSupport.m b/VoidLink/Input/ControllerSupport.m
index 31572b37..562f135d 100644
--- a/VoidLink/Input/ControllerSupport.m
+++ b/VoidLink/Input/ControllerSupport.m
@@ -67,7 +67,10 @@ static const double MOUSE_SPEED_DIVISOR = 1.25;
float _stickToMouseVelocity;
CADisplayLink *_displayLink;
-
+ float stickMaxOffset;
+ float _leftStickMinOffset;
+ float _rightStickMinOffset;
+
OnScreenControls *_osc;
VoidController *_oscController;
NSMutableSet* _activeGCControllers;
@@ -1210,13 +1213,23 @@ double rc_expo(double x, double expo) {
}
else if(buttonFlagId.intValue!=self->_controllerMouseSwitch || !self->_mapControllerToMouse) UPDATE_BUTTON_FLAG(voidController, buttonFlagId.intValue, button.pressed);
}
+
+ CGFloat leftStickXRaw = gamepad.leftThumbstick.xAxis.value * self->stickMaxOffset;
+ CGFloat leftStickYRaw = gamepad.leftThumbstick.yAxis.value * self->stickMaxOffset;
- leftStickX = self->_controllerMouseEnabledFlag ? 0 :gamepad.leftThumbstick.xAxis.value * 0x7FFE;
- leftStickY = self->_controllerMouseEnabledFlag ? 0 : gamepad.leftThumbstick.yAxis.value * 0x7FFE;
+ CGFloat rightStickXRaw = gamepad.rightThumbstick.xAxis.value * self->stickMaxOffset;
+ CGFloat rightStickYRaw = gamepad.rightThumbstick.yAxis.value * self->stickMaxOffset;
+
- rightStickX = self->_controllerMouseEnabledFlag ? 0 : gamepad.rightThumbstick.xAxis.value * 0x7FFE;
- rightStickY = self->_controllerMouseEnabledFlag ? 0 : gamepad.rightThumbstick.yAxis.value * 0x7FFE;
+ CGVector leftStickOffset = [ControllerUtil compensatedWithOffsetVector:CGVectorMake(leftStickXRaw, leftStickYRaw) withMinOffset:self->_leftStickMinOffset];
+ CGVector rightStickOffset = [ControllerUtil compensatedWithOffsetVector:CGVectorMake(rightStickXRaw, rightStickYRaw) withMinOffset:self->_rightStickMinOffset];
+
+ leftStickX = self->_controllerMouseEnabledFlag ? 0 : leftStickOffset.dx;
+ leftStickY = self->_controllerMouseEnabledFlag ? 0 : leftStickOffset.dy;
+
+ rightStickX = self->_controllerMouseEnabledFlag ? 0 : rightStickOffset.dx;
+ rightStickY = self->_controllerMouseEnabledFlag ? 0 : rightStickOffset.dy;
if(self->_controllerMouseEnabledFlag){
self->stickToMouseInputX = self->_controllerMouseStick == LeftStickToMouse ? gamepad.leftThumbstick.xAxis.value : gamepad.rightThumbstick.xAxis.value;
@@ -1751,6 +1764,10 @@ double rc_expo(double x, double expo) {
_controllerGyroSwitchTogglePressed = false;
_controllerGyroSwitchHoldPressed = false;
+ stickMaxOffset = 0x7FFE;
+ _leftStickMinOffset = oscProfile.physicalLeftStickMinOffset;
+ _rightStickMinOffset = oscProfile.physicalRightStickMinOffset;
+
if(oscProfile.controllerGyroSwitchMode == ControllerGyroSwitchDisabled && ![self useMotionHandler]) _gyroEnabledFlag = true;
if(![self useMotionHandler]) [self->motionHandler stopGyroUpdateWithInterruptNoneGyroInput:false resetLeftStick:true];
diff --git a/VoidLink/Input/ControllerUtil.swift b/VoidLink/Input/ControllerUtil.swift
index aecd169c..0da2dd7b 100644
--- a/VoidLink/Input/ControllerUtil.swift
+++ b/VoidLink/Input/ControllerUtil.swift
@@ -51,7 +51,9 @@ import GameController
@objc class ControllerUtil: NSObject {
-
+
+ static private let stickMaxOffset:CGFloat = 0x7FFE
+
@objc static func listen(
controller: GCController,
swapABXY: Bool,
@@ -74,8 +76,8 @@ import GameController
handler(buttonDict, gamepad, element)
}
}
-
-
+
+
// MARK: - 构建按钮映射
private static func buildMapping(for controller: GCController, swapABXY:Bool)
-> [ControllerButton : GCControllerButtonInput]
@@ -125,7 +127,7 @@ import GameController
if let home = pad.buttonHome {
result[.special] = home
}
-
+
if let controller = pad.controller {
let profile = controller.physicalInputProfile
if let paddle1 = profile.buttons[GCInputXboxPaddleOne] {
@@ -189,4 +191,13 @@ import GameController
case .null: return SwiftLocalizationHelper.localizedString(forKey: "Null")
}
}
+
+ @objc static func compensated(offsetVector: CGVector, withMinOffset minOffset: CGFloat) -> CGVector{
+ let vectorHypot = hypot(offsetVector.dx, offsetVector.dy)
+ guard vectorHypot > 0 else {return CGVector(dx: 0, dy: 0)}
+ let targetHypot = minOffset + (stickMaxOffset-minOffset)*(vectorHypot/stickMaxOffset)
+ let compensatedX = targetHypot * (offsetVector.dx/vectorHypot)
+ let compensatedY = targetHypot * (offsetVector.dy/vectorHypot)
+ return CGVector(dx: compensatedX, dy: compensatedY)
+ }
}
diff --git a/VoidLink/Input/CustomOSC/OSCProfile.h b/VoidLink/Input/CustomOSC/OSCProfile.h
index f394a190..7cae79cb 100644
--- a/VoidLink/Input/CustomOSC/OSCProfile.h
+++ b/VoidLink/Input/CustomOSC/OSCProfile.h
@@ -42,6 +42,8 @@ typedef NS_ENUM(NSInteger, MapGyroTo) {
@property (nonatomic, assign) CGFloat accelSensitivityY;
@property (nonatomic, assign) CGFloat accelSensitivityZ;
@property (nonatomic, assign) double gyroToStickMinOffset;
+@property (nonatomic, assign) double physicalLeftStickMinOffset;
+@property (nonatomic, assign) double physicalRightStickMinOffset;
@property (nonatomic, assign) int controllerGyroSwitchMode;
@property (nonatomic, assign) bool reverseGyroHoldButton;
@property (nonatomic, assign) int controllerGyroSwitchHold;
diff --git a/VoidLink/Input/CustomOSC/OSCProfile.m b/VoidLink/Input/CustomOSC/OSCProfile.m
index 1a25c9f9..81917b36 100644
--- a/VoidLink/Input/CustomOSC/OSCProfile.m
+++ b/VoidLink/Input/CustomOSC/OSCProfile.m
@@ -44,6 +44,8 @@
[encoder encodeFloat:self.accelSensitivityY forKey:@"accelSensitivityY"];
[encoder encodeFloat:self.accelSensitivityZ forKey:@"accelSensitivityZ"];
[encoder encodeDouble:self.gyroToStickMinOffset forKey:@"gyroToStickMinOffset"];
+ [encoder encodeDouble:self.physicalLeftStickMinOffset forKey:@"physicalLeftStickMinOffset"];
+ [encoder encodeDouble:self.physicalRightStickMinOffset forKey:@"physicalRightStickMinOffset"];
[encoder encodeInt:self.controllerGyroSwitchMode forKey:@"controllerGyroSwitchMode"];
[encoder encodeBool:self.reverseGyroHoldButton forKey:@"reverseGyroHoldButton"];
[encoder encodeInt:self.controllerGyroSwitchHold forKey:@"controllerGyroSwitchHold"];
@@ -66,6 +68,8 @@
self.accelSensitivityY = [decoder containsValueForKey:@"accelSensitivityY"] ? [decoder decodeFloatForKey:@"accelSensitivityY"] : 1.0;
self.accelSensitivityZ = [decoder containsValueForKey:@"accelSensitivityZ"] ? [decoder decodeFloatForKey:@"accelSensitivityZ"] : 1.0;
self.gyroToStickMinOffset = [decoder containsValueForKey:@"gyroToStickMinOffset"] ? [decoder decodeDoubleForKey:@"gyroToStickMinOffset"] : 0;
+ self.physicalLeftStickMinOffset = [decoder containsValueForKey:@"physicalLeftStickMinOffset"] ? [decoder decodeDoubleForKey:@"physicalLeftStickMinOffset"] : 0;
+ self.physicalRightStickMinOffset = [decoder containsValueForKey:@"physicalRightStickMinOffset"] ? [decoder decodeDoubleForKey:@"physicalRightStickMinOffset"] : 0;
self.controllerGyroSwitchMode = [decoder containsValueForKey:@"controllerGyroSwitchMode"] ? [decoder decodeIntForKey:@"controllerGyroSwitchMode"] : ControllerGyroSwitchDisabled;
self.reverseGyroHoldButton = [decoder containsValueForKey:@"reverseGyroHoldButton"] ? [decoder decodeBoolForKey:@"reverseGyroHoldButton"] : false;
self.controllerGyroSwitchHold = [decoder containsValueForKey:@"controllerGyroSwitchHold"] ? [decoder decodeIntForKey:@"controllerGyroSwitchHold"] : ControllerButtonNull;
@@ -91,6 +95,8 @@
copy.accelSensitivityY = self.accelSensitivityY;
copy.accelSensitivityZ = self.accelSensitivityZ;
copy.gyroToStickMinOffset = self.gyroToStickMinOffset;
+ copy.physicalLeftStickMinOffset = self.physicalLeftStickMinOffset;
+ copy.physicalRightStickMinOffset = self.physicalRightStickMinOffset;
copy.controllerGyroSwitchMode = self.controllerGyroSwitchMode;
copy.reverseGyroHoldButton = self.reverseGyroHoldButton;
copy.controllerGyroSwitchHold = self.controllerGyroSwitchHold;
diff --git a/VoidLink/Input/MotionHandler.swift b/VoidLink/Input/MotionHandler.swift
index abdee09a..82e0e2e4 100644
--- a/VoidLink/Input/MotionHandler.swift
+++ b/VoidLink/Input/MotionHandler.swift
@@ -304,21 +304,24 @@ import CoreMotion
if oscProfile.yawPitchToRightStick {
yaw = rightStickTouchInputX + rightStickPhysicalInputX + gyroInputToStickInput(input:yawSource*sensitvityYaw*widgetYawFactor*10)
yaw = self.clampStickInput(input: yaw)
- yaw = (yaw >= 0 ? 1.0 : -1.0) * gyroToStickMinOffset + (self.stickMaxOffset - gyroToStickMinOffset) * (yaw/self.stickMaxOffset)
pitch = rightStickTouchInputY + rightStickPhysicalInputY - gyroInputToStickInput(input:pitchSource*sensitvityPitch*widgetPitchFactor*10)
pitch = self.clampStickInput(input: pitch)
- pitch = (pitch >= 0 ? 1.0 : -1.0) * gyroToStickMinOffset + (self.stickMaxOffset - gyroToStickMinOffset) * (pitch/self.stickMaxOffset)
-
- self.onScreenControls.sendRightStickTouchPadEvent(yaw, pitch)
+
+ let offsetVector = ControllerUtil.compensated(offsetVector: CGVector(dx: yaw, dy: pitch), withMinOffset: gyroToStickMinOffset)
+
+ self.onScreenControls.sendRightStickTouchPadEvent(offsetVector.dx, offsetVector.dy)
}
if oscProfile.rollToLeftStick {
roll = gyroInputToStickInput(input:rollSource*sensitvityRoll*widgetRollFactor*0.2)
rollIntegral = rollIntegral + roll
- var mixedLeftStickOffset = self.clampStickInput(input: rollIntegral+leftStickTouchInputX+leftStickPhysicalInputX)
- mixedLeftStickOffset = (mixedLeftStickOffset >= 0 ? 1.0 : -1.0) * gyroToStickMinOffset + (self.stickMaxOffset - gyroToStickMinOffset) * (mixedLeftStickOffset/self.stickMaxOffset)
- self.onScreenControls.sendLeftStickTouchPadEvent(mixedLeftStickOffset, leftStickTouchInputY+leftStickPhysicalInputY)
+ let mixedLeftStickOffsetX = self.clampStickInput(input: rollIntegral+leftStickTouchInputX+leftStickPhysicalInputX)
+ let mixedLeftStickOffsetY = self.clampStickInput(input: leftStickTouchInputY+leftStickPhysicalInputY)
+
+ let offsetVector = ControllerUtil.compensated(offsetVector: CGVector(dx: mixedLeftStickOffsetX, dy: mixedLeftStickOffsetY), withMinOffset: gyroToStickMinOffset)
+
+ self.onScreenControls.sendLeftStickTouchPadEvent(offsetVector.dx, offsetVector.dy)
}
}
}
diff --git a/VoidLink/Input/OnScreenWidgetView.swift b/VoidLink/Input/OnScreenWidgetView.swift
index 23e470b8..8a189f5d 100644
--- a/VoidLink/Input/OnScreenWidgetView.swift
+++ b/VoidLink/Input/OnScreenWidgetView.swift
@@ -1291,31 +1291,29 @@ import UIKit
}
private func sendRightStickTouchPadEvent(inputX: CGFloat, inputY: CGFloat){
- var targetX = self.touchInputToStickInput(input: inputX)
- var targetY = -self.touchInputToStickInput(input: inputY)
- // vertical input must be inverted
-
+ let targetX = self.touchInputToStickInput(input: inputX)
+ let targetY = -self.touchInputToStickInput(input: inputY)
let mixRightStickInputToGyro = (oscProfile.mapGyroTo == MapGyroTo.mapGyroToControllerStick
&& oscProfile.yawPitchToRightStick)
if !mixRightStickInputToGyro || (self.motionHandler.gyroMixInputStarted() != true) {
- targetX = (targetX >= 0 ? 1.0 : -1.0) * self.minStickOffset + (self.stickMaxOffset - self.minStickOffset) * (targetX/self.stickMaxOffset)
- targetY = (targetY >= 0 ? 1.0 : -1.0) * self.minStickOffset + (self.stickMaxOffset - self.minStickOffset) * (targetY/self.stickMaxOffset)
- self.onScreenControls.sendRightStickTouchPadEvent(targetX, targetY)
+
+ let offsetVector = ControllerUtil.compensated(offsetVector: CGVector(dx: targetX, dy: targetY), withMinOffset: minStickOffset)
+ self.onScreenControls.sendRightStickTouchPadEvent(offsetVector.dx, offsetVector.dy)
}
self.motionHandler.mixOnScreenRightStickAndGyroInput(x: targetX, y: targetY)
}
private func sendLeftStickTouchPadEvent(inputX: CGFloat, inputY: CGFloat){
- var targetX = self.touchInputToStickInput(input: inputX)
- var targetY = -self.touchInputToStickInput(input: inputY)
- targetX = (targetX >= 0 ? 1.0 : -1.0) * self.minStickOffset + (self.stickMaxOffset - self.minStickOffset) * (targetX/self.stickMaxOffset)
- targetY = (targetY >= 0 ? 1.0 : -1.0) * self.minStickOffset + (self.stickMaxOffset - self.minStickOffset) * (targetY/self.stickMaxOffset)
+ let targetX = self.touchInputToStickInput(input: inputX)
+ let targetY = -self.touchInputToStickInput(input: inputY)
let mixLeftStickInputToGyro = (oscProfile.mapGyroTo == MapGyroTo.mapGyroToControllerStick
&& oscProfile.rollToLeftStick)
if !mixLeftStickInputToGyro || (self.motionHandler.gyroMixInputStarted() != true) {
- self.onScreenControls.sendLeftStickTouchPadEvent(targetX, targetY)
+
+ let offsetVector = ControllerUtil.compensated(offsetVector: CGVector(dx: targetX, dy: targetY), withMinOffset: minStickOffset)
+ self.onScreenControls.sendLeftStickTouchPadEvent(offsetVector.dx, offsetVector.dy)
}
self.motionHandler.mixOnScreenLeftStickAndGyroInput(x: targetX, y: targetY)
}
diff --git a/VoidLink/Localization/Localizable.xcstrings b/VoidLink/Localization/Localizable.xcstrings
index 04e68705..6c948f68 100644
--- a/VoidLink/Localization/Localizable.xcstrings
+++ b/VoidLink/Localization/Localizable.xcstrings
@@ -1497,25 +1497,25 @@
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Gyro toggle button: %@\nGyro hold-down button: %@"
+ "value" : "Gyro toggle button: %@\nGyro hold-down button: %@\n\nThese buttons are able to switch DS4 gyro mapping on & off when enabled."
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
- "value" : "陀螺仪切换按键: %@\n陀螺仪保持按键: %@"
+ "value" : "陀螺仪切换按键: %@\n陀螺仪保持按键: %@\n\n以上按键被启用后,也可用于启动/暂停DS4陀螺仪映射"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
- "value" : "陀螺儀切換按鍵: %@\n陀螺儀保持按鍵: %@"
+ "value" : "陀螺儀切換按鍵: %@\n陀螺儀保持按鍵: %@\n\n以上按鍵啟用後,也可用於啟動/暫停 DS4 陀螺儀映射。"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
- "value" : "陀螺儀切換按鍵: %@\n陀螺儀保持按鍵: %@"
+ "value" : "陀螺儀切換按鍵: %@\n陀螺儀保持按鍵: %@\n\n以上按鍵啟用後,也可用於啟動/暫停 DS4 陀螺儀映射。"
}
}
}
@@ -2788,35 +2788,6 @@
}
}
},
- "gyroModeStackDoc" : {
- "extractionState" : "manual",
- "localizations" : {
- "en" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/uscwmv3vz8g30rmk"
- }
- },
- "zh-Hans" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3"
- }
- },
- "zh-Hant" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3"
- }
- },
- "zh-HK" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/bz6ggagp0ly4loq3"
- }
- }
- }
- },
"gyroModeStackTip" : {
"extractionState" : "manual",
"localizations" : {
@@ -3520,6 +3491,35 @@
}
}
},
+ "mapGyroToStackTip" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "- To map gyro directly to mouse/stick, must turn off DS4 gyro mapping in “Controller” section.\n\n- All settings in “Motion Control” section are saved with on-screen widget profile, which can be selected & managed in the right edge slide toolbox menu.\n\n- Motion control can be turned on & off by controller buttons or on-screen buttons"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "needs_review",
+ "value" : "- 关闭“手柄”菜单分区的DS4陀螺仪映射后, 方可将陀螺仪直接映射为手柄或鼠标。\n\n- “体感控制”菜单分区的所有设置均保存在屏显控件配置中,可在右边缘滑出的工具箱菜单中切换。\n\n- 体感控制陀螺仪可由手柄按键或屏显按键开关。"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "- 關閉「手柄」選單分區的 DS4 陀螺儀映射後,方可將陀螺儀直接映射為手柄或滑鼠。\n\n- 「體感控制」選單分區的所有設定皆儲存在螢幕顯示小工具配置中,可在右邊緣滑出的工具箱選單中切換。\n\n- 體感控制陀螺儀可由手把按鍵或螢幕顯示按鍵開關。"
+ }
+ },
+ "zh-HK" : {
+ "stringUnit" : {
+ "state" : "needs_review",
+ "value" : "- 關閉「手柄」選單分區的 DS4 陀螺儀映射後,方可將陀螺儀直接映射為手柄或滑鼠。\n\n- 「體感控制」選單分區的所有設定皆儲存在螢幕顯示小工具配置中,可在右邊緣滑出的工具箱選單中切換。\n\n- 體感控制陀螺儀可由手把按鍵或螢幕顯示按鍵開關。"
+ }
+ }
+ }
+ },
"micPermissionTip" : {
"extractionState" : "manual",
"localizations" : {
@@ -4655,6 +4655,35 @@
}
}
},
+ "physicaStickMinOffsetTip" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "This is saved in onscreen widget profile, which can be selected & managed in toolbox menu slide from the right screen edge."
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "此项设置保存在屏显控件配置方案中, 可在屏幕右边缘滑出的工具箱菜单中切换。"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "此項設定儲存在螢幕小工具配置方案中,可在螢幕右邊緣滑出的工具箱選單中切換。"
+ }
+ },
+ "zh-HK" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "此項設定儲存在螢幕小工具配置方案中,可在螢幕右邊緣滑出的工具箱選單中切換。"
+ }
+ }
+ }
+ },
"pipStackTip" : {
"extractionState" : "manual",
"localizations" : {
@@ -5606,6 +5635,35 @@
}
}
},
+ "reverseHoldButtonStackTip" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "When enabled, the gyro-hold button becomes a gyro-pause button: holding it down pauses the gyro, and releasing it reactivates it."
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "启用该项后,陀螺仪保持按键变为陀螺仪暂停按键。按住暂停陀螺仪,松开重新启动陀螺仪。"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "啟用此項後,陀螺儀保持按鍵將變為陀螺儀暫停按鍵。按住可暫停陀螺儀,鬆開則重新啟動陀螺儀。"
+ }
+ },
+ "zh-HK" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "啟用此項後,陀螺儀保持按鍵將變為陀螺儀暫停按鍵。按住可暫停陀螺儀,鬆開則重新啟動陀螺儀。"
+ }
+ }
+ }
+ },
"right" : {
"extractionState" : "manual",
"localizations" : {
@@ -7182,6 +7240,35 @@
}
}
},
+ "yourMotionControlSoution" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/watpxdpsogirwie0"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/iy4zgh86odhfgf6h"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/iy4zgh86odhfgf6h"
+ }
+ },
+ "zh-HK" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "https://voidlink.yuque.com/org-wiki-voidlink-znirha/fa3tgr/iy4zgh86odhfgf6h"
+ }
+ }
+ }
+ },
"yuv444StackTip" : {
"extractionState" : "manual",
"localizations" : {
diff --git a/VoidLink/ViewControllers/SettingsViewController.h b/VoidLink/ViewControllers/SettingsViewController.h
index 5d12bed6..8576fce1 100644
--- a/VoidLink/ViewControllers/SettingsViewController.h
+++ b/VoidLink/ViewControllers/SettingsViewController.h
@@ -232,6 +232,12 @@
@property (strong, nonatomic) IBOutlet UIStackView *reverseHoldButtonStack;
@property (strong, nonatomic) IBOutlet UISwitch *reverseHoldButtonSwitch;
+@property (strong, nonatomic) IBOutlet UIStackView *leftStickMinOffsetStack;
+@property (strong, nonatomic) IBOutlet UISlider *leftStickMinOffsetSlider;
+
+@property (strong, nonatomic) IBOutlet UIStackView *rightStickMinOffsetStack;
+@property (strong, nonatomic) IBOutlet UISlider *rightStickMinOffsetSlider;
+
@property (strong, nonatomic) IBOutlet UIStackView *testStack;
@property (nonatomic, strong) MicHandler *micHandler;
diff --git a/VoidLink/ViewControllers/SettingsViewController.m b/VoidLink/ViewControllers/SettingsViewController.m
index b6a9a4ad..139ea844 100644
--- a/VoidLink/ViewControllers/SettingsViewController.m
+++ b/VoidLink/ViewControllers/SettingsViewController.m
@@ -352,7 +352,7 @@ BOOL isCustomResolution(int resolutionSelected) {
NSLog(@"Mic error: %@", error);
}
-- (void)reloadMotionControlConfigs{
+- (void)reloadGameProfileConfigs{
oscProfile = [oscProfileMan getSelectedProfile];
self.controllerGyroSwitchButtonSetter.selectedSegmentIndex = oscProfile.controllerGyroSwitchMode;
@@ -384,10 +384,15 @@ BOOL isCustomResolution(int resolutionSelected) {
[self.gyroToStickMinOffsetSlider setValue:(uint16_t)oscProfile.gyroToStickMinOffset];
[self gyroMinStickOffsetSliderMoved:self.gyroToStickMinOffsetSlider];
+ [self.leftStickMinOffsetSlider setValue:(uint16_t)oscProfile.physicalLeftStickMinOffset];
+ [self leftStickMinOffsetSliderMoved:self.leftStickMinOffsetSlider];
+ [self.rightStickMinOffsetSlider setValue:(uint16_t)oscProfile.physicalRightStickMinOffset];
+ [self rightStickMinOffsetSliderMoved:self.rightStickMinOffsetSlider];
+
[self.synthPhysicalInputSwitch setOn:oscProfile.synthesizePhysicalStick];
}
-- (void)saveMotionControlConfigs{
+- (void)saveGameProfileConfigs{
CGFloat yawSensitivityPercent = [self map_velocFactorDisplay_fromSliderValue:self.yawSensitivitySlider.value];
CGFloat pitchSensitivityPercent = [self map_velocFactorDisplay_fromSliderValue:self.pitchSensitivitySlider.value];
@@ -403,6 +408,8 @@ BOOL isCustomResolution(int resolutionSelected) {
&& oscProfile.synthesizePhysicalStick == self.synthPhysicalInputSwitch.isOn
&& oscProfile.controllerGyroSwitchMode == self.controllerGyroSwitchButtonSetter.selectedSegmentIndex
&& oscProfile.reverseGyroHoldButton == self.reverseHoldButtonSwitch.isOn
+ && (int16_t)(oscProfile.physicalLeftStickMinOffset) == (int16_t)self.leftStickMinOffsetSlider.value
+ && (int16_t)(oscProfile.physicalRightStickMinOffset) == (int16_t)self.rightStickMinOffsetSlider.value
);
if(!configNotChanged){
@@ -416,6 +423,8 @@ BOOL isCustomResolution(int resolutionSelected) {
oscProfile.synthesizePhysicalStick = self.synthPhysicalInputSwitch.isOn;
oscProfile.controllerGyroSwitchMode = (int)self.controllerGyroSwitchButtonSetter.selectedSegmentIndex;
oscProfile.reverseGyroHoldButton = self.reverseHoldButtonSwitch.isOn;
+ oscProfile.physicalLeftStickMinOffset = (int16_t)self.leftStickMinOffsetSlider.value;
+ oscProfile.physicalRightStickMinOffset = (int16_t)self.rightStickMinOffsetSlider.value;
[oscProfileMan replaceSelectedProfileWith:oscProfile overwriteDefault:YES];
}
}
@@ -442,7 +451,7 @@ BOOL isCustomResolution(int resolutionSelected) {
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(reloadMotionControlConfigs)
+ selector:@selector(reloadGameProfileConfigs)
name:@"OscLayoutCloseNotification"
object:nil];
@@ -496,7 +505,7 @@ BOOL isCustomResolution(int resolutionSelected) {
};
*/
- [self reloadMotionControlConfigs];
+ [self reloadGameProfileConfigs];
self->tempSettings = [self->dataMan getSettings];
}
@@ -785,6 +794,8 @@ BOOL isCustomResolution(int resolutionSelected) {
[self addSetting:self.emulatedControllerTypeStack ofId:@"emulatedControllerTypeStack" withInfoTag:YES withDynamicLabel:NO to:controllerSection];
[self addSetting:self.gyroModeStack ofId:@"gyroModeStack" withInfoTag:YES withDynamicLabel:YES to:controllerSection];
[self addSetting:self.gyroSensitivityStack ofId:@"gyroSensitivityStack" withInfoTag:NO withDynamicLabel:YES to:controllerSection];
+ [self addSetting:self.leftStickMinOffsetStack ofId:@"leftStickMinOffsetStack" withInfoTag:YES withDynamicLabel:YES to:controllerSection];
+ [self addSetting:self.rightStickMinOffsetStack ofId:@"rightStickMinOffsetStack" withInfoTag:YES withDynamicLabel:YES to:controllerSection];
[self addSetting:self.controllerToMouseStack ofId:@"controllerToMouseStack" withInfoTag:YES withDynamicLabel:YES to:controllerSection];
[self addSetting:self.controllerMouseVelocityStack ofId:@"controllerMouseVelocityStack" withInfoTag:NO withDynamicLabel:YES to:controllerSection];
[self addSetting:self.controllerMouseExpoStack ofId:@"controllerMouseExpoStack" withInfoTag:YES withDynamicLabel:YES to:controllerSection];
@@ -800,7 +811,7 @@ BOOL isCustomResolution(int resolutionSelected) {
}
[self addSetting:self.controllerGyroSwitchButtonStack ofId:@"controllerGyroSwitchButtonStack" withInfoTag:YES withDynamicLabel:YES to:motionControlSection];
[self addSetting:self.reverseHoldButtonStack ofId:@"reverseHoldButtonStack" withInfoTag:YES withDynamicLabel:YES to:motionControlSection];
- [self addSetting:self.mapGyroToStack ofId:@"mapGyroToStack" withInfoTag:NO withDynamicLabel:NO to:motionControlSection];
+ [self addSetting:self.mapGyroToStack ofId:@"mapGyroToStack" withInfoTag:YES withDynamicLabel:NO to:motionControlSection];
[self addSetting:self.gyroToStickSwitchStack ofId:@"gyroToStickStack" withInfoTag:NO withDynamicLabel:NO to:motionControlSection];
[self addSetting:self.yawPitchSensitivityStack ofId:@"yawPitchSensitivityStack" withInfoTag:NO withDynamicLabel:NO to:motionControlSection];
[self addDynamicLabelForStack:self.yawSensitivityStack];
@@ -1328,11 +1339,19 @@ BOOL isCustomResolution(int resolutionSelected) {
showOnlineDocAction = true;
onlineDocLink = [LocalizationHelper localizedStringForKey:@"emulatedControllerTypeStackDoc"];
}
+
if([sender.superview.accessibilityIdentifier isEqualToString: @"gyroModeStack"]){
tipText = [LocalizationHelper localizedStringForKey:@"gyroModeStackTip"];
showOnlineDocAction = true;
- onlineDocLink = [LocalizationHelper localizedStringForKey:@"gyroModeStackDoc"];
+ onlineDocLink = [LocalizationHelper localizedStringForKey:@"yourMotionControlSoution"];
}
+ if([sender.superview.accessibilityIdentifier isEqualToString: @"mapGyroToStack"]){
+ tipText = [LocalizationHelper localizedStringForKey:@"mapGyroToStackTip"];
+ showOnlineDocAction = true;
+ onlineDocLink = [LocalizationHelper localizedStringForKey:@"yourMotionControlSoution"];
+ }
+
+
if([sender.superview.accessibilityIdentifier isEqualToString: @"renderingBackendStack"]){
tipText = [LocalizationHelper localizedStringForKey:@"renderingBackendStackTip"];
showOnlineDocAction = false;
@@ -1388,7 +1407,19 @@ BOOL isCustomResolution(int resolutionSelected) {
tipText = [LocalizationHelper localizedStringForKey:@"controllerToMouseStackTip", [ControllerUtil stringFor:tempSettings.controllerMouseSwitch.intValue], [LocalizationHelper localizedStringForKey:tempSettings.controllerMouseStick.intValue == LeftStickToMouse ? @"Left stick" : @"Right stick"], [ControllerUtil stringFor:tempSettings.controllerMouseLeftButton.intValue], [ControllerUtil stringFor:tempSettings.controllerMouseRightButton.intValue]];
showOnlineDocAction = false;
}
-
+ if([sender.superview.accessibilityIdentifier isEqualToString: @"reverseHoldButtonStack"]){
+ tipText = [LocalizationHelper localizedStringForKey:@"reverseHoldButtonStackTip"];
+ showOnlineDocAction = false;
+ }
+ if([sender.superview.accessibilityIdentifier isEqualToString: @"leftStickMinOffsetStack"]){
+ tipText = [LocalizationHelper localizedStringForKey:@"physicaStickMinOffsetTip"];
+ showOnlineDocAction = false;
+ }
+ if([sender.superview.accessibilityIdentifier isEqualToString: @"rightStickMinOffsetStack"]){
+ tipText = [LocalizationHelper localizedStringForKey:@"physicaStickMinOffsetTip"];
+ showOnlineDocAction = false;
+ }
+
UIAlertController *tipsAlertController = [UIAlertController alertControllerWithTitle: [LocalizationHelper localizedStringForKey:@"Tips"] message:tipText preferredStyle:UIAlertControllerStyleAlert];
@@ -1810,7 +1841,10 @@ BOOL isCustomResolution(int resolutionSelected) {
[self.emulatedControllerTypeSelector setSelectedSegmentIndex:[self controllerTypeToSegmentIndex:self->tempSettings.emulatedControllerType.intValue]];
[self.emulatedControllerTypeSelector addTarget:self action:@selector(emulatedControllerTypeChanged:) forControlEvents:(UIControlEventValueChanged)]; // Update label display when slider is being moved.
[self emulatedControllerTypeChanged:self.emulatedControllerTypeSelector];
-
+
+ [self.leftStickMinOffsetSlider addTarget:self action:@selector(leftStickMinOffsetSliderMoved:) forControlEvents:UIControlEventValueChanged];
+ [self.rightStickMinOffsetSlider addTarget:self action:@selector(rightStickMinOffsetSliderMoved:) forControlEvents:UIControlEventValueChanged];
+
[self.audioOnPcSwitch setOn:self->tempSettings.playAudioOnPC];
[self.localVolumeSlider setValue:self->tempSettings.localVolume.floatValue*100];
@@ -2521,6 +2555,14 @@ BOOL isCustomResolution(int resolutionSelected) {
[self findDynamicLabelFromStack:_gyroToStickMinOffsetStack].text = [NSString stringWithFormat:@" %d ", (int16_t)sender.value];
}
+- (void)leftStickMinOffsetSliderMoved:(UISlider* )sender{
+ [self findDynamicLabelFromStack:_leftStickMinOffsetStack].text = [NSString stringWithFormat:@" %d ", (int16_t)sender.value];
+}
+
+- (void)rightStickMinOffsetSliderMoved:(UISlider* )sender{
+ [self findDynamicLabelFromStack:_rightStickMinOffsetStack].text = [NSString stringWithFormat:@" %d ", (int16_t)sender.value];
+}
+
- (void)invokeOscLayout{
// init CustomOSC stuff
/* sets a reference to the correct 'LayoutOnScreenControlsViewController' depending on whether the user is on an iPhone or iPad */
@@ -3212,7 +3254,7 @@ BOOL isCustomResolution(int resolutionSelected) {
if(![MicHandler permissionGranted]) [self.redirectMicSwitch setOn:false];
- [self saveMotionControlConfigs];
+ [self saveGameProfileConfigs];
}
- (void) saveSettings {
diff --git a/VoidLink/mul.lproj/iPad.xcstrings b/VoidLink/mul.lproj/iPad.xcstrings
index fe6978fc..a14352a0 100644
--- a/VoidLink/mul.lproj/iPad.xcstrings
+++ b/VoidLink/mul.lproj/iPad.xcstrings
@@ -163,6 +163,36 @@
}
}
},
+ "3XV-jL-NYF.text" : {
+ "comment" : "Class = \"UILabel\"; text = \"Left Stick Minimum Offset\"; ObjectID = \"3XV-jL-NYF\";",
+ "extractionState" : "extracted_with_value",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "new",
+ "value" : "Left Stick Minimum Offset"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "左摇杆最小偏移"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "左搖桿最小偏移"
+ }
+ },
+ "zh-HK" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "左搖桿最小偏移"
+ }
+ }
+ }
+ },
"4FU-iP-c6T.segmentTitles[0]" : {
"comment" : "Class = \"UISegmentedControl\"; 4FU-iP-c6T.segmentTitles[0] = \"Captured\"; ObjectID = \"4FU-iP-c6T\";",
"extractionState" : "extracted_with_value",
@@ -2311,6 +2341,36 @@
}
}
},
+ "f3L-bA-RW6.text" : {
+ "comment" : "Class = \"UILabel\"; text = \"Right Stick Minimum Offset\"; ObjectID = \"f3L-bA-RW6\";",
+ "extractionState" : "extracted_with_value",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "new",
+ "value" : "Right Stick Minimum Offset"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "右摇杆最小偏移"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "右搖桿最小偏移"
+ }
+ },
+ "zh-HK" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "右搖桿最小偏移"
+ }
+ }
+ }
+ },
"FJv-HJ-UYJ.text" : {
"comment" : "Class = \"UILabel\"; text = \"Touch Move Event Interval\"; ObjectID = \"FJv-HJ-UYJ\";",
"extractionState" : "extracted_with_value",
@@ -2852,30 +2912,30 @@
}
},
"HqA-an-KEe.text" : {
- "comment" : "Class = \"UILabel\"; text = \"Minimum offset (Gyro→Stick)\"; ObjectID = \"HqA-an-KEe\";",
+ "comment" : "Class = \"UILabel\"; text = \"Minimum Offset (Gyro→Stick)\"; ObjectID = \"HqA-an-KEe\";",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
- "value" : "Minimum offset (Gyro→Stick)"
+ "value" : "Minimum Offset (Gyro→Stick)"
}
},
"zh-Hans" : {
"stringUnit" : {
- "state" : "translated",
+ "state" : "needs_review",
"value" : "最小偏移量(陀螺仪→摇杆)"
}
},
"zh-Hant" : {
"stringUnit" : {
- "state" : "translated",
+ "state" : "needs_review",
"value" : "最小偏移量(陀螺儀→搖桿)"
}
},
"zh-HK" : {
"stringUnit" : {
- "state" : "translated",
+ "state" : "needs_review",
"value" : "最小偏移量(陀螺儀→搖桿)"
}
}
diff --git a/VoidLink/mul.lproj/iPhone.xcstrings b/VoidLink/mul.lproj/iPhone.xcstrings
index 5dee1d5b..fcbe651f 100644
--- a/VoidLink/mul.lproj/iPhone.xcstrings
+++ b/VoidLink/mul.lproj/iPhone.xcstrings
@@ -950,30 +950,30 @@
}
},
"52u-T9-1Hv.text" : {
- "comment" : "Class = \"UILabel\"; text = \"Minimum offset (Gyro→Stick)\"; ObjectID = \"52u-T9-1Hv\";",
+ "comment" : "Class = \"UILabel\"; text = \"Minimum Offset (Gyro→Stick)\"; ObjectID = \"52u-T9-1Hv\";",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
- "value" : "Minimum offset (Gyro→Stick)"
+ "value" : "Minimum Offset (Gyro→Stick)"
}
},
"zh-Hans" : {
"stringUnit" : {
- "state" : "translated",
+ "state" : "needs_review",
"value" : "最小偏移量(陀螺仪→摇杆)"
}
},
"zh-Hant" : {
"stringUnit" : {
- "state" : "translated",
+ "state" : "needs_review",
"value" : "最小偏移量(陀螺儀→搖桿)"
}
},
"zh-HK" : {
"stringUnit" : {
- "state" : "translated",
+ "state" : "needs_review",
"value" : "最小偏移量(陀螺儀→搖桿)"
}
}
@@ -4063,6 +4063,36 @@
}
}
},
+ "q6s-4H-y9n.text" : {
+ "comment" : "Class = \"UILabel\"; text = \"Right Stick Minimum Offset\"; ObjectID = \"q6s-4H-y9n\";",
+ "extractionState" : "extracted_with_value",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "new",
+ "value" : "Right Stick Minimum Offset"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "右摇杆最小偏移"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "右搖桿最小偏移"
+ }
+ },
+ "zh-HK" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "右搖桿最小偏移"
+ }
+ }
+ }
+ },
"q6v-Jb-ysy.segmentTitles[0]" : {
"comment" : "Class = \"UISegmentedControl\"; q6v-Jb-ysy.segmentTitles[0] = \"Xbox 360\"; ObjectID = \"q6v-Jb-ysy\";",
"extractionState" : "extracted_with_value",
@@ -5203,6 +5233,36 @@
}
}
},
+ "whI-oR-V8o.text" : {
+ "comment" : "Class = \"UILabel\"; text = \"Left Stick Minimum Offset\"; ObjectID = \"whI-oR-V8o\";",
+ "extractionState" : "extracted_with_value",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "new",
+ "value" : "Left Stick Minimum Offset"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "左摇杆最小偏移"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "左搖桿最小偏移"
+ }
+ },
+ "zh-HK" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "左搖桿最小偏移"
+ }
+ }
+ }
+ },
"wOV-Sp-UUE.segmentTitles[0]" : {
"comment" : "Class = \"UISegmentedControl\"; wOV-Sp-UUE.segmentTitles[0] = \"Slide From Left Edge\"; ObjectID = \"wOV-Sp-UUE\";",
"extractionState" : "extracted_with_value",