The Time of Conundrum Lesson 1 – How to develop a reduced-nausea first person controller in VR

by Antony Vitillo, Friday, March 4, 2016 at 4:10 PM

Hello everyone and welcome to the first episode of this serie that talks about what we’ve learned during the developing of our concept demo "The time of Conundrum".

Today I just want to talk about one of the biggest problem about Virtual Reality: locomotion. OK, I know, you’re thinking this is just another post about our ImmotionRoom system, but no, this is not a post about how cool our system is, about how it has the potentiality to remove all the motion sickness in player movement and all this stuff (that is all true, anyway ;) ). Today I just want to talk about traditional input systems, because yeah, Vive room-scale and ImmotionRoom walk-scale are cool, but not everyone has them at this moment, so I think we, as developer, should publish games that support at least a double input scheme, with one using gamepad or other traditional input methods, to let the greatest number of players experience our games.

undefined

But this leads us to a problem: gamepad and keyboards, with the traditional first person controllers, lead to the so-called motion sickness, because of the well-known problem of the disparity of the visual cues from the proprioception of the body, so they’re not good to be used. The dev community has created a lot of interesting alternatives, where movement is performed using teleporting schemes, but, let’s be sincere, for most of games, this kind of movement mechanics just su**s. Can you imagine some kind of first person shooter with teleporting mechanics? It would be weird…

So, how can we solve the problem? Honestly I don’t know how to solve it, but I know how to mitigate it, with a wonderful method: my buddy Gianni suffers a lot from motion sickness, so he wasn’t able to play "The Time of Conundrum" for more than 1 minute on Gear VR with a traditional FPS controller… but after having applied this method, he was able to stay even 10 minutes inside the game! (This seems one of those sentences you can read on banners on the web that promise you to loose weight in one month using a strange method!)

undefined

Actually, this ISN’T even OUR method. I’ve read it on Gamasutra and other websites, but I’ve discovered it is still not widespread, so I’m helping in spreading the word about it, because we tested it and we found it is a valid method. The secret is: REMOVE ALL THE ACCELERATIONS AND DECELERATIONS. So, your player can’t go faster or go slower, it has no realistic physics: it goes immediately from 0 to a constant speed when the user wants to go forward and decreases from that speed to 0 when the user provides no input. I know, I know, it is not a great way to provide a realistic control, but… do you really think that teleportation is better?

This method works because the human body feels the sickness when it feels an acceleration, so removing all of them, makes the game way more comfortable.

But… how can you remove them? Well, let’s make our hands a bit dirty and dig into the code of the OVRPlayerController inside Unity (here I’m using code from Oculus Utilities v0.1.3).

At first remove all references to the word acceleration.

At line 34, replace this:

31
32
33
34
 /// <summary>
 /// The rate acceleration during movement.
 /// </summary>
 public float Acceleration = 0.1f;

with this:

31
32
33
34
 /// <summary>
 /// The rate ConstantSpeed during movement.
 /// </summary>
 public float ConstantSpeed = 0.3f;

and this (line 85):

85
 private Vector3 MoveThrottle = Vector3.zero;

with:

85
 private Vector3 MoveSpeed = Vector3.zero;

Substitute all occurrence of the names Acceleration and MoveThrottle accordingly. Now we don’t consider acceleration anymore, we consider only speed.

A simple name change isn’t enough, though. We have to wipe out all the changes of speed present inside the code. So, let’s go to lines 180-182 and comment out the following lines:

180
181
182
 MoveSpeed.x /= motorDamp;
 MoveSpeed.y = (MoveSpeed.y > 0.0f) ? (MoveSpeed.y / motorDamp) : MoveSpeed.y;
 MoveSpeed.z /= motorDamp;

to remove damping (that is a speed variation). Similarly, comment out lines 197-201, to remove change in speed due to uneven ground:

197
198
199
200
201
 if (Controller.isGrounded && MoveThrottle.y <= transform.lossyScale.y * 0.001f)
 {
	bumpUpOffset = Mathf.Max(Controller.stepOffset, new Vector3(moveDirection.x, 0, moveDirection.z).magnitude);
	moveDirection -= bumpUpOffset * Vector3.up;
 }

OK, we’ve killed a bit of reality of our game. We feel bad, I know. But we must go on.

Before line 216 (at the beginning of the UpdateMovement method), add this line:

216
 MoveSpeed = Vector3.zero;

to initialize the movement of the player to zero at each frame: so, if player will not press any input key, we’ll have a still player.

Comment out line 251:

251
 MoveScale *= SimulationRate * Time.deltaTime;

because this talks about acceleration (in disguise). Then at line 304, substitute this:

304
 moveInfluence = SimulationRate * Time.deltaTime * Acceleration * 0.1f * MoveScale * MoveScaleMultiplier;

with:

304
 moveInfluence = ConstantSpeed * 0.1f * MoveScale * MoveScaleMultiplier;

Essentially, considering that MoveScale will always be 1 (if the controller is grounded), we’ll have a moveInfluence that is proportional to the constant speed and the delta time of execution of the game.

Et… voilà, we have our nausea-reduced-player controller! Try it your own and verify if you feel less nausea, too! (and be happy like a reindeer playing a guitar)!!

undefined

 

In case you were wondering about the rotational movement… well, actually I still haven’t find a solution for this problem, but I can say three things:

1) At line 327, substituting this:

327
 euler.y += secondaryAxis.x * rotateInfluence;

with this:

327
328
329
330
 if (secondaryAxis.x > 0.1f)
     euler.y += rotateInfluence;
 else if (secondaryAxis.x < -0.1f)
     euler.y -= rotateInfluence;

makes sure you’re rotating always at the same speed, independently from the present time pressure on your gamepad thumbstick. This helps a bit in having a constant rotation speed.

2) One way to reduce rotational nausea is to make the rotation non-continuous, so make your player rotate jerkily, having short abrupt burst of rotation of a little degrees amount alternated with longest periods of stillness (the same mechanism provided by the RotationRatchet mechanism of the class OVRPlayerController). I’ve tried this method and I think that it is true that it reduces the nausea, but it makes the player controller unusable for most situations.

3) If the player does not rotate using gamepad, but rotates only using a fully-rotating chair, it is far better and the problem is solved… but you can’t force your player to use it.

One final notice: I provided this code as an example… I’m quite sure there will be somewhere a bug that will set your PC on fire and that if a StackOverflow pro programmer read it, it would have a heart attack… but it somewhat works… take it as a start to make your own implementation!

Hope this will be of help to someone… if you have suggestions, feel free to point them out in the comments!

If you liked this tutorial, please consider supporting us in any ways, like having a look at our ImmotionRoom system videos, subscribing to our newsletter or sharing this article on the social networks!

Have a nice nausea-free day!