Skip to content

Commit aefee46

Browse files
Added Snap Turning (#556)
* Added Snap Turning - Disabled by default, can be enabled in settings - Amount per input can be defined in settings (current range is 15 - 90 degrees) - Default input action is the right thumb stick (same as smooth turn) * Moved snap turn from slider to selector for settings * Fixed snap turning with smooth HUD/Helmet - Previously, the smooth HUD/Helmet would uncomfortably move a long distance after you snap turn - Now, if smooth HUD and snap turning are both enabled, the helmet snap turns for a frame, then resumes it's previous behavior * Disable snap turning while in zero-g --------- Co-authored-by: Raicuparta <[email protected]>
1 parent 28a335a commit aefee46

File tree

6 files changed

+125
-11
lines changed

6 files changed

+125
-11
lines changed

NomaiVR/ModConfig/IModSettingProvider.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ public interface IModSettingProvider
1717
bool PreventClipping { get; }
1818
bool FlashlightGesture { get; }
1919
bool ControllerOrientedMovement { get; }
20+
bool SnapTurning { get; }
21+
string SnapTurnIncrement { get; }
2022
bool AutoHideToolbelt { get; }
2123
float ToolbeltHeight { get; }
2224
float HudScale { get; }

NomaiVR/ModConfig/ModSettings.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ public static class ModSettings
1919
public static bool PreventClipping => settingsProvider.PreventClipping;
2020
public static bool FlashlightGesture => settingsProvider.FlashlightGesture;
2121
public static bool ControllerOrientedMovement => settingsProvider.ControllerOrientedMovement;
22+
public static bool SnapTurning => settingsProvider.SnapTurning;
23+
public static string SnapTurnIncrement => settingsProvider.SnapTurnIncrement;
2224
public static bool AutoHideToolbelt => settingsProvider.AutoHideToolbelt;
2325
public static float ToolbeltHeight => settingsProvider.ToolbeltHeight;
2426
public static float HudScale => settingsProvider.HudScale;

NomaiVR/ModConfig/OWMLSettingsProvider.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ public class OwmlSettingsProvider : IModSettingProvider
1818
public bool PreventClipping { get; private set; }
1919
public bool FlashlightGesture { get; private set; }
2020
public bool ControllerOrientedMovement { get; private set; }
21+
public bool SnapTurning { get; private set; }
22+
public string SnapTurnIncrement { get; private set; }
2123
public bool AutoHideToolbelt { get; private set; }
2224
public float ToolbeltHeight { get; private set; }
2325
public float HudScale { get; private set; }
@@ -38,6 +40,8 @@ public void Configure()
3840
VibrationStrength = config.GetSettingsValue<float>("vibrationIntensity");
3941
ShowHelmet = config.GetSettingsValue<bool>("helmetVisibility");
4042
ControllerOrientedMovement = config.GetSettingsValue<bool>("movementControllerOriented");
43+
SnapTurning = config.GetSettingsValue<bool>("snapTurning");
44+
SnapTurnIncrement = config.GetSettingsValue<string>("snapTurnIncrement");
4145
EnableGesturePrompts = config.GetSettingsValue<bool>("showGesturePrompts");
4246
EnableHandLaser = config.GetSettingsValue<bool>("showHandLaser");
4347
EnableFeetMarker = config.GetSettingsValue<bool>("showFeetMarker");

NomaiVR/ModConfig/default-config.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,25 @@
7878
"yes": "Controller",
7979
"no": "Head"
8080
},
81+
"snapTurning": {
82+
"type": "toggle",
83+
"value": false,
84+
"title": "Snap turning",
85+
"yes": "Enabled",
86+
"no": "Disabled"
87+
},
88+
"snapTurnIncrement": {
89+
"type": "selector",
90+
"value": "45",
91+
"options": [
92+
"15",
93+
"30",
94+
"45",
95+
"60",
96+
"90"
97+
],
98+
"title": "Snap turn increment"
99+
},
81100
"showGesturePrompts": {
82101
"type": "toggle",
83102
"value": true,

NomaiVR/Player/PlayerBodyPosition.cs

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ internal class PlayerBodyPosition : NomaiVRModule<PlayerBodyPosition.Behaviour,
1414

1515
public class Behaviour : MonoBehaviour
1616
{
17+
public static Action OnSnapTurn;
18+
1719
private Transform cameraParent;
1820
private static Transform playArea;
1921
private static OWCamera playerCamera;
@@ -22,6 +24,10 @@ public class Behaviour : MonoBehaviour
2224
private static PlayerCharacterController playerController;
2325
private static Autopilot autopilot;
2426
private readonly SteamVR_Action_Boolean recenterAction = SteamVR_Actions._default.Recenter;
27+
private static readonly SteamVR_Action_Vector2 turnAction = SteamVR_Actions._default.Look;
28+
private static readonly float snapTurnInputThreshold = 0.15f;
29+
private static bool isSnapTurnInCooldown = false;
30+
private float lastTurnTime;
2531

2632
internal void Start()
2733
{
@@ -95,12 +101,23 @@ internal void Update()
95101
}
96102

97103
UpdateRecenter();
104+
105+
if (ModSettings.SnapTurning)
106+
{
107+
// Check if time has passed and input stick was returned to neutral
108+
if (isSnapTurnInCooldown && Time.time - lastTurnTime > 0.05f && Mathf.Abs(turnAction.axis.x) < snapTurnInputThreshold)
109+
{
110+
isSnapTurnInCooldown = false;
111+
lastTurnTime = Time.time;
112+
}
113+
}
98114
}
99115

100116
public class Patch : NomaiVRPatch
101117
{
102118
public override void ApplyPatches()
103119
{
120+
Postfix<OWInput>(nameof(OWInput.GetAxisValue), nameof(PostGetAxisValue));
104121
Postfix<PlayerCharacterController>(nameof(PlayerCharacterController.UpdateTurning), nameof(PostCharacterTurning));
105122
Postfix<JetpackThrusterController>(nameof(JetpackThrusterController.FixedUpdate), nameof(PostThrusterUpdate));
106123
Prefix<OWCamera>("set_" + nameof(OWCamera.fieldOfView), nameof(PatchOwCameraFOV));
@@ -136,11 +153,25 @@ private static void PatchTurning(Action<Quaternion> rotationSetter)
136153
return;
137154
}
138155

156+
if (ModSettings.SnapTurning && !PlayerState.InZeroG())
157+
{
158+
float turnInput = turnAction.axis.x;
139159

140-
var rotationSource = isControllerOriented ? LaserPointer.Behaviour.MovementLaser : playerCamera.transform;
160+
// If snap turning, only do the snap turn, skip reorienting the play area
161+
if (!isSnapTurnInCooldown && Mathf.Abs(turnInput) > snapTurnInputThreshold)
162+
{
163+
isSnapTurnInCooldown = true;
164+
float sign = Mathf.Sign(turnInput);
165+
Quaternion snapRotation = Quaternion.AngleAxis(GetSnapTurnIncrement() * sign, playerBody.transform.up);
166+
var fromToSnap = Quaternion.FromToRotation(playerBody.transform.forward, snapRotation * playerBody.transform.forward);
141167

142-
var fromTo = Quaternion.FromToRotation(playerBody.transform.forward, Vector3.ProjectOnPlane(rotationSource.transform.forward, playerBody.transform.up));
168+
rotationSetter(fromToSnap * playerBody.transform.rotation);
169+
OnSnapTurn?.Invoke();
170+
return;
171+
}
172+
}
143173

174+
var rotationSource = isControllerOriented ? LaserPointer.Behaviour.MovementLaser : playerCamera.transform;
144175
var magnitude = 0f;
145176
if (!isControllerOriented)
146177
{
@@ -154,6 +185,7 @@ private static void PatchTurning(Action<Quaternion> rotationSetter)
154185
}
155186
}
156187

188+
var fromTo = Quaternion.FromToRotation(playerBody.transform.forward, Vector3.ProjectOnPlane(rotationSource.transform.forward, playerBody.transform.up));
157189
var targetRotation = fromTo * playerBody.transform.rotation;
158190
var inverseRotation = Quaternion.Inverse(fromTo) * playArea.rotation;
159191

@@ -170,6 +202,20 @@ private static void PatchTurning(Action<Quaternion> rotationSetter)
170202
}
171203
}
172204

