Image

Dynamic Slope Quantization for VR Comfort

Background

As a preface to this, we will be discussing terms like “smooth movement” (moving the player smoothly throughout the game-world without them having to move in real life), “teleportation” (immediately snapping the player to a virtual destination after a brief “blink” of the camera), and “room-scale” (where a player has enough space in real life to walk around in (like a big room) while playing the VR experience, enabling them to move about in their VR environment as they’d like).

As VR developers, we run into many challenges, least of which is making sure that people who adorn a VR headset remain comfortable while in our experiences. Often, whether or not an experience is comfortable is based on how the user moves or is moved. While moving the player in a smooth way would seem to be more natural (since many of us do that in real life to get around), it will often cause motion sickness in VR, whereas instantaneous movements in VR (like teleportation) can be more comfortable. This VR motion sickness cannot only occur when moving horizontally but also vertically. This leads to many VR experiences having very flat environments to prevent any vertical motion issues.

We have a history of building highly detailed worlds in our games. We’ve been supporting both flat-screen and VR experiences using the same game environments for both. While making flat levels or spaces might work well in a VR-only title, it would be much more scrutinized on a flat-screen where players are much less likely to have motion sickness and often expect a wider breadth of navigation capabilities throughout environments. Also, there are game design advantages to conforming the player movement to the environment, as opposed to locking them onto planes that could potentially not match that same environment.

I’ve developed a technique for allowing players to traverse slopes, stairs, or any amount of reasonable vertical movement in VR by taking advantage of the fact that instantaneous travel has some comfort benefits. It also allows you to keep people in room-scale experiences reasonably close to the floor of the game environment. We’ve been calling it Dynamic Slope Quantization internally.

Our Method

Our VR camera is attached to an in-game object that represents the real-world floor of the player; let’s call it the floor component. That floor component is attached to a capsule, which is our character representation and moves around the in-game environment.

[Graphic courtesy of Derrick Robinson]

As the player walks around their room in VR, we are constantly moving that player capsule to match an equivalent in-world position if possible. When we move that capsule, we have to offset the floor component attached to it by the opposite of the movement of the capsule. This ensures that the VR camera doesn’t compound movements and that the capsule “syncs” up to the player’s real-world position. This also includes offsetting the height that the capsule moves. The effect is that you’re walking on a flat floor. If you didn’t offset the player location, you would have a constant movement effect, the capsule would never truly match the real-world player location, and you’d get a roller coaster feeling when going up and down the in-game environment.

At this point, you’ll want to sum up all the vertical distances that the capsule has moved. Then, you’ll want to figure out a height that you want the resynchronization of the floor and capsule to occur. We chose 20 cm which feels pretty good. Once this distance has been crossed by the capsule, you take the floor component and snap it back into the capsule where it would normally reside. This should be wherever we’d expect the floor to be in-game. That’s all it should take for the effect to work.

Here’s some pseudo-code to give you an idea of what that would look like. This code example uses Unreal Engine 4 structures but should be easy to implement in your environment.


//Code to move capsule to where the HMD is located
//CapsuleTravel is added to over time to keep track of how far
//the capsule has moved over several updates
float CapsuleTravel = 0.f;

//Find the HMD location in the Game World Space
FVector HMDPosition = GetHMDPositionInGameWorld();

//Find the Player Capsule location in the Game World Space
FVector CapsulePosition = GetCapsulePositionInGameWorld();

//Build a game world movement vector to tell the movement component where to move.
//There are probably more efficient ways to do this, but it works for these purposes.
FVector MovementDirection = (HMDPosition - CapsulePosition) * FVector(1,1,0);

//Move the character.
MoveCharacter(MovementDirection);

//AFTER PLAYER HAS MOVED to be where the HMD is located. This ideally would happen before the
//player would see anything.
FVector UpdatedCapsuleLocation = GetCapsulePositionInGameWorld();
//Find the vertical distance the capsule traveled after moving to match the HMD.
float HeightDelta = UpdatedCapsuleLocation.Z - CapsulePosition.Z;

//Accumulate the total vertical distance the capsule has traveled.
CapsuleTravel += HeightDelta;

//If the distance goes over the vertical distance threshold, go ahead and snap the floor component back into it’s default relative location.
if(FMath::Abs(CapsuleTravel) >= VerticalThreshold) // We chose VerticalThreshold to be 20cm.
{
    ResetHMDVerticalHeight();
}
//If we haven’t crossed the vertical distance threshold, we need to offset the floor component
//by the amount the capsule traveled to prevent the player’s HMD from also moving vertically.
//We also want to offset the floor by the amount the player capsule was told to move to make sure it only moved the amount we told it to
//and not double it.
else
{
    FloorComponent.AddWorldOffset(FVector(-MovementDirection.X,-MovementDirection.Y,-HeightDelta));
}

In order to keep the player as close to the floor as possible, you’ll want to sneak in floor height resets when possible and comfortable. For example, I found it worked well to immediately sync the floor height when the player begins to smooth-move and then to not quantize the player’s floor height until the player stops smooth-moving. Upon the player’s execution of teleporting or snap turning is also a good opportunity to reset the player floor height.

While we want to make sure everyone is comfortable playing our games, for our future VR games, we see this feature as a comfort option. We hope this little trick can help VR developers make their environments with fewer compromises, and not have to worry about the consequences for people who are susceptible to motion sickness in VR.

-Karl Johnson

Senior Software Engineer

twitter:@smapty