Search Unity

  1. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

Floating Point Errors and Large Scale Worlds

Discussion in 'World Building' started by Phelan-Simpson, Apr 15, 2018.

  1. cosmochristo


    Sep 24, 2018
    I have measured the moving collider performance issue raised by @Marcos-Elias, @ristophonics, and @razzraziel. With 10k collider objects (boxes with a collider component), if I continuously transform the group by transforming the scene root, there is a significant physics load seen in the profiler. As I did in my latest paper, I ran it on my core i5 laptop and, with very little geometry visible, and used the mouse keys to continually move the scene root.

    In the first image attached, on the left of the screen can be seen the simple visible geometry and profiler window on right.


    You need to tell Unity to not perform unnecessary processing when the scene root is moved and this can be by turning off the active hierarchy above the colliders before each transform and back on after it:
    Code (CSharp):
    1. nucleonsGroup.SetActive(false);
    2. // Move the scene to the new position by changing scene parent object transform.
    3. transform.Translate(rotated_transform);
    5.  nucleonsGroup.SetActive(true);
    The next image shows what happens when I do this.

    The profiler measurements show that the magnitude of processing devoted to collider movement is now much less compared to the proportion of cpu devoted to rendering a few simple objects, even while continuously transforming 10,000 colliders. Therefore, moving colliders does not need to cause a significant performance load.

    Attached Files:

    Last edited: Sep 8, 2019
  2. Peter77


    QA Jesus

    Jun 12, 2013
    Did you profile it in a build or editor?

    I'm asking because:
  3. cosmochristo


    Sep 24, 2018
    @Peter77 Good question, I am running from the editor play button because, even though I have developer build ticked and autoconnect profiler ticked, no profiler is displayed when I run it as an app.

    I run it full screen, so the rendering of editor windows superpig mentioned is not an issue.
    I also turn vsync off so there are no cpu spikes from that.
    There are no memory allocations during run either, after the initial creation of 10k colliders.
    There may be other interference from the editor but, on average, the relative load comparisons are still valid anyway.

    The doco on profiling with dev build talks about running profiler on another machine, which would be ideal but I do not have a network setup.

    If you can tell me how to get the profiler up on the same machine when running from a built app then I will try it.
  4. Peter77


    QA Jesus

    Jun 12, 2013
    That should be no different:
    • Start development build
    • Open Profiler window
    • Select Player in Profiler drop-down
    • Press record in Profiler window
  5. cosmochristo


    Sep 24, 2018
    It is on my system: after starting it there is no menu option to open profiler. Is there a KB shortcut?
    No menu options for anything. OSX Mojave 10.14.6. Unity 2018.4.7.f1
  6. Peter77


    QA Jesus

    Jun 12, 2013
    You're saying when you start the build, the "Windows > Analysis > Profiler" menu item disappears from the Unity Editor main menu? :eek:
  7. cosmochristo


    Sep 24, 2018
    I mean it is not available in the build that I run. If I need to run it from the editor (like I have been doing) then ... do you mean I should close the editor after starting profiler and it will connect with the build when I run that?
  8. cosmochristo


    Sep 24, 2018
    @Peter77: I worked it out. Here are the profile measurements from two dev builds: the first with an active hierarchy during moves and the second with it inactive.
    Profile with active 10k colliders under root node while moving (the hierarchy).

    Profile for inactive hierarchy:


    So the difference is 60fps+ (active hierarchy) to 200fps+ (inactive).

    For these tests, I used 1280x800 with rendering quality set to very low. Same version of Unity 2018.7. Only apps running were the unity editor with the profiler, the game from dev build.

    Attached Files:

    buFFalo94 and Peter77 like this.
  9. cosmochristo


    Sep 24, 2018
    I put an example video online here:

    The relevant scripts shown attached in video are:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    5. /*
    6. * Performs simple Floating Origin player movement by reverse transforming the scene
    7. * Floating origin movement only happens if there is
    8. * a change in navigation input and it is above a minimum threshold.
    9. *
    10. * Here's and online example testing 10k colliders:
    11. * which shows how the PlayerMove and PlayerView are attached.
    12. *
    13. * Assumptions:
    14. * Player object has camera attached, it is positioned at the origin, and
    15. * tagged "playerCapsule".
    16. * This script is attached to the scene parent object.
    17. */
    18. public class PlayerMove : MonoBehaviour
    19. {
    21.     // Minimum player collision detection distance
    22.     private const float COLLISION_DISTANCE = 2f;
    23.     // Multiplier for when bouncing back from collisions
    24.     private const float COLLISION_ADJUST = 1.2f;
    25.     // Threshold for detecting navigation changes
    26.     private const float NAV_SHAKE_THRESHOLD = 0.0001f;
    27.     // Max distance for detecting player collisions
    28.     private const float RAYCAST_DISTANCE = 10f;
    30.     // Multiplier to each movement input
    31.     private readonly float speed = 7.0f;
    32.     private GameObject player;
    33.     private GameObject SceneRoot;
    34.     // Layer mask for ray casting
    35.     private int layerMask;
    36.     // Horizontal movement deltas
    37.     private float deltaX;
    38.     private float deltaZ;
    39.     private float speedAdj;
    40.     // Current reverse transform
    41.     private Vector3 reverseMovement;  
    42.     // Rotated reverse transform
    43.     private Vector3 rotated_transform = new Vector3(0f,0f,0f);
    44.     private readonly Vector3 player_position = new Vector3(0f, 0f, 0f);
    45.     private RaycastHit rayCollision;
    47.     void Start()
    48.     {
    49.         // Accloc mem once only
    50.         reverseMovement = new Vector3(0, 0, 0);
    52.         // Use Physics.Raycast to cast a ray forward into scene to check for collisions
    53.         // create a bit mask for 7 layers with 0 for player layer to use in Raycast
    54.         layerMask = 1 << 8;
    55.         layerMask = ~layerMask;
    57.         // turn off cursor display in game window
    58.         Cursor.visible = false;
    60.         // Get access to the player object
    61.         player = GameObject.FindGameObjectWithTag("playerCapsule");
    62.         SceneRoot = GameObject.FindGameObjectWithTag("root");
    64.         if (player == null)
    65.         {
    66.             print("player not found");
    67.         }
    68.     }
    70.     /// <summary>
    71.     /// Do not use FixedUpdate here because performance drops dramatically (on dual core i5 macbook pro).
    72.     /// <seealso cref="PlayerView.cs"/>
    73.     /// </summary>
    74.     void Update()
    75.     {
    76.         // Get the horizontal movement changes from keyboard and
    77.         // negate them so we can move scene in reverse
    78.         deltaX = -Input.GetAxis("Horizontal");
    79.         deltaZ = -Input.GetAxis("Vertical");
    81.         // Only process floating origin movement if there is navigation input
    82.         // change and it is above noise/shake threshold.
    83.         // Performance: don't really want a sqr root here -
    84.         //   or even a squares comparision.
    85.         if ((Mathf.Abs(deltaX) + Mathf.Abs(deltaZ)) > NAV_SHAKE_THRESHOLD)
    86.         {
    88.             speedAdj = Time.deltaTime * speed;
    90.             // Scene reverse transform for floating origin navigation.
    91.             // Make movement delta proportional to time since last move and speed factor.
    92.             // Peformance: changed this to assignment so no mem alloc and GC needed, and
    93.             // 2 multiplies a bit faster than multiply by 3D vector.
    94.             reverseMovement.x = deltaX * speedAdj;
    95.             reverseMovement.z = deltaZ * speedAdj;
    97.             /*// Uncomment to do player collision detection.
    98.               //If player collided with close object then ...
    99.             if (Physics.Raycast(player_position, player.transform.TransformDirection(Vector3.forward), out rayCollision, COLLISION_DISTANCE, layerMask)
    100.                 && (rayCollision.distance < COLLISION_DISTANCE))
    101.             {
    102.                 /// ... bounce back a little from collision
    103.                 transform.Translate(-rotated_transform*COLLISION_ADJUST);
    104.             }
    105.             else // no collision, so move scene in reverse
    106.             {*/
    107.                 // use player camera rotation to modify reverse movement vector so that player forward corresponds to forward movement input
    108.                 rotated_transform = Quaternion.Euler(player.transform.localEulerAngles) * reverseMovement;
    110.                 SceneRoot.SetActive(false);
    112.                 // Move the scene to the new position by changing scene parent object transform.
    113.                 transform.Translate(rotated_transform);
    115.                 SceneRoot.SetActive(true);
    116.             /*}*/
    117.         }
    118.     }
    119. }
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    5. /*
    6. * Rotates the player, and hence attached player view, based on mouse input.
    7. *
    8. * Assumptions:
    9. * This script is attached to player object.
    10. * Camera is attached to player object.
    11. */
    12. public class PlayerView : MonoBehaviour
    13. {
    15.     // Control how fast player view responds to mouse movement
    16.     // More sophisticated games would allow the sensitivity to be changed to suit a player's preferences.
    17.     private const float sensitivity = 2f;
    19.     // Limit (clamp) the vertical rotation to +/- this angle
    20.     private const float vertClamp = 60.0f;
    22.     // Rotation about the horizontal axis (up and down)
    23.     private float currentRotationX = 0;
    24.     // Left and right rotation
    25.     private float currentRotationY = 0;
    27.     void Update()
    28.     {
    29.         // horizontal and vertical rotation at the same time
    30.         currentRotationX -= Input.GetAxis("Mouse Y") * sensitivity;
    31.         currentRotationX = Mathf.Clamp(currentRotationX, -vertClamp, vertClamp);
    33.         currentRotationY = transform.localEulerAngles.y + Input.GetAxis("Mouse X") * sensitivity;
    35.         transform.localEulerAngles = new Vector3(currentRotationX, currentRotationY, 0);
    36.     }
    37. }
    Last edited: Sep 13, 2019
  10. ncho


    Feb 1, 2014
    At a map size of 4096x4096 am I at risk for any physics inaccuracies? I haven't noticed anything yet but my game does rely on raycasting significantly so I'm somewhat paranoid about this.
  11. Stardog


    Jun 28, 2010
    Probably not. If you put the middle of the terrain at the origin will only be 2048 from the edges.
  12. ristophonics


    May 23, 2014
    Depends... Is the 4096x4096 the resolution of the heightmap or the distance in meters of the terrain?
  13. cosmochristo


    Sep 24, 2018
    If you are using a threshold of similar that size, I would have to say yes. The tile size might be ok for keeping the map vertex coords small but the threshold can independently lead to physics errors at much smaller distances.
    I did some very constrained physics tests because physics can be highly sensitive to error. I reported the experiment here:

    and you can see the videos here: part 1: 1DOF:
    part 2: 2DOF:

    Note that the videos compare continuous floating origin(CFO) with the threshold method that I call POS.

    The main point I would like to make is that there will always be some increased error with the lower 3space resolution induced by greater distance from the origin and complex calculations are sensitive to ti and magnify it.
    As for map tiles, I used to use a LOD system that effectively adjusts size when you get closer.
    Last edited: Oct 24, 2019
    PrimalCoder and buFFalo94 like this.
  14. buFFalo94


    Sep 14, 2015
    Really cool tests
    cosmochristo likes this.