205+
// Override vanilla input handling for disabling turning while snap turning is enabled
206+
private static void PostGetAxisValue(ref Vector2 __result, IInputCommands command, InputMode mask)
207+
{
208+
if (!ModSettings.SnapTurning || (OWInput.GetInputMode() != InputMode.Character))
209+
{
210+
return;
211+
}
212+
213+
if (command.CommandType == InputConsts.InputCommandType.LOOK)
214+
{
215+
__result = Vector2.zero;
216+
}
217+
}
218+
173219
private static bool PatchOwCameraFOV(OWCamera __instance)
174220
{
175221
//Prevents changing the fov of VR cameras
@@ -183,6 +229,25 @@ private static bool GetOwCameraFOVScaled(OWCamera __instance, ref float __result
183229
if (__instance.mainCamera.stereoEnabled) __result = CameraHelper.GetScaledFieldOfView(__instance.mainCamera);
184230
return !__instance.mainCamera.stereoEnabled;
185231
}
232+
233+
private static float GetSnapTurnIncrement()
234+
{
235+
switch(ModSettings.SnapTurnIncrement)
236+
{
237+
case "15":
238+
return 15f;
239+
case "30":
240+
return 30f;
241+
case "45":
242+
return 45f;
243+
case "60":
244+
return 60f;
245+
case "90":
246+
return 90f;
247+
default:
248+
return 45f;
249+
}
250+
}
186251
}
187252

