Search code examples
c#unity-game-enginegame-physicsrigid-bodieslerp

Move Inside Pipe Like F-Zero GX


I've been working on this project for a while - I've been trying to get a simple cube that the player can move left or right with the arrows the ability to rotate around a pipe, similar to how racers can move around a pipe in F-Zero GX:

https://www.youtube.com/watch?v=RlS1i7aCnvg

Now, I know I'm not the first one to try to take a stab at implementing these physics, as I've read through two-three threads from people that have been trying to implement similar techniques. The problem is that I've tried playing around with these different methods or variations of them. One of these have gotten to act VERY closely to the behavior I wanted - but the player cube would STILL rotate on its X axis unexpectedly after the cube makes it over halfway up the full pipe's wall from the ground. Here's a visual of the level build I'm talking about:

enter image description here

The whole idea of this is, the cube can "move" or "walk" around the full wall of the pipe, like you see in the pipe sections of the F-Zero video, while of course still moving forward. I have a cylinder that is actually inside the one you see, which is actually just a convex trigger - so that is used to ensure that the gravity tick of the player cube is off while the cube is inside the pipe model.

I've gotten close to making this work, but it's a matter of seeing the player being able to move all the way in a full circle, ie. move in a circular motion completely back to the bottom or where the player began when entering the pipe. However, the cube likes to "flip" when it's a little over halfway done with completing the wall traveling. I've been reading in another post that someone actually changed the rigidbody of the object so that the "ship" stays upright, would that have something to do with it?

https://forum.unity.com/threads/f-zero-esque-control-question.157909/

"Essentially I gave my vehicle a hover-skirt."

Should I consider changing the shape of my rigidbody accordingly? What is the best resource to do this? Or perhaps I should use a character controller instead? I'm still leaning on a rigidbody setup, but I've opened my mind to the possibility after reading about those.

This Raycast code got very close to what I wanted, no dice:

float distDown = Mathf.Infinity;
            RaycastHit hitDown;
            if (Physics.SphereCast(transform.position, 0.25f, -transform.up, out hitDown, 5))
            {
                distDown = hitDown.distance;
            }
transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(Vector3.Cross(transform.right, hitDown.normal), hitDown.normal), Time.deltaTime * 1.0f);

