Player falls through the floor using floating origin

In my game, the player is constantly moving along an uneven surface, with widely varying x, y and z coordinates. I added a floating origin script, which works great the first few times the player goes out of range, but on the 6th time or so, the player falls through the floor. Any idea what I might be doing wrong?

class FloatingOrigin {
    public float threshold = 20f; // Low number for debugging.

    List<Transform> getObjectsToMove() {
        // This method gets my camera, the player, and the terrain chunks.
    }

    void LateUpdate() {
        Vector3 cameraPosition = gameObject.transform.position;
        if (cameraPosition.sqrMagnitude > threshold * threshold) {
            List<Transform> toMove = getObjectsToMove();

            foreach (Transform t in toMove) {
                t.position -= cameraPosition;
            }
        }
    }
}

What an interesting problem. I have a theory!

My theory is that due to floating point error, sometimes when the player is moved and the ground collider is moved by your amount, the final result is slightly off. This could mean if the player had physically slammed into the ground on this frame, then the next frame they were actually in the collider, it might break the physics boundary.

Try this: lift your player by maybe 0.01f on the Y axis during the flip.

1 Like

Thanks! That didn’t work, but it gave me some ideas. I’ll let you know if any of them work.

1 Like

I still don’t know why it happens. Maybe there’s a tiny seam between my terrain chunks or something. But I have a workaround which almost works. There’s already code to make sure the player sticks to the ground, using a SphereCast and projecting the player’s velocity onto the hit’s normal. My new code does a ray cast downward. If it doesn’t hit anything, it does another ray cast from a much higher point, and puts the player back on the terrain.

My code correctly detects the situation, and seems to properly restore the player’s position, but the player’s velocity is messed up. It launches straight up in the air for several seconds, before falling back down. When it lands, it goes right through the ground again, and doesn’t trigger my test a second time (that part I can debug). Here’s the code:

      // TouchingGround() uses a SphereCast to check whether the player is grounded.
        if (TouchingGround(0.5, out RaycastHit hitInfo)) {
            if (Mathf.Abs(Vector3.Angle(hitInfo.normal, Vector3.up)) < 85f) {
                m_rigidBody.velocity = Vector3.ProjectOnPlane(m_rigidBody.velocity, hitInfo.normal);
            }
        }
        else {
            Vector3 pos = transform.position;
            if (!Physics.Raycast(pos, Vector3.down, 1000f)) {
                if (Physics.Raycast(new Vector3(pos.x, pos.y + 100f, pos.z), Vector3.down, out RaycastHit hit, 100f)) {
                    transform.localPosition = hit.point;
                }
            }

Any idea why the ball is launched?

I think there’s an issue with moving things back to the origin when they are physics objects.

You will have to disable as much of the physics as possible it during the move, perhaps, and then restore its previous state by setting the velocity manually, or something.

Good thinking… maybe if you do a .GetComponentsInChildren<Collider>(); and iterate all those colliders to set them .enabled = false;, then move everything, then turn them all back on again.

I’ve seen this before too. I wonder if any of those suggestions worked for anybody?