The first impression I get from your script is that if there’s any discrepancy between the “gravity direction” and your “character orientation” raycasts, you may easily find yourself in an abnormal state.
What is the length of your GroundCheckDistance variable? Are all GroundLayer objects properly assigned to that layer? Either of those may cause problems with proper Raycast hit detection.
Under what conditions are you using TubeGravity? Is it ever switching to/away from it at a wrong time? (It appears to be fine, based on what I’ve gleaned from your animated gif example, but asking simply to be thorough)
A sizable risk when trying to rely entirely on surface normals for both gravity and orientation like this is that, first off, you probably don’t need to fire two Raycasts in this scenario. If the direction of “gravity” and your orientation are both based on a Raycast fired in the same direction, you can just double check the distance to where you hit as necessary for the difference between GroundNormalDistance and GroundCheckDistance.
Furthermore, collision surface normals aren’t smoothed like mesh normals are. As soon as your character has rotated to a point where the Raycast hits the next triangle, you’re now going to snap to the next incremental rotation in the loop. This is a potential risk for a “jittery” appearance even when it’s all working correctly.
One final question to be asked to start: What type(s) of collider is(are) being used for the loop? Is it a single, concave collider? Is it numerous convex colliders? Depending on its construction, it may also be worth verifying that the Raycast(s) is hitting the right surface as well.
Edit: As a way of testing this (as needed), you could throw in something like this temporarily:
Debug.DrawRay(hit.point, hit.normal, Color.white, 5.0f);
As a side note, I would recommend making a slight tweak to your Rigidbody.AddForce()
call. By giving it [ForceMode.Acceleration]
, you won't need to worry if you begin to rebalance the character's mass.
phys.AddForce(GravityDirection, GravityForce, ForceMode.Acceleration);
Edit 2: As another potential approach to smooth things out, here's an option to consider:
For the current direction of “gravity” at any given time, perform a raycast down from the character to see what ground is hit. In other words, leave it as-is.
However, for the character’s orientation (as the deciding factor and general baseline to determine which direction is currently “down” for the raycast), use OnCollisionStay for your point of reference. In the case of the loop in your example, at least, this will provide multiple potential simultaneous points of contact at a time.
On that basis, you would be able to smooth between them to determine the most likely surface normal for your current position (after making sure to filter out anything beyond a maximum angle difference).
In that regard, if the “gravity” is only intended to help hold the character in place during the loop (as a simulation of centripedal force), then using the collisions in this manner could even serve as a potential replacement for much of the raycast usage (to some extent, anyway).
While this still isn’t a comprehensive solution either (and is really just me offering an alternative approach to the system you’re already working at), I hope it may at least serve as food for thought.