Another possibility I've tried to explore is creating your own gravity pull - I even have a class that tries to do this, FauxGravity, which is attached to the object the player collides with (in this case the cylinder):

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FauxGravity : MonoBehaviour {

    public Transform attractor;
    public Transform player;
    public Transform collider;
    Rigidbody attractRB;
    Rigidbody playerRB;
    Vector3 myNormal;
    public Vector3 vectorFromPipeCenter;
    public Vector3 forwardPipeVector;
    Vector3 project2Center;
    public Vector3 pipeGravityPull;
    public int gravity = 1;

    // Use this for initialization
    void Start () {

        //Physics.gravity.magnitude = 0;
    }

    // Update is called once per frame
    void Update () {

    }

    //private void OnCollisionEnter(Collision collision)
    //{
    //    //collision.gameObject.GetComponent<Rigidbody>().transform.up = Vector3.zero;
    //    //player.gameObject.GetComponent<Rigidbody>().useGravity = false;
    //    myNormal = playerRB.transform.up;
    //    //playerRB = collision.gameObject.GetComponent<Rigidbody>();

    //    gravity = 0;
    //    //playerRB.isKinematic = true;
    //    //player.gameObject.GetComponent<Rigidbody>().AddRelativeForce()
    //}

    public void FixedUpdate()
    {
        if (gravity == 0)
        {
            //playerRB.isKinematic = true;
            //Debug.Log("Gravity is 0.");
            attractRB = attractor.GetComponent<Rigidbody>();
            playerRB = player.GetComponent<Rigidbody>();
            //playerRB.AddForce(-10 * playerRB.mass * myNormal);
            Debug.Log("PlayerRB position: " + playerRB.position);
            Debug.Log("AttractRB position: " + attractRB.position);
            vectorFromPipeCenter = playerRB.position - attractRB.position;
            vectorFromPipeCenter.z = 0;
            //vectorFromPipeCenter.Normalize();
            Debug.Log("Player distance from pipe center: " + vectorFromPipeCenter.magnitude);
            Debug.Log("Player vector from pipe center" + vectorFromPipeCenter);
            //vectorFromPipeCenter = attractRB.position - playerRB.position;
            Debug.Log("playerRB forward is " + playerRB.rotation.z);
            Debug.Log("playerRB magnitude is " + player.forward.magnitude);
            forwardPipeVector = player.forward.magnitude * Vector3.forward;
            Debug.Log("Player forward vector? " + forwardPipeVector);
            // or
            //Vector forwardPipeVector = pipeTransform.forward;
            // And finally
            project2Center = Vector3.Project(vectorFromPipeCenter, forwardPipeVector);
            Debug.Log("What is project2Center? " + project2Center);
            float radiusFromCrossectionCenter = vectorFromPipeCenter.magnitude;
            double playerY = System.Convert.ToDouble(playerRB.position.y);
            double playerX = System.Convert.ToDouble(playerRB.position.x);
            //float inverseTan = System.Convert.ToSingle(System.Math.Atan(playerY / playerX));
            //Debug.Log("Normal is: " + Quaternion.AngleAxis(inverseTan, forwardPipeVector));
            // pipe pull force = distance from pipe center to power 2
            //pipeGravityPull = Quaternion.AngleAxis(inverseTan, playerRB.transform.forward) * project2Center * Mathf.Pow ( (radiusFromCrossectionCenter * 1 ), 2 );
            pipeGravityPull = new Vector3(playerRB.position.x, radiusFromCrossectionCenter - playerRB.position.y, 0)/Mathf.Sqrt(Mathf.Pow(playerRB.position.x,2) + Mathf.Pow((radiusFromCrossectionCenter-playerRB.position.y),2));
            Debug.Log("Pipe gravity vector? " + pipeGravityPull);
            //playerRB.useGravity = true;
            Debug.DrawLine(pipeGravityPull, pipeGravityPull);
            Debug.Log("Adding force from FG");
            //playerRB.AddForce(pipeGravityPull);
        }
        if (gravity == 1)
        {
            player.GetComponent<Rigidbody>().useGravity = true;
            //playerRB.isKinematic = false;
        }
    }

    private void OnCollisionExit(Collision collision)
    {
        //Debug.Log("Gravity is 1 again.");
        //player.gameObject.GetComponent<Rigidbody>().useGravity = true;
        //gravity = 1;
        //playerRB.useGravity = true;
        playerRB.isKinematic = false;
        //playerRB.AddForce(10, 20, 0);
    }

    void gravityAttract(Collider colliderObject)
    {
        var rb = colliderObject.GetComponent<Rigidbody>();
        rb.AddForce(Vector3.down * 30, ForceMode.Force);
        rb.AddForce(Vector3.up * 30, ForceMode.Force);
    }
}

Do I need to move my FixedUpdate logic to an Update method instead? The last algorithm I've explored in this approach is essentially ensuring the pull is always equal to 1 by taking the vector of the player with the cross section of the pipe he/she is traveling in, according to my dad (he's an astrophysicist).

Here's my player's movement class, which has several commented and uncommented attempts to rotate the cube in order to allow the downside of the cube to always face the pipe wall as it climbs:

using System;
using UnityEngine;

public class PlayerMovement : MonoBehaviour {

    public Rigidbody rb;

