# Move Along Wall of Slanted Pipe (F-Zero like Physics)

Discussion in 'Physics' started by KOemxe, Dec 8, 2018.

1. ### KOemxe

Joined:
May 1, 2018
Posts:
22
Hey there! I'm figuring out how to get this rigidbody cube of mine to move along the wall of a horizontal pipe OK, although with some small glitches. Here's my new challenge.

I've been trying to get my cube to stick to the walls of this pipe when it enters, but without too much luck. I'll tell you right now, the pipe was made in Blender, and has three components:

1.) An inverted pipe, to allow the cube to make contact with the pipe in order to move from inside
2.) A pipe equivalent that is NOT inverted, therefore allowing the cube to bump it from outside, etc
3.) A convex trigger pipe, that keeps track of when the cube makes contact with the pipe structure in order to start simulating the physics. Skin width is currently 0.01.

My player cube rigidbody has the following components:

1.) Cube with rigidbody itself
2.) A sphere hanging a little from below. This acts as a roller, the idea is that it ROLLS on the wall of any pipe it makes contact with to help the cube move along the wall of the pipe like it rolls with wheels, WITHOUT turning the cube.

In order to make this work, I freeze the x and y rotation so that there's no chance the cube can make extra erratic motion. However, it doesn't seem to help THAT much. It works perfectly when I'm in a horizontal pipe (especially when it's large enough) but when it comes to this slanted pipe, I have a lot more difficulty. I'm sure it has something to do with my player movement itself, but I'm not sure how to fix it. The player can ONLY move left and right with the arrow keys while it constantly moves forward.

Here's the "FauxGravity" class, which is attached as a script to the slanted pipe (it was attached to the working horizontal pipe in another scene) which helps to calculate the radius of the pipe, etc:

Code (CSharp):
1. using System.Collections;
2. using System.Collections.Generic;
3. using UnityEngine;
4.
5. public class FauxGravity : MonoBehaviour {
6.
7.     public Transform attractor;
8.     public Transform player;
9.     public Transform collider;
10.     Rigidbody attractRB;
11.     Rigidbody playerRB;
12.     Vector3 myNormal;
13.     public Vector3 vectorFromPipeCenter;
14.     public Vector3 forwardPipeVector;
15.     public Vector3 rollerVectorFromPipeCenter;
16.     Vector3 project2Center;
17.     public Vector3 pipeGravityPull;
18.     public int gravity = 1;
19.     public float radiusFromCrossectionCenter = 0f;
20.
21.     // Use this for initialization
22.     void Start () {
23.
24.         //Physics.gravity.magnitude = 0;
25.     }
26.
27.     // Update is called once per frame
28.     void Update () {
29.
30.     }
31.
32.     //private void OnCollisionEnter(Collision collision)
33.     //{
34.     //    //collision.gameObject.GetComponent<Rigidbody>().transform.up = Vector3.zero;
35.     //    //player.gameObject.GetComponent<Rigidbody>().useGravity = false;
36.     //    myNormal = playerRB.transform.up;
37.     //    //playerRB = collision.gameObject.GetComponent<Rigidbody>();
38.
39.     //    gravity = 0;
40.     //    //playerRB.isKinematic = true;
42.     //}
43.
44.     public void FixedUpdate()
45.     {
46.         if (gravity == 0)
47.         {
48.             //playerRB.isKinematic = true;
49.             //Debug.Log("Gravity is 0.");
50.             attractRB = attractor.GetComponent<Rigidbody>();
51.             playerRB = player.GetComponent<Rigidbody>();
52.             //player.GetComponentInChildren<Collider>().isTrigger = false;
53.             //playerRB.AddForce(-10 * playerRB.mass * myNormal);
54.             //Debug.Log("PlayerRB position: " + playerRB.position);
55.             //Debug.Log("AttractRB position: " + attractRB.position);
56.             vectorFromPipeCenter = playerRB.position - attractRB.position;
57.             vectorFromPipeCenter.z = 0;
58.             rollerVectorFromPipeCenter = playerRB.GetComponentInChildren<Collider>().ClosestPointOnBounds(playerRB.position) - attractRB.position;
59.             //vectorFromPipeCenter.Normalize();
60.             //Debug.Log("Player distance from pipe center: " + vectorFromPipeCenter.magnitude);
61.             Debug.Log("Player vector from pipe center" + vectorFromPipeCenter);
62.             //vectorFromPipeCenter = attractRB.position - playerRB.position;
63.             //Debug.Log("playerRB forward is " + playerRB.rotation.z);
64.             //Debug.Log("playerRB magnitude is " + player.forward.magnitude);
65.             forwardPipeVector = player.forward.magnitude * Vector3.forward;
66.             //Debug.Log("Player forward vector? " + forwardPipeVector);
67.             // or
68.             //Vector forwardPipeVector = pipeTransform.forward;
69.             // And finally
70.             project2Center = Vector3.Project(vectorFromPipeCenter, forwardPipeVector);
71.             //Debug.Log("What is project2Center? " + project2Center);
73.             double playerY = System.Convert.ToDouble(playerRB.position.y);
74.             double playerX = System.Convert.ToDouble(playerRB.position.x);
75.             //float inverseTan = System.Convert.ToSingle(System.Math.Atan(playerY / playerX));
76.             //Debug.Log("Normal is: " + Quaternion.AngleAxis(inverseTan, forwardPipeVector));
77.             // pipe pull force = distance from pipe center to power 2
78.             //pipeGravityPull = Quaternion.AngleAxis(inverseTan, playerRB.transform.forward) * project2Center * Mathf.Pow ( (radiusFromCrossectionCenter * 1 ), 2 );
80.             //Debug.Log("Pipe gravity vector? " + pipeGravityPull);
81.             //playerRB.useGravity = true;
82.             Debug.DrawLine(pipeGravityPull, pipeGravityPull);
85.         }
86.         if (gravity == 1)
87.         {
88.             player.GetComponent<Rigidbody>().useGravity = true;
89.             //playerRB.isKinematic = false;
90.         }
91.     }
92.
93.     private void OnCollisionExit(Collision collision)
94.     {
95.         //Debug.Log("Gravity is 1 again.");
96.         //player.gameObject.GetComponent<Rigidbody>().useGravity = true;
97.         //gravity = 1;
98.         //playerRB.useGravity = true;
99.         //playerRB.isKinematic = false;
101.     }
102.
103.     void gravityAttract(Collider colliderObject)
104.     {
105.         var rb = colliderObject.GetComponent<Rigidbody>();
108.     }
109. }
Here's the PlayerMovement class, which works in tandem with the other FauxGravity class, and moves that way:

Code (CSharp):
1. using System;
2. using UnityEngine;
3.
4. public class PlayerMovement : MonoBehaviour {
5.
6.     public Rigidbody rb;
7.     public Collider roller;
8.     public float forwardForce = 2000f;
9.     public float sidewaysForce = 500f;
10.     public Boolean fauxGravity = false;
11.     public Vector3 distanceFromPipeCenter = new Vector3(0, 0, 0);
12.     public Vector3 pipePull = new Vector3(0,0,0);
13.     public float radiusInPipe = 0f;
14.     Vector3 transformDifference = new Vector3(0,0,0);
15.     float previousFrame =2f;
16.     public Transform ground;
17.     public Transform attractor;
18.
19.     // Use this for initialization
20.     void Start () {
21.
22.     }
23.
24.     // Update is called once per frame use fixed update for Unity Fizzix
25.     void FixedUpdate () {
26.         //distanceFromPipeCenter.Normalize();
28.         rb.AddForce(0, 0, forwardForce * Time.deltaTime);
29.
30.         if (Input.GetKey(KeyCode.RightArrow) && !fauxGravity)
31.         {
32.             rb.AddForce(sidewaysForce * Time.deltaTime, 0, 0, ForceMode.VelocityChange);
33.         }
34.
35.         if (Input.GetKey(KeyCode.LeftArrow) && !fauxGravity)
36.         {
37.             rb.AddForce(-sidewaysForce * Time.deltaTime, 0, 0, ForceMode.VelocityChange);
38.         }
40.         {
42.         }
43.         //transform.rotation = Quaternion.identity;
44.         //pipePull.z = 0;
45.         Vector3 nextRight = new Vector3(sidewaysForce /** pipePull.x*/ * Time.deltaTime, ((2 * radiusInPipe * (float)Math.PI) / (sidewaysForce)) * Time.fixedDeltaTime, 0);
46.         Vector3 nextLeft = new Vector3(-sidewaysForce /** pipePull.x*/ * Time.deltaTime, ((2 * radiusInPipe * (float)Math.PI) / (sidewaysForce)) * Time.fixedDeltaTime, 0);
47.         if (Input.GetKey(KeyCode.RightArrow) && fauxGravity)
48.         {
49.             //Debug.Log("Right pressed");
50.             //Debug.Log("Rotation before: " + rb.rotation);
51.             //rb.rotation = rb.rotation * Quaternion.FromToRotation(rb.transform.up, pipePull);
52.             //rb.rotation = Quaternion.Lerp(rb.rotation, Quaternion.LookRotation(Vector3.Cross(rb.transform.right, pipePull), pipePull), Time.deltaTime * 5.0f);
53.             //Debug.Log("Rotation after: " + rb.rotation);
54.             //Vector3 next = new Vector3(sidewaysForce /** pipePull.x*/ * Time.deltaTime, ((2 * radiusInPipe * (float)Math.PI) / (sidewaysForce)) * Time.fixedDeltaTime, 0);
55.             if (distanceFromPipeCenter.y < 0)
56.             {
57.                 //Debug.Log("Right A, pull positive: " + pipePull);
58.                 //Debug.Log("X Pull: " + pipePull.x);
59.                 Debug.Log("Current deltaTime: " + Time.deltaTime);
60.                 //Vector3 next = new Vector3(sidewaysForce /** pipePull.x*/ * Time.deltaTime, ((2 * radiusInPipe * (float)Math.PI)/(sidewaysForce))  * Time.fixedDeltaTime, 0);
61.                 Debug.Log("About to apply: " + nextRight);
63.             }
64.             else if(distanceFromPipeCenter.x>=3)
65.             {
66.                 //Debug.Log("Right B, pull negative: " + distanceFromPipeCenter);
67.
68.                 //rb.AddForce(sidewaysForce /** pipePull.x*/ * Time.deltaTime, ((2 * radiusInPipe * (float)Math.PI) * (sidewaysForce)) * Time.deltaTime, 0, ForceMode.VelocityChange);
70.             }
71.             else if(distanceFromPipeCenter.x>0)
72.             {
74.                 //rb.AddForce(-sidewaysForce /** pipePull.x*/ * Time.deltaTime, ((2 * radiusInPipe * (float)Math.PI) * (sidewaysForce)) * Time.deltaTime, 0, ForceMode.VelocityChange);
75.             }
76.             else if(distanceFromPipeCenter.y>0)
77.             {
78.                 Debug.Log("X distance: " + distanceFromPipeCenter.x);
80.                 Debug.Log("Frame count? " + Time.fixedDeltaTime);
81.                 Vector3 pull = new Vector3(-sidewaysForce /** pipePull.x*/ * Time.deltaTime, -((2 * radiusInPipe * (float)Math.PI) / (sidewaysForce)) * Time.fixedDeltaTime, 0);
82.                 Debug.Log("About to apply: " + pull );
83.
84.                 //rb.AddForce(-sidewaysForce /** pipePull.x*/ * Time.deltaTime, -((2 * radiusInPipe * (float)Math.PI) * sidewaysForce) * Time.deltaTime, 0, ForceMode.VelocityChange);
86.             }
87.             else
88.             {
90.                 //rb.AddForce(sidewaysForce /** pipePull.x*/ * Time.deltaTime, ((2 * radiusInPipe * (float)Math.PI) / (sidewaysForce)) * Time.fixedDeltaTime, 0, ForceMode.VelocityChange);
91.             }
92.             //Debug.Log(rb.angularVelocity);
93.             float headingDeltaAngle = Input.GetAxis("Horizontal") * Time.deltaTime * sidewaysForce;
99.             Debug.Log("Transformation rotation now " + Quaternion.FromToRotation(-transform.up, distanceFromPipeCenter) * transform.rotation);
100.             //align with surface normal
101.             transform.rotation = Quaternion.FromToRotation(-transform.up, distanceFromPipeCenter) * transform.rotation;
104.             //transform.rotation = headingDelta * transform.rotation;
105.         }
106.         if (Input.GetKey(KeyCode.LeftArrow) && fauxGravity)
107.         {
108.             //Debug.Log("Left pressed");
109.             //Debug.Log("Rotation before: " + rb.rotation);
110.             //rb.rotation = rb.rotation * Quaternion.FromToRotation(rb.transform.up, pipePull);
111.             //rb.rotation = Quaternion.Lerp(rb.rotation, Quaternion.LookRotation(Vector3.Cross(rb.transform.right, pipePull), pipePull), Time.deltaTime * 5.0f);
112.             //Debug.Log("Rotation after: " + rb.rotation);
113.             //if (distanceFromPipeCenter.y < 0)
114.             //{
115.             //    //Debug.Log("Left A, pull positive: " + pipePull);
116.             //    rb.AddForce(-sidewaysForce/* * pipePull.x*/ * Time.deltaTime, sidewaysForce /pipePull.y * Time.deltaTime, Math.Abs(pipePull.z) * Time.deltaTime, ForceMode.VelocityChange);
117.             //}
118.             //else if(distanceFromPipeCenter.x >=0)
119.             //{
120.             //    //Debug.Log("Left B, pull negative: " + distanceFromPipeCenter);
121.             //    rb.AddForce(sidewaysForce /** pipePull.x*/ * Time.deltaTime, sidewaysForce * -pipePull.y * Time.deltaTime, Math.Abs(pipePull.z) * Time.deltaTime, ForceMode.VelocityChange);
122.             //}
123.             //else
124.             //{
125.             //    //rb.AddForce(sidewaysForce /** pipePull.x*/ * Time.deltaTime, sidewaysForce * -pipePull.y * Time.deltaTime, 0, ForceMode.VelocityChange);
126.             //    Debug.Log("Deadzone. PAUSE. Pull: " + pipePull);
127.             //}
129.             //Debug.Log(rb.angularVelocity);
130.             float headingDeltaAngle = Input.GetAxis("Horizontal") * Time.deltaTime * sidewaysForce;
134.             //align with surface normal
135.             transform.rotation = Quaternion.FromToRotation(-transform.up, distanceFromPipeCenter) * transform.rotation;
137.             transform.rotation = headingDelta * transform.rotation;
138.
139.         }
140.         if (fauxGravity)
141.         {
142.             rb.useGravity = false;
143.             if(rb.position.y-ground.position.y > 2)
144.             {
145.                 roller.isTrigger = false;
146.             }
147.             else
148.             {
149.                 roller.isTrigger = true;
150.             }
152.             //roller.isTrigger = false;
153.             ///*We get the user input and modifiy the direction the ship will face towards*/
154.             //float yaw = Time.deltaTime * Input.GetAxis("Horizontal");
155.             ///*We want to save our current transform.up vector so we can smoothly change it later*/
156.             //Vector3 prev_up = rb.transform.up;
157.             ///*Now we set all angles to zero except for the Y which corresponds to the Yaw*/
158.             //transform.rotation = Quaternion.Euler(0, yaw, 0);
159.
160.             //RaycastHit hit;
161.             //if (Physics.Raycast(transform.position, -prev_up, out hit))
162.             //{
163.             //    Debug.DrawLine(transform.position, hit.point);
164.
165.             //    /*Here are the meat and potatoes: first we calculate the new up vector for the ship using lerp so that it is smoothed*/
166.             //    Vector3 desired_up = Vector3.Lerp(prev_up, hit.normal, Time.deltaTime /** pitch_smooth*/);
167.             //    /*Then we get the angle that we have to rotate in quaternion format*/
168.             //    Quaternion tilt = Quaternion.FromToRotation(transform.up, desired_up);
169.             //    /*Now we apply it to the ship with the quaternion product property*/
170.             //    transform.rotation = tilt * transform.rotation;
171.
172.             //    /*Smoothly adjust our height*/
173.             //    //smooth_y = Mathf.Lerp(smooth_y, hover_height - hit.distance, Time.deltaTime * height_smooth);
174.             //    //transform.localPosition += prev_up * smooth_y;
175.             //}
176.             //float distForward = Mathf.Infinity;
177.             //RaycastHit hitForward;
178.
179.             //if (Physics.SphereCast(transform.position, 0.25f, -transform.up + transform.forward, out hitForward, 5))
180.             //{
181.             //    distForward = hitForward.distance;
182.             //}
183.             float distDown = Mathf.Infinity;
184.             RaycastHit hitDown;
185.             if (Physics.SphereCast(transform.position, 0.25f, -transform.up, out hitDown, 5))
186.             {
187.                 distDown = hitDown.distance;
188.             }
189.             //float distBack = Mathf.Infinity;
190.             //RaycastHit hitBack;
191.             //if (Physics.SphereCast(transform.position, 0.25f, -transform.up + -transform.forward, out hitBack, 5))
192.             //{
193.             //    distBack = hitBack.distance;
194.             //}
195.
196.             //if (distForward < distDown && distForward < distBack)
197.             //{
198.             //    transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(Vector3.Cross(transform.right, hitForward.normal), hitForward.normal), Time.deltaTime * 5.0f);
199.             //}
200.             //else if (distDown < distForward && distDown < distBack)
201.             //{
202.             //transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(Vector3.Cross(transform.right, hitDown.normal), hitDown.normal), Time.deltaTime * 1.0f);
203.             //}
204.             //else if (distBack < distForward && distBack < distDown)
205.             //{
206.             //    transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(Vector3.Cross(transform.right, hitBack.normal), hitBack.normal), Time.deltaTime * 5.0f);
207.             //}
208.
209.             //GetComponent<Rigidbody>().AddForce(-transform.up * Time.deltaTime * 10);
210.             transformDifference = rb.position;
211.             previousFrame = Time.frameCount;
212.         }
213.         if (rb.position.y <-1f)
214.         {
215.             FindObjectOfType<GameManagement>().EndGame();
216.         }
217.     }
218.
219.     //void OnCollisionEnter(Collision collision)
220.     //{
221.     //    //collision.gameObject.GetComponent<Rigidbody>().transform.up = Vector3.zero;
222.     //    //player.gameObject.GetComponent<Rigidbody>().useGravity = false;
223.     //    System.Console.WriteLine("Player has collided with: " + collision.collider.name);
224.     //    if(collision.gameObject.name == "PipeBasic 1")
225.     //    {
226.     //        System.Console.WriteLine("Player Collided with Pipe");
227.     //        fauxGravity = true;
228.     //    }
229.     //    //playerRB.isKinematic = true;
231.     //}
232. }
Finally, here's the PlayerCollision class, which is used to detect when the player hits the player (restarts the scene/level) and detect when the player hits a pipe so that it starts using the physics I'm TRYING to implement:

Code (CSharp):
1. using UnityEngine;
2.
3. public class PlayerCollison : MonoBehaviour {
4.
5.     public PlayerMovement movement;
6.     public FauxGravity fauxG;
7.
8.     void OnCollisionEnter(Collision collisionInfo)
9.     {
10.         if (collisionInfo.collider.tag == "Obstacle")
11.         {
12.             Debug.Log("We hit a freaking obstacle.");
13.             Debug.Log("Player has collided with: " + collisionInfo.collider.name);
14.             movement.enabled = false;
15.             FindObjectOfType<GameManagement>().EndGame();
16.         }
17.
18.
19.     }
20.
21.     void OnTriggerEnter(Collider collisionInfo)
22.     {
23.         if (collisionInfo.tag == "Pipe")
24.         {
25.             movement.fauxGravity = true;
26.             //Debug.Log("False gravity on");
27.             fauxG.gravity = 0;
28.             movement.pipePull = fauxG.pipeGravityPull;
29.             movement.attractor = fauxG.attractor;
31.             //Debug.Log("Pipe pull: " + movement.pipePull);
32.             //Debug.Log("Pipe name: " + fauxG.attractor.name);
33.             //movement.roller.isTrigger = false;
34.             movement.distanceFromPipeCenter = fauxG.vectorFromPipeCenter;
35.             //Debug.Log("Distance from center: " + movement.distanceFromPipeCenter);
36.         }
37.     }
38.
39.     void OnTriggerStay(Collider other)
40.     {
41.         if (other.tag == "Pipe")
42.         {
43.             //movement.pipePull = fauxG.pipeGravityPull;
44.             //Debug.Log("Pipe pull: " + movement.pipePull);
45.             //movement.rb.GetComponentInChildren.rigidbody.
46.             //movement.roller.isTrigger = false;
48.             if (other.bounds.Contains(transform.position))
49.             {
50.                 Debug.Log("Still in bounds!");
52.                 if (fauxG.vectorFromPipeCenter.y > 0)
53.                 {
54.                     movement.roller.isTrigger = false;
55.                 }
56.                 else
57.                 {
58.                     movement.roller.isTrigger = true;
59.                 }
60.                 movement.distanceFromPipeCenter = fauxG.vectorFromPipeCenter;
61.             }
62.             else
63.             {
64.                 Debug.Log("No longer in bounds...");
65.                 movement.fauxGravity = false;
66.                 movement.GetComponent<Rigidbody>().useGravity = true;
67.             }
68.             //Debug.Log("Distance from center: " + movement.distanceFromPipeCenter);
69.         }
70.     }
71.
72.     void OnTriggerExit(Collider collision)
73.     {
74.         Debug.Log("I was called!");
75.         if (collision.tag == "Pipe")
76.         {
77.             if (!collision.bounds.Contains(transform.position))
78.             {
79.                 Debug.Log("False gravity off");
80.                 movement.fauxGravity = false;
81.                 movement.roller.isTrigger = true;
82.                 movement.GetComponent<Rigidbody>().useGravity = true;
83.                 fauxG.gravity = 1;
84.             }
85.         }
86.     }
87. }
Sorry if it seems a little messy. I've been trying to play with a few existing examples from other projects online, and tweak with them to further understand more of what they're getting at. I feel I've made some progress, but two problems in particular:

1.) There's a few positions in the game, where the player makes contact with the pipe, and STILL thinks it's in the pipe after it actually leaves it and so thinks it's moving like it's in the pipe

2.) The "roller" component of the player will not really stick to the top wall.