188253
}
Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using NomaiVR.ModConfig;
2+
using NomaiVR.Player;
23
using UnityEngine;
34

45
namespace NomaiVR.UI
@@ -8,19 +9,22 @@ public class HelmetFollowCameraRotation : MonoBehaviour
89
private Quaternion lastFrameRotation;
910
private const float speed = 0.5f;
1011
private bool smoothEnabled = true;
12+
private bool snapEnabled = false;
13+
private bool snapTurnedLastFrame = false;
1114

1215
private void Start()
1316
{
1417
lastFrameRotation = transform.rotation;
1518

16-
SetSmoothEnabled();
19+
RefreshEnabledSettings();
1720

18-
ModSettings.OnConfigChange += SetSmoothEnabled;
21+
ModSettings.OnConfigChange += RefreshEnabledSettings;
1922
}
2023

2124
private void OnDestroy()
2225
{
23-
ModSettings.OnConfigChange -= SetSmoothEnabled;
26+
ModSettings.OnConfigChange -= RefreshEnabledSettings;
27+
PlayerBodyPosition.Behaviour.OnSnapTurn -= OnSnapTurn;
2428
}
2529

2630
private void LateUpdate()
@@ -32,23 +36,41 @@ private void LateUpdate()
3236

3337
var targetRotation = Camera.main.transform.rotation;
3438

35-
if (smoothEnabled)
39+
if (!smoothEnabled)
3640
{
37-
var difference = Mathf.Abs(Quaternion.Angle(lastFrameRotation, targetRotation));
38-
var step = speed * Time.unscaledDeltaTime * difference * difference;
39-
transform.rotation = Quaternion.RotateTowards(lastFrameRotation, targetRotation, step);
41+
transform.rotation = targetRotation;
4042
}
41-
else
43+
else if (snapEnabled && snapTurnedLastFrame)
4244
{
45+
snapTurnedLastFrame = false;
4346
transform.rotation = targetRotation;
4447
}
48+
else
49+
{
50+
var difference = Mathf.Abs(Quaternion.Angle(lastFrameRotation, targetRotation));
51+
var step = speed * Time.unscaledDeltaTime * difference * difference;
52+
transform.rotation = Quaternion.RotateTowards(lastFrameRotation, targetRotation, step);
53+
}
4554

4655
lastFrameRotation = transform.rotation;
4756
}
4857

49-
private void SetSmoothEnabled()
58+
private void RefreshEnabledSettings()
5059
{
5160
smoothEnabled = ModSettings.HudSmoothFollow;
61+
snapEnabled = ModSettings.SnapTurning;
62+
63+
PlayerBodyPosition.Behaviour.OnSnapTurn -= OnSnapTurn;
64+
65+
if (snapEnabled)
66+
{
67+
PlayerBodyPosition.Behaviour.OnSnapTurn += OnSnapTurn;
68+
}
69+
}
70+
71+
private void OnSnapTurn()
72+
{
73+
snapTurnedLastFrame = true;
5274
}
5375
}
5476
}

0 commit comments

Comments
 (0)