    public float forwardForce = 2000f;
    public float sidewaysForce = 500f;
    public Boolean fauxGravity = false;
    public Vector3 distanceFromPipeCenter = new Vector3(0, 0, 0);
    public Vector3 pipePull = new Vector3(0,0,0);

    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame use fixed update for Unity Fizzix
    void FixedUpdate () {
        //distanceFromPipeCenter.Normalize();
        //add forward force
        rb.AddForce(0, 0, forwardForce * Time.deltaTime);

        if (Input.GetKey(KeyCode.RightArrow)/* && !fauxGravity*/)
        {
            rb.AddForce(sidewaysForce * Time.deltaTime, 0, 0, ForceMode.VelocityChange);
        }

        if (Input.GetKey(KeyCode.LeftArrow)/* && !fauxGravity*/)
        {
            rb.AddForce(-sidewaysForce * Time.deltaTime, 0, 0, ForceMode.VelocityChange);
        }
        if (pipePull.x == 0)
        {
            pipePull.x = 1;
        }
        if (pipePull.y == 0)
        {
            pipePull.y = 1;
        }
        //transform.rotation = Quaternion.identity;
        //pipePull.z = 0;
        if (Input.GetKey(KeyCode.RightArrow) && fauxGravity)
        {
            Debug.Log("Right pressed");
            //Debug.Log("Rotation before: " + rb.rotation);
            //rb.rotation = rb.rotation * Quaternion.FromToRotation(rb.transform.up, pipePull);
            //rb.rotation = Quaternion.Lerp(rb.rotation, Quaternion.LookRotation(Vector3.Cross(rb.transform.right, pipePull), pipePull), Time.deltaTime * 5.0f);
            //Debug.Log("Rotation after: " + rb.rotation);
            //if (distanceFromPipeCenter.y < 0)
            //{
            //    Debug.Log("Right A, pull positive: " +pipePull);
                //rb.AddForce(sidewaysForce * pipePull.x * Time.deltaTime, sidewaysForce * pipePull.y * Time.deltaTime, 0, ForceMode.VelocityChange);
            //}
            //else
            //{
            //    Debug.Log("Right B, pull negative: " + distanceFromPipeCenter);
            //    rb.AddForce(sidewaysForce * pipePull.x * Time.deltaTime, -sidewaysForce * pipePull.y * Time.deltaTime, 0, ForceMode.VelocityChange);
            //}
            //Debug.Log(rb.angularVelocity);
            //float headingDeltaAngle = Input.GetAxis("Horizontal") * Time.deltaTime * sidewaysForce;
            //Quaternion headingDelta = Quaternion.AngleAxis(headingDeltaAngle, transform.up);
            //align with surface normal
            //transform.rotation = Quaternion.FromToRotation(transform.up, distanceFromPipeCenter) * transform.rotation;
            //apply heading rotation
            //transform.rotation = headingDelta * transform.rotation;
        }
        if (Input.GetKey(KeyCode.LeftArrow) && fauxGravity)
        {
            Debug.Log("Left pressed");
            //Debug.Log("Rotation before: " + rb.rotation);
            //rb.rotation = rb.rotation * Quaternion.FromToRotation(rb.transform.up, pipePull);
            //rb.rotation = Quaternion.Lerp(rb.rotation, Quaternion.LookRotation(Vector3.Cross(rb.transform.right, pipePull), pipePull), Time.deltaTime * 5.0f);
            //Debug.Log("Rotation after: " + rb.rotation);
            //if (distanceFromPipeCenter.y < 0)
            //{
            //    Debug.Log("Left A, pull positive: " +pipePull);
                //rb.AddForce(-sidewaysForce * pipePull.x * Time.deltaTime, sidewaysForce * pipePull.y * Time.deltaTime, 0, ForceMode.VelocityChange);
            //}
            //else
            //{
            //    Debug.Log("Left B, pull negative: " + distanceFromPipeCenter);
            //    rb.AddForce(-sidewaysForce * pipePull.x * Time.deltaTime, -sidewaysForce * pipePull.y * Time.deltaTime, 0, ForceMode.VelocityChange);
            //}
            //Debug.Log(rb.angularVelocity);
            //float headingDeltaAngle = Input.GetAxis("Horizontal") * Time.deltaTime * sidewaysForce;
            //Quaternion headingDelta = Quaternion.AngleAxis(headingDeltaAngle, transform.up);
            //align with surface normal
            //transform.rotation = Quaternion.FromToRotation(transform.up, distanceFromPipeCenter) * transform.rotation;
            //apply heading rotation
            //transform.rotation = headingDelta * transform.rotation;
        }
        if (fauxGravity)
        {
            rb.useGravity = false;

            ///*We get the user input and modifiy the direction the ship will face towards*/
            //float yaw = Time.deltaTime * Input.GetAxis("Horizontal");
            ///*We want to save our current transform.up vector so we can smoothly change it later*/
            //Vector3 prev_up = rb.transform.up;
            ///*Now we set all angles to zero except for the Y which corresponds to the Yaw*/
            //transform.rotation = Quaternion.Euler(0, yaw, 0);

            //RaycastHit hit;
            //if (Physics.Raycast(transform.position, -prev_up, out hit))
            //{
            //    Debug.DrawLine(transform.position, hit.point);

            //    /*Here are the meat and potatoes: first we calculate the new up vector for the ship using lerp so that it is smoothed*/
            //    Vector3 desired_up = Vector3.Lerp(prev_up, hit.normal, Time.deltaTime /** pitch_smooth*/);
            //    /*Then we get the angle that we have to rotate in quaternion format*/
            //    Quaternion tilt = Quaternion.FromToRotation(transform.up, desired_up);
            //    /*Now we apply it to the ship with the quaternion product property*/
            //    transform.rotation = tilt * transform.rotation;

            //    /*Smoothly adjust our height*/
            //    //smooth_y = Mathf.Lerp(smooth_y, hover_height - hit.distance, Time.deltaTime * height_smooth);
            //    //transform.localPosition += prev_up * smooth_y;
            //}
            //float distForward = Mathf.Infinity;
            //RaycastHit hitForward;

            //if (Physics.SphereCast(transform.position, 0.25f, -transform.up + transform.forward, out hitForward, 5))
            //{
            //    distForward = hitForward.distance;
            //}
            float distDown = Mathf.Infinity;
            RaycastHit hitDown;
            if (Physics.SphereCast(transform.position, 0.25f, -transform.up, out hitDown, 5))
            {
                distDown = hitDown.distance;
            }
            //float distBack = Mathf.Infinity;
            //RaycastHit hitBack;
            //if (Physics.SphereCast(transform.position, 0.25f, -transform.up + -transform.forward, out hitBack, 5))
            //{
            //    distBack = hitBack.distance;
            //}

            //if (distForward < distDown && distForward < distBack)
            //{
            //    transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(Vector3.Cross(transform.right, hitForward.normal), hitForward.normal), Time.deltaTime * 5.0f);
            //}
            //else if (distDown < distForward && distDown < distBack)
            //{
            transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(Vector3.Cross(transform.right, hitDown.normal), hitDown.normal), Time.deltaTime * 1.0f);
            //}
            //else if (distBack < distForward && distBack < distDown)
            //{
            //    transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(Vector3.Cross(transform.right, hitBack.normal), hitBack.normal), Time.deltaTime * 5.0f);
            //}

            //GetComponent<Rigidbody>().AddForce(-transform.up * Time.deltaTime * 10);
        }
        if (rb.position.y <-1f)
        {
            FindObjectOfType<GameManagement>().EndGame();
        }
    }

    //void OnCollisionEnter(Collision collision)
    //{
    //    //collision.gameObject.GetComponent<Rigidbody>().transform.up = Vector3.zero;
    //    //player.gameObject.GetComponent<Rigidbody>().useGravity = false;
    //    System.Console.WriteLine("Player has collided with: " + collision.collider.name);
    //    if(collision.gameObject.name == "PipeBasic 1")
    //    {
    //        System.Console.WriteLine("Player Collided with Pipe");
    //        fauxGravity = true;
    //    }
    //    //playerRB.isKinematic = true;
    //    //player.gameObject.GetComponent<Rigidbody>().AddRelativeForce()
    //}
}

