Goal: When the player VR headset leans left/right, the bike will lean with it by sending the input through the Bike Controller script.
When talking about bike leaning, we're discussing rotating the bike across the z axis.
There are two components needed for this feature: 1) the target vector (where the head is), and 2) an independent vector for comparison.
Independent Vector Comparison: The idea is that, as the bike/head rotates, the IVC will act as a comparison point from where the player's head actually is.
Regardless of how this feature is implemented, the IVC needs to be placed at the bottom of the bike and follows the bike's position and x and y rotation without being parented to it. The reason for this is that the bike's own center transform changes with how the Bike Controller script implemented leaning, thus making it an unreliable comparison vector (demonstrated in the right image via green).
However, the IVC will need to take in the angle of the floor the bike is resting on (likely through the bike's rigidbody component), so that it's never the case the IVC is incorrectly atributting the slope as a head movement (represented correctly in the 3rd figure in the left image)
An easy way of having the IVC gameobject follow the bike's position, but not it's rotation, is through Unity's built-in Lazy Follow script and turn off it's "follow rotation" variable.
There would still need to be a script that updates the IVC's x and y rotation, but the Lazy Follow script is worth knowing about for it's positional uses.
In terms of actually translating the head movement into a +1/-1 ratio for the Bike Controller script. The following should work, though we can discuss it in more detail throughout the milestone
With the IVC set up, we now have a base to compare the position of our VR Camera movement.
Wherever the head moves, we can compare the VR Camera position with the IVC to create a Target Direction.
Then, using Vector3.Angle, get the angle in degrees comparing the Target Direction with the IVC transform.up
Note: transform.up is representative of 0 degrees, thus the max angle the player can lean should be 90 degrees
If the angleToCenter is greater than 90 or less than -90, we know that the head is below the floor (or whatever degree we set up to be the deadzone angle).
With that out of the way, convert the angle into a percentage value by dividing it by 90 (our floor).
Because the IVC is split by transform.up, the angle will automatically be either negative or positive depending on which side of "up" the head lies on.
This percentage value can now be sent to the Bike Controller script as it lies between -1 and +1
Full code:
Regardless, this implementation will lead to a problem of erratic bike leaning. If any movement of the head translates to immediate movement of the bike, there should be a deadzone where, if the player's head lies within it, the bike will either lerp back to it's zero input position, or not rotate any more.
We'll need to create a Player Height gameobject that's attached to the IVC. This time, using the exact same code as before, we can create a cone that acts as a deadzone for player head movement.Â
Extra variables can be added for ensuring forward/backward movement wont impact left/right leaning, but this should be a good start.
The PlayerHeight gameobject should rest slightly above where the VR Camera will be as a way of giving wiggle room within the deadzone.
It's unclear whether a cone for a deadzone is useful or a rectangle would be better. Playtesting will need to be done.
This solution does not account for distance. So there are cases where the player is at different distances from the IVC, but at an angle that will result in similar inputs. (ie. the player stands up and walks away)
The solution talks about head movement on a ratio of 0 to 90, but it's unlikely the player will ever lean their head to the floor to tilt the bike. There are two solutions for this:
1. Push the IVC gameobject upward so that the "floor" of the IVC is closer to the head
2. Change the degree the angleToCenter is divided by (such as 60 degrees), and ignore any inputs that are greater than that number,
This can be talked through, but I believe both solutions are viable.