Search Unity

Discussion A better method to prevent wall clipping in VR

Discussion in 'XR Interaction Toolkit and Input' started by FernwehSmith, Oct 7, 2022.

  1. FernwehSmith

    FernwehSmith

    Joined:
    Jan 31, 2022
    Posts:
    36
    Hello All,
    I was hoping to get suggestions on how to handle the camera clipping walls. I don't want to fade to black. What I'd like to do is if the user starts poking their head into a wall, push the character backwards a little bit so that they cant peak through. The only solutions I've found so far is using a character controller and setting the center of the controller every frame so that the x and z positions match those of the camera. An example is in the video linked down below.

    There's 3 problems with this.
    - Firstly (and least important) it prevents the user from leaning over waist height items like tables and benches. - Second, while this approach does prevent clipping with the walls, its very 'snappy'. When a user pushes in too far it snaps them backwards suddenly, which is very disorienting. I'd like to smoothly push them back if possible.
    - Third (and most oddly) I've found when I pick up a grabbable item, and then try to move using continuous move, if the item is colliding with the character controller, it pushes the controller away with a lot of force.

    I was wondering if anyone has some tips for how to approach this. I've been search all day and not found anything that seems to be widely accepted as the "Correct" thing to do. Is there such a method? Have I missed something very obvious?

     
  2. FernwehSmith

    FernwehSmith

    Joined:
    Jan 31, 2022
    Posts:
    36
    In-case anyone comes across this and is having the same issues, I believe a workable solution. I'm still refining it but I'll provide a brief overview below. Later on I'll share a more in-depth explanation.

    In my custom CharacterControllerDriver, instead of just setting the CharacterController (from now on referred to as CC) to the camera position on the Y plane every fixed update, I instead use Physics.CapsuleCast in the Y plane direction towards the camera to determine if moving the CC would cause it to overlap with something. If there is no hit, I update the CC center as described in the above video. If there is a hit, as along as its no too close, then I will move the CC just enough so that it doesn't overlap with the object it would have otherwise hit. As this doesn't actually stop the camera from clipping into objects, I then perform a raycast from the camera's base position (which I define as the World X and Z values of the CC's center, and the World Y value of the camera) towards the camera's world position. If there is a hit, I know the camera is passing through something and can either black it out or push the CC back a bit using the move function.

    This method (in experience so far) is actually pretty good for preventing/handling wall clipping without any of the frustrating stuff like the super jumpy pushbacks that happen when you do clip a wall, or the game breaking stuff like walking through a wall and then having your CC snap to position, effectively letting you phase through things.

    There are two caveats I wanted to mention.
    Firstly, it's important to move the points for the capsule cast opposite to the direction of the cast by a small amount (I've found 1cm to be plenty). Then you need to add the amount you pushed the capsule back to the maxDistance. This is because I found that the volume taken up by the defined capsule overlaps or even just touches a collider, then the CapsuleCast won't recognise a hit with that object. And then you get super odd results.

    Second, if the camera and CC are separated (say because you're leaning over a virtual table) and then you try to rotate using a TurnProvider (I've not checked with a snap turn provider but it happens with the continuous), then very weird and things happen, and you get teleported all over the place. I'm not exactly sure why this is, but I've found that the solution (as hacky as it is) is to recreate the turn provider, and in the "TurnRig" function, before the line
    xrOrigin.RotateAroundCameraUsingOriginUp(turnAmount);
    store the world position of the CC center, then after that line is called, restore the CC's center. It Looks something like this

    Code (CSharp):
    1. var worldCenter = transform.TransformPoint(CC.center);
    2. xrOrigin.RotateAroundCameraUsingOriginUp(turnAmount);
    3. CC.center = transform.InverseTransformPoint(worldCenter );
    Not exactly why this works but it does. (Also this assumes you've got this custom turn provider on the XROrigin and that you have a reference to the CC already stored.)

    I hope this helps someone. I know its very basic. I hope to have a far more thorough explanation later on. Please feel free to ask any questions if you have them.