Walkable Udon vehicle (How to) [UdonSharp]

I created a vehicle script that moves the local player along with a vehicle movement (no matter if animated or not) as long as the player is inside the collider box of the vehicle. It preserves the player rotation so that a vehicle does not rotate the player (to avoid motionsickness). You can walk inside the vehicle as if you would walk on normal worldspace ground.

The following code can be used with UdonSharp (https://github.com/Merlin-san/UdonSharp)

Example (Video): https://drive.google.com/drive/u/7/folders/1LbvWpol5gI0V1WSRG8cGGa1tbszvUBnH
Soucecode: https://drive.google.com/open?id=1LbvWpol5gI0V1WSRG8cGGa1tbszvUBnH
Unitypackage with example scene: https://drive.google.com/open?id=1LbvWpol5gI0V1WSRG8cGGa1tbszvUBnH

Sourcecode: (better watch it in the above link where it is formatted correctly)

//----------------------------------------------------------------------------
    using UdonSharp;
    using UnityEngine;
    using VRC.SDKBase;
    using VRC.Udon;
    using UnityEngine.UI;
    /// 
    /// Please credit Reimajo if you use this source code
    /// 
    /// You need to put this script on an empty GameObject under the vehicle, else your vehicle won't animate.
    /// The box collider also needs to be alone on an seperated empty gameobject and set as trigger.
    /// 
    public class VehicleMovesPlayer : UdonSharpBehaviour
    {
        //a panel with a text script on it, to write debug stuff onto
        //remove this before publishing, it is for debugging only
        public GameObject textPanelObject = null;
        //walls that will be activated when the player is inside the vehicle, not needed actually
        public GameObject activatedWhenPlayerIsInVehicle = null;
        //an empty(!) gameObject under the vehicle that has a box collider on it (set as trigger) 
        //to mark an area in which the player is "inside" the vehicle
        public GameObject vehicleBoundsBoxCollider = null;
        //position of vehicle from the last frame
        Vector3 lastFramePosition;
        //rotation of vehicle from the last frame
        Quaternion lastFrameRotation;
        //rotation of vehicle since last frame
        Quaternion stationRotation;
        //movement of vehicle since last frame
        Vector3 stationMovement;
        //new player rotation
        Quaternion newPlayerRotation;
        //current player rotation before changes
        Vector3 currentPlayerPosition;
        //new player position
        Vector3 newPlayerPosition;
        //used to store the vehicle box collider
        Collider vehicleCollider;
        //reference to the local player
        VRCPlayerApi localPlayer;
        ///
        /// This function will be called once when the world loads
        ///
        private void Start()
        {
            localPlayer = Networking.LocalPlayer;
            lastFramePosition = transform.position; //just to prevent nullpointer exceptions
            lastFrameRotation = transform.rotation; //just to prevent nullpointer exceptions
            vehicleCollider = vehicleBoundsBoxCollider.GetComponent<Collider>(); //getting the box collider from the vehicle
            activatedWhenPlayerIsInVehicle.SetActive(false); //disable vehicle walls
            //remove this before publishing, it is for debugging only
            textPanelObject.GetComponent<Text>().text = "Script loaded successfully"; //just to confirm that the script was loaded
        }
        ///
        /// This function will be called once every frame
        ///
        private void Update()
        {
            //read the current player position and store it
            currentPlayerPosition = localPlayer.GetPosition();
            //just for debugging, uncomment before publishing
            textPanelObject.GetComponent<Text>().text = "x:"+currentPlayerPosition.x.ToString()+", y:"+ currentPlayerPosition.y.ToString() + ", z:" + currentPlayerPosition.z.ToString();
            {
                if (vehicleCollider.bounds.Contains(currentPlayerPosition))
                {
                    //remove this before publishing, it is for debugging only
                    textPanelObject.GetComponent<Text>().text = "player in station";
                    //enables the vehicle walls
                    activatedWhenPlayerIsInVehicle.SetActive(true);
                    //calculate the station movement since last frame
                    stationMovement = transform.position - lastFramePosition;
                    //calculate the station rotation since last frame
                    stationRotation = transform.rotation * Quaternion.Inverse(lastFrameRotation);
                    //moving the player with the vehicle movement
                    newPlayerPosition = currentPlayerPosition + stationMovement;
                    //preserving the player rotation
                    newPlayerRotation = localPlayer.GetRotation() * Quaternion.Inverse(stationRotation);
                    //teleport player since you can't change the position/rotation directly afaik
                    localPlayer.TeleportTo(newPlayerPosition, newPlayerRotation);
                    //Yes, could bypass all of the code above and the variable declarations by writing the teleportation in one line of code.
                    //I'm not doing this since it is harder to read and doesn't really give me any benefits as long as I don't use local variables.
                    //Anyway, this is how it would look like:
                    //localPlayer.TeleportTo(localPlayer.GetPosition() + transform.position - lastFramePosition, localPlayer.GetRotation() * Quaternion.Inverse(stationRotation));
                }
                else
                {
                    //disables the vehicle walls
                    activatedWhenPlayerIsInVehicle.SetActive(false);
                }
                //storing vehicle position from this frame
                lastFramePosition = transform.position;
                //storing vehicle rotation from this frame
                lastFrameRotation = transform.rotation;
            }
        }
    }
    //Todo: Calculate the rotation for an offset point on which the player is actually standing
    //      to improve the position preserving when the vehicle rotates (offset to the middle)
4 Likes

Since you’re using UdonSharp for this, it would probably be best to indicate that in the title with something like [U#] / [UdonSharp]

Done. Not sure why a post needs to be 20 characters long but here you go.

2 Likes

Please use triple backticks to format code blocks!

well hello there, could you make a tutorial video how you made car drive able? im up to make a racing + chill world but im hella new to this kind of stuff, so yah any help with that would be really grateful.