XR Interaction: Character Controller and Teleportation Disconnect

I’ve set up the XR Interaction Toolkit’s built-in teleportation and it works fine.
I’ve also created a continuous movement script that uses the character controller. This also works fine.

Having both enabled along with the character controller though, is the problem.

I’m following a script by Valem, here are the relevant bits:

    void FixedUpdate()
    {
        CapsuleFollowHeadset();
       
        Quaternion rotate = Quaternion.Euler(0, camera1.transform.eulerAngles.y, 0);
        Vector3 move = rotate * new Vector3(vectorValue.x, 0, vectorValue.y);

        characterController.Move(move * speed * Time.fixedDeltaTime);

        bool isGrounded = CheckIfGrounded();
        
        if (isGrounded)
        {
            fallingSpeed = 0;
        }
        else
        {
            fallingSpeed += gravity * Time.fixedDeltaTime;
        }

        characterController.Move(Vector3.up * fallingSpeed * Time.fixedDeltaTime);
        Debug.Log(isGrounded);
        Debug.Log(fallingSpeed);

    }

   void CapsuleFollowHeadset()
    {
        characterController.height = rig.cameraInRigSpaceHeight + additionalHeight;
        Vector3 capsuleCenter = transform.InverseTransformPoint(rig.cameraGameObject.transform.position); 
        characterController.center = new Vector3(capsuleCenter.x, characterController.height/2 + characterController.skinWidth, capsuleCenter.z); 
    }

If the CapsuleFollowHeadset() function isn’t there, specifically the code at line 33, teleportation will not work. When attempting to teleport the XR Rig will teleport to the spot and immediately appear where it was before teleporting. Blink and you’ll miss it. I’m trying to understand why that is. Removing the character controller will also allow for teleportation to work, but then of course it’s needed in order to move.

This is the setup of the scene and the components attached to the XR Rig

This is a video showing the teleportation/character controller issue more clearly

What’s going on guys it’s probably simple and it’s just flying over my head. Thanks!

I’m not sure where the value of vectorValue is being set in your code snippet, but I’m guessing you’re storing that value before the teleport happens, but then still using it afterward so your Movement Script is moving the character controller back to where it was.

The teleportation (and any continuous movement) happens through the Locomotion Provider component, which executes at a script order of -210 (earlier than default scripts). If you are reading the value before that, it will have stale results. You can subscribe to the beginLocomotion/endLocomotion events to make sure you execute your code after the teleport has occurred.

I believe I am having a similar issue where I am using a character controller and I am trying to teleport my player to different coordinates by changing the transform of my XR Origin.

If my player does not move before the teleport, everything works fine. If the player moves then the teleport does not work. I suspect that, as @chris-massie mentioned, my movement script is moving the character controller back. If I remove the character controller from the XR Origin everything works, but I am using the character controller to handle collisions with my environement (i.e not have my player go through walls, or bounce off of them).

I am trying to subscribe to the beginLocomotion events, but after reading up on the documentation I am still struggling to set that up correctly. It’s my first time listening to events.

I am trying to add the following lines to my GameManager script:

    private LocomotionProvider locomotionProvider;

    void OnEnable() {
        locomotionProvider.endLocomotion += TestAction;
            }

    void OnDisable() {
        locomotionProvider.endLocomotion -= TestAction;
    }

    void TestAction(LocomotionSystem locomotion)
    {
        Debug.Log("End Locomotion");
    }

This code gives me a “NullReferenceExeption: Object reference not set to an instance of an object”

(1) I understand why I am getting the error, but am not sure how to fix it. From what I can tell, the LocomotionProvider script is not attached to any object in my scene so I don’t know how to reference it.

(2) As a second step, I am not sure how to wait for the endLocomotion action before triggering my teleportation.

Any help would be very much appreciated, thank you.

@mamabox
(1) Since your locomotionProvider field is private, it won’t show up in the Inspector for you to set it, so it will always be null. You need to add the SerializeField attribute to it so you can serialize the reference to that component from the Inspector window.

[SerializeField]
private LocomotionProvider locomotionProvider;

The LocomotionProvider is an abstract base class so there won’t be a component on a GameObject of that type, but you can have one of its derived component types like TeleportationProvider on a GameObject in your scene. Once you add SerializeField, you should be able to set the reference to the Teleportation Provider component to fix the NullReferenceException. You can also change the type of the field in your script to TeleportationProvider if you only want to allow that type instead of other components like Snap Turn Provider which also derive from that abstract base class.

(2) Your TestAction method will be called when the teleportation finishes, so you can put your code that needs to wait until after teleportation happens into that method.

Another way you can ensure your code always executes after teleportation which you may find easier is by setting the Script Execution Order of your script to run after the Teleportation Provider script. Drag your script into that window and then set the value to -209 to make it execute its Update method after Teleportation Provider (which has a value of -210), and then click Apply. The script execution orders of the scripts in XRI can be found in the XRInteractionUpdateOrder scripting API docs.