Can anyone give some additional hints, tips, and tricks? I would think using a Raycast, and even a SphereCast of it, is probably going to be the closest kind of approach to what I want, but I'm just stuck on how I would exactly implementing it without risking rotating the player cube on the wrong side and screwing up the physics interaction. Am I perhaps overthinking this too much?

Thanks so much in advance everybody.

UPDATE:

I've been changing the logic of the Y velocity of the player, since moving in a circle has to involve specific physics, like the equation given on the page below:

https://www.physicsclassroom.com/class/circles/Lesson-1/Speed-and-Velocity

However, I'm still struggling to find a good time value to divide the top part of the equation against. Here's my movement code when moving right so far:

if (distanceFromPipeCenter.y < 0)
            {
                //Debug.Log("Right A, pull positive: " + pipePull);
                //Debug.Log("X Pull: " + pipePull.x);
                rb.AddForce(sidewaysForce /** pipePull.x*/ * Time.deltaTime, (sidewaysForce * 2 * radiusInPipe * (float)Math.PI) / Time.deltaTime, 0, ForceMode.VelocityChange);
            }
            else if(distanceFromPipeCenter.x>=3)
            {
                //Debug.Log("Right B, pull negative: " + distanceFromPipeCenter);

                rb.AddForce(-sidewaysForce /** pipePull.x*/ * Time.deltaTime, (sidewaysForce * 2 * radiusInPipe * (float)Math.PI) / Time.deltaTime, 0, ForceMode.VelocityChange);
            }
            else if(distanceFromPipeCenter.x>0)
            {

                rb.AddForce(-sidewaysForce /** pipePull.x*/ * Time.deltaTime, (sidewaysForce * 2 * radiusInPipe * (float)Math.PI) / Time.deltaTime, 0, ForceMode.VelocityChange);
            }
            else if(distanceFromPipeCenter.y>0)
            {
                Debug.Log("X distance: " + distanceFromPipeCenter.x);
                Debug.Log("Radius: " + radiusInPipe);
                Debug.Log("Frame count? " + Time.fixedDeltaTime);
                Vector3 pull = new Vector3(-sidewaysForce /** pipePull.x*/ * Time.deltaTime, ((-sidewaysForce * 2 * radiusInPipe * (float)Math.PI) / Time.fixedDeltaTime) * Time.deltaTime, 0);
                Debug.Log("About to apply: " + pull );
                rb.AddForce(-sidewaysForce /** pipePull.x*/ * Time.deltaTime, (-sidewaysForce * 2 * radiusInPipe * (float)Math.PI) / Time.deltaTime, 0, ForceMode.VelocityChange);
            }
            else
            {
                rb.AddForce(sidewaysForce /** pipePull.x*/ * Time.deltaTime, (-sidewaysForce * 2 * radiusInPipe * (float)Math.PI) / Time.deltaTime, 0, ForceMode.VelocityChange);
            }

It seems dividing by Time.deltaTime just jumps the player a huge distance - not what I want. I also tried to used the amount of time since the beginning of the game as the divider, and then multiplying that whole result by Time.deltaTime - but as you probably can deduce, the frames just increase and then eventually slow the velocity down this way.

Any further thoughts or suggestions?


Solution

  • SO, turns out I overthought the whole, or rather, overlooked a key and amazing feature in this game engine.

    I was trying to calculate all of these movements because I thought we had to calculate forces and movements all to the game world's origin. That whole time, I was saying all kinds of rants to myself, thinking, "Man, it'd be so much easier if you could just add a force/torque in the object's local coordinates..."

    Enter AddRelativeForce() instead of AddForce().

    With that, and the use of the radius passed in from the tunnel I'm passing through, with gravity off, I was able to much more easily get the effect desired.

    This code is probably a little bloated at this point, but the key takeaway is the nextLeft and nextRight Vector3 objects I used. I hope this helps someone down the road. Let me know if I can clarify anything or make this post better in any other way.

    void FixedUpdate () {
            //distanceFromPipeCenter.Normalize();
            //add forward force
            rb.AddForce(0, 0, forwardForce * Time.deltaTime);
    
            if (Input.GetKey(KeyCode.RightArrow) && !fauxGravity)
            {
                rb.AddForce(sidewaysForce * Time.deltaTime, 0, 0, ForceMode.VelocityChange);
            }
    
            if (Input.GetKey(KeyCode.LeftArrow) && !fauxGravity)
            {
                rb.AddForce(-sidewaysForce * Time.deltaTime, 0, 0, ForceMode.VelocityChange);
            }
            if(radiusInPipe == 0)
            {
                radiusInPipe = 1;
            }
            //transform.rotation = Quaternion.identity;
            //pipePull.z = 0;
            Vector3 nextRight = new Vector3(sidewaysForce /** pipePull.x*/ * Time.deltaTime, ((2 * radiusInPipe * (float)Math.PI) / (sidewaysForce)) * Time.fixedDeltaTime, 0);
            Vector3 nextLeft = new Vector3(-sidewaysForce /** pipePull.x*/ * Time.deltaTime, ((2 * radiusInPipe * (float)Math.PI) / (sidewaysForce)) * Time.fixedDeltaTime, 0);
            if (Input.GetKey(KeyCode.RightArrow) && fauxGravity)
            {
                //Debug.Log("Right pressed");
                //Debug.Log("Rotation before: " + rb.rotation);
                //rb.rotation = rb.rotation * Quaternion.FromToRotation(rb.transform.up, pipePull);
                //rb.rotation = Quaternion.Lerp(rb.rotation, Quaternion.LookRotation(Vector3.Cross(rb.transform.right, pipePull), pipePull), Time.deltaTime * 5.0f);
                //Debug.Log("Rotation after: " + rb.rotation);
                //Vector3 next = new Vector3(sidewaysForce /** pipePull.x*/ * Time.deltaTime, ((2 * radiusInPipe * (float)Math.PI) / (sidewaysForce)) * Time.fixedDeltaTime, 0);
                if (distanceFromPipeCenter.y < 0)
                {
                    //Debug.Log("Right A, pull positive: " + pipePull);
                    //Debug.Log("X Pull: " + pipePull.x);
                    Debug.Log("Current deltaTime: " + Time.deltaTime);
                    //Vector3 next = new Vector3(sidewaysForce /** pipePull.x*/ * Time.deltaTime, ((2 * radiusInPipe * (float)Math.PI)/(sidewaysForce))  * Time.fixedDeltaTime, 0);
                    Debug.Log("About to apply: " + nextRight);
                    rb.AddRelativeForce(nextRight, ForceMode.VelocityChange);
                }
                else if(distanceFromPipeCenter.x>=3)
                {
                    //Debug.Log("Right B, pull negative: " + distanceFromPipeCenter);
    
                    //rb.AddForce(sidewaysForce /** pipePull.x*/ * Time.deltaTime, ((2 * radiusInPipe * (float)Math.PI) * (sidewaysForce)) * Time.deltaTime, 0, ForceMode.VelocityChange);
                    rb.AddRelativeForce(nextRight, ForceMode.VelocityChange);
                }
                else if(distanceFromPipeCenter.x>0)
                {
                    rb.AddRelativeForce(nextRight, ForceMode.VelocityChange);
                    //rb.AddForce(-sidewaysForce /** pipePull.x*/ * Time.deltaTime, ((2 * radiusInPipe * (float)Math.PI) * (sidewaysForce)) * Time.deltaTime, 0, ForceMode.VelocityChange);
                }
                else if(distanceFromPipeCenter.y>0)
                {
                    Debug.Log("X distance: " + distanceFromPipeCenter.x);
                    Debug.Log("Radius: " + radiusInPipe);
                    Debug.Log("Frame count? " + Time.fixedDeltaTime);
                    Vector3 pull = new Vector3(-sidewaysForce /** pipePull.x*/ * Time.deltaTime, -((2 * radiusInPipe * (float)Math.PI) / (sidewaysForce)) * Time.fixedDeltaTime, 0);
                    Debug.Log("About to apply: " + pull );
    
                    //rb.AddForce(-sidewaysForce /** pipePull.x*/ * Time.deltaTime, -((2 * radiusInPipe * (float)Math.PI) * sidewaysForce) * Time.deltaTime, 0, ForceMode.VelocityChange);
                    rb.AddRelativeForce(nextRight, ForceMode.VelocityChange);
                }
                else
                {
                    rb.AddRelativeForce(nextRight, ForceMode.VelocityChange);
                    //rb.AddForce(sidewaysForce /** pipePull.x*/ * Time.deltaTime, ((2 * radiusInPipe * (float)Math.PI) / (sidewaysForce)) * Time.fixedDeltaTime, 0, ForceMode.VelocityChange);
                }
                //Debug.Log(rb.angularVelocity);
                float headingDeltaAngle = Input.GetAxis("Horizontal") * Time.deltaTime * sidewaysForce;
                Quaternion headingDelta = Quaternion.AngleAxis(headingDeltaAngle, -transform.up);
                headingDelta.y = 0;
                headingDelta.x = 0;
                //align with surface normal
                transform.rotation = Quaternion.FromToRotation(-transform.up, distanceFromPipeCenter) * transform.rotation;
                //apply heading rotation
                transform.rotation = headingDelta * transform.rotation;
            }
            if (Input.GetKey(KeyCode.LeftArrow) && fauxGravity)
            {
                //Debug.Log("Left pressed");
                //Debug.Log("Rotation before: " + rb.rotation);
                //rb.rotation = rb.rotation * Quaternion.FromToRotation(rb.transform.up, pipePull);
                //rb.rotation = Quaternion.Lerp(rb.rotation, Quaternion.LookRotation(Vector3.Cross(rb.transform.right, pipePull), pipePull), Time.deltaTime * 5.0f);
                //Debug.Log("Rotation after: " + rb.rotation);
                //if (distanceFromPipeCenter.y < 0)
                //{
                //    //Debug.Log("Left A, pull positive: " + pipePull);
                //    rb.AddForce(-sidewaysForce/* * pipePull.x*/ * Time.deltaTime, sidewaysForce /pipePull.y * Time.deltaTime, Math.Abs(pipePull.z) * Time.deltaTime, ForceMode.VelocityChange);
                //}
                //else if(distanceFromPipeCenter.x >=0)
                //{
                //    //Debug.Log("Left B, pull negative: " + distanceFromPipeCenter);
                //    rb.AddForce(sidewaysForce /** pipePull.x*/ * Time.deltaTime, sidewaysForce * -pipePull.y * Time.deltaTime, Math.Abs(pipePull.z) * Time.deltaTime, ForceMode.VelocityChange);
                //}
                //else
                //{
                //    //rb.AddForce(sidewaysForce /** pipePull.x*/ * Time.deltaTime, sidewaysForce * -pipePull.y * Time.deltaTime, 0, ForceMode.VelocityChange);
                //    Debug.Log("Deadzone. PAUSE. Pull: " + pipePull);
                //}
                rb.AddRelativeForce(nextLeft, ForceMode.VelocityChange);
                //Debug.Log(rb.angularVelocity);
                float headingDeltaAngle = Input.GetAxis("Horizontal") * Time.deltaTime * sidewaysForce;
                Quaternion headingDelta = Quaternion.AngleAxis(headingDeltaAngle, -transform.up);
                headingDelta.y = 0;
                headingDelta.x = 0;
                //align with surface normal
                transform.rotation = Quaternion.FromToRotation(-transform.up, distanceFromPipeCenter) * transform.rotation;
                //apply heading rotation
                transform.rotation = headingDelta * transform.rotation;
    
            }
            if (fauxGravity)
            {
                rb.useGravity = false;
                if(rb.position.y-ground.position.y > 2)
                {
                    roller.isTrigger = false;
                }
                else
                {
                    roller.isTrigger = true;
                }
                //rb.AddForce(pipePull);
                //roller.isTrigger = false;
                ///*We get the user input and modifiy the direction the ship will face towards*/
                //float yaw = Time.deltaTime * Input.GetAxis("Horizontal");
                ///*We want to save our current transform.up vector so we can smoothly change it later*/
                //Vector3 prev_up = rb.transform.up;
                ///*Now we set all angles to zero except for the Y which corresponds to the Yaw*/
                //transform.rotation = Quaternion.Euler(0, yaw, 0);
    
                //RaycastHit hit;
                //if (Physics.Raycast(transform.position, -prev_up, out hit))
                //{
                //    Debug.DrawLine(transform.position, hit.point);
    
                //    /*Here are the meat and potatoes: first we calculate the new up vector for the ship using lerp so that it is smoothed*/
                //    Vector3 desired_up = Vector3.Lerp(prev_up, hit.normal, Time.deltaTime /** pitch_smooth*/);
                //    /*Then we get the angle that we have to rotate in quaternion format*/
                //    Quaternion tilt = Quaternion.FromToRotation(transform.up, desired_up);
                //    /*Now we apply it to the ship with the quaternion product property*/
                //    transform.rotation = tilt * transform.rotation;
    
                //    /*Smoothly adjust our height*/
                //    //smooth_y = Mathf.Lerp(smooth_y, hover_height - hit.distance, Time.deltaTime * height_smooth);
                //    //transform.localPosition += prev_up * smooth_y;
                //}
                //float distForward = Mathf.Infinity;
                //RaycastHit hitForward;
    
                //if (Physics.SphereCast(transform.position, 0.25f, -transform.up + transform.forward, out hitForward, 5))
                //{
                //    distForward = hitForward.distance;
                //}
                float distDown = Mathf.Infinity;
                RaycastHit hitDown;
                if (Physics.SphereCast(transform.position, 0.25f, -transform.up, out hitDown, 5))
                {
                    distDown = hitDown.distance;
                }
                //float distBack = Mathf.Infinity;
                //RaycastHit hitBack;
                //if (Physics.SphereCast(transform.position, 0.25f, -transform.up + -transform.forward, out hitBack, 5))
                //{
                //    distBack = hitBack.distance;
                //}
    
                //if (distForward < distDown && distForward < distBack)
                //{
                //    transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(Vector3.Cross(transform.right, hitForward.normal), hitForward.normal), Time.deltaTime * 5.0f);
                //}
                //else if (distDown < distForward && distDown < distBack)
                //{
                //transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(Vector3.Cross(transform.right, hitDown.normal), hitDown.normal), Time.deltaTime * 1.0f);
                //}
                //else if (distBack < distForward && distBack < distDown)
                //{
                //    transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(Vector3.Cross(transform.right, hitBack.normal), hitBack.normal), Time.deltaTime * 5.0f);
                //}
    
                //GetComponent<Rigidbody>().AddForce(-transform.up * Time.deltaTime * 10);
                transformDifference = rb.position;
                previousFrame = Time.frameCount;
            }
            if (rb.position.y <-1f)
            {
                FindObjectOfType<GameManagement>().EndGame();
            }
        }