NWH Common
Search Results for

    Shifting Origin

    NWH.Common.ShiftingOrigin.ShiftingOrigin reduces floating-point precision problems by moving loaded scene content back toward the origin when the main camera travels too far away.

    Setup

    1. Add one ShiftingOrigin component to an active GameObject.
    2. Tag the camera that represents the player's position as MainCamera. The component follows Camera.main; there is no target-camera field in the inspector.
    3. Set distanceThreshold in meters. The default is 500.
    4. Register optional callbacks in onBeforeJump and onAfterJump.

    Only one active instance is supported. ShiftingOrigin.Instance is assigned during Awake and an assert is raised if another instance is already present.

    Trigger Condition

    During LateUpdate, the component shifts when:

    Camera.main.transform.position.magnitude > distanceThreshold
    

    The comparison uses the camera's full three-dimensional distance from (0, 0, 0), not its distance from the ShiftingOrigin GameObject. Camera.main is checked again every 60 frames so a newly selected main camera can become the target.

    What Moves

    Every root Transform in the game is translated by the negative of the main camera's position. Children follow their root normally, so relative positions are preserved. The roots are collected from an object query rather than from SceneManager, so inactive objects and DontDestroyOnLoad objects move with everything else. Leaving those behind would let the world run away from them.

    The component also:

    • adjusts particles in particle systems that use world simulation space;
    • sets cached Rigidbody sleep thresholds to zero and interpolation to None before the move, then restores both afterwards so no body sleeps through the shift and no interpolated pose streaks across it;
    • calls Physics.SyncTransforms() after the move; and
    • adds the shift vector to the read-only TotalOffset property.

    Particle systems in local or custom simulation space are not rewritten because their particles already follow their transform as appropriate.

    Runtime-Created Objects

    Rigidbodies and particle systems are re-scanned at the start of every shift, so objects spawned since the last one are included automatically. RefreshCaches() is public and can be called directly, but no normal setup needs it.

    The scan uses FindObjectsByType over the whole scene and is not cheap; it runs at most once per distanceThreshold of travel.

    There is no public method that forces a shift. A shift is initiated only by the threshold check.

    Absolute Coordinates

    TotalOffset is the cumulative translation removed from the scenes. Add it to a shifted local world position when an external system needs the corresponding unshifted coordinate:

    using NWH.Common.ShiftingOrigin;
    using UnityEngine;
    
    public static class AbsolutePosition
    {
        public static Vector3 FromShifted(Vector3 shiftedPosition)
        {
            ShiftingOrigin origin = ShiftingOrigin.Instance;
            return origin == null
                ? shiftedPosition
                : shiftedPosition + origin.TotalOffset;
        }
    }
    

    Events

    • onBeforeJump is invoked immediately before root transforms and particles move. Its built-in listener zeroes Rigidbody sleep thresholds and disables interpolation.
    • onAfterJump is invoked after the move. Its built-in listener restores the sleep thresholds, calls Physics.SyncTransforms(), then restores interpolation.
    • OnShift is a static Action<Vector3> raised last, carrying the offset that was applied to every object. Subscribe from components that cache world positions across frames; being static, it can be reached without a reference to the component.

    Use these to resynchronize systems that store absolute coordinates outside the moved hierarchy. Static event subscriptions are cleared at the start of each play session.

    Troubleshooting

    The origin never shifts

    • Verify that an enabled camera is tagged MainCamera and therefore returned by Camera.main.
    • Check the camera's world-position magnitude. The trigger uses >; a camera exactly at distanceThreshold does not shift.
    • Lower distanceThreshold to make the shift happen sooner.

    An object did not move

    Roots are collected from an object query, so anything with a Transform is included. Check whether a separate world-coordinate system restores the object's position after onAfterJump, or whether the object caches an absolute position that needs correcting from OnShift.

    In this article
    Back to top Copyright © NWH - Vehicle Physics, Aerodynamics, Dynamic Water Physics