Search Unity

[Question] Optimisation for 10k gameobjects.

Discussion in 'Physics' started by SparklingSushi, Sep 15, 2017.

  1. SparklingSushi

    SparklingSushi

    Joined:
    Dec 6, 2016
    Posts:
    3
    Hey folks! I'm looking for any ways to optimize my project. I need to spawn 10k asteroids and manage their behaviors through game cycle, I need to keep all asteroids simulated (colliding and flying). So that every asteroid has rigidbody and circle collider (which is quite bad for a performance).

    What is happening right now:
    • At runtime I Instantiate 10k gameobjects.
    • Set them random velocity.
    • Cache all data that I need to reduce GetComponent calls.
    • In Update function I go through all spawned asteroids and check if they in camera area. If they are collider, rigidbody, spriterenderer are turn on and the velocity is set. Otherwise I set them as kinematic and turn off other components.
    Questions & Problems:
    • How to keep collisions on asteroids which are off the camera in a efficient way?
    • What should I consider to increase performance?
    • Can I use object pooling here in some way?
    • I thought about some kind of boids flocking for asteroids that are outside of camera view but this behavior may be a little strange for flying asteroids.
    • Are there any other approaches that i can consider in this project?


    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. namespace WorldObjects.Asteroids
    4. {
    5.     public class AsteroidSpawner : MonoBehaviour
    6.     {
    7.         [SerializeField] private AsteroidData _asteroidData;
    8.  
    9.         private const int AsteroidCount = 10000;
    10.         private float initialPositionX = -49.5f;
    11.         private float initialPositionY = 49.5f;
    12.         private GameObject[] _asteroids;
    13.         private Vector2[] _asteroidsVelocities;
    14.         private Rigidbody2D[] _asteroidsRigidbodies;
    15.         private Collider2D[] _asteroidsColliders;
    16.         private SpriteRenderer[] _asteroidsSpriteRenderers;
    17.  
    18.         void Start()
    19.         {
    20.             _asteroids = new GameObject[AsteroidCount];
    21.             _asteroidsVelocities = new Vector2[AsteroidCount];
    22.             _asteroidsRigidbodies = new Rigidbody2D[AsteroidCount];
    23.             _asteroidsColliders = new Collider2D[AsteroidCount];
    24.             _asteroidsSpriteRenderers = new SpriteRenderer[AsteroidCount];
    25.             SpawnAsteroids();
    26.             StoreAsteroidsData();
    27.         }
    28.  
    29.         void Update()
    30.         {
    31.             for (int i = 0; i < AsteroidCount; i++)
    32.             {
    33.                 if (_asteroids[i])
    34.                 {
    35.                     var tmp = Camera.main.WorldToViewportPoint(_asteroids[i].transform.position);
    36.                     if (0f < tmp.z && 0f < tmp.x && tmp.x < 1f && 0f < tmp.y && tmp.y < 1f)
    37.                     {
    38.                         _asteroidsRigidbodies[i].isKinematic = false;
    39.                         _asteroidsSpriteRenderers[i].enabled = true;
    40.                         _asteroidsColliders[i].enabled = true;
    41.                         _asteroidsRigidbodies[i].velocity = _asteroidsVelocities[i];
    42.                     }
    43.                     else
    44.                     {
    45.                         _asteroidsRigidbodies[i].isKinematic = true;
    46.                         _asteroidsSpriteRenderers[i].enabled = false;
    47.                         _asteroidsColliders[i].enabled = false;
    48.                         _asteroids[i].transform.position += new Vector3(_asteroidsVelocities[i].x, _asteroidsVelocities[i].y,0f) * Time.deltaTime;
    49.                     }
    50.                 }
    51.             }
    52.         }
    53.  
    54.         private void SpawnAsteroids()
    55.         {
    56.             int k = 0;
    57.             for (float i = initialPositionX; i <= 49.5f; i+=1)
    58.             {
    59.                 for (float j = initialPositionY; j >= -49.5; j-=1)
    60.                 {
    61.                     var newAsteroid = Instantiate(_asteroidData.AsteroidPrefab, new Vector3(i, j, 0f), Quaternion.identity);
    62.                     _asteroids[k] = newAsteroid;
    63.                     k++;
    64.                 }
    65.             }
    66.         }
    67.  
    68.         private void StoreAsteroidsData()
    69.         {
    70.             var rnd = new System.Random(1);
    71.             for (int i = 0; i < AsteroidCount; i++)
    72.             {
    73.                 int directionX = rnd.Next(-1, 1);
    74.                 float vX = (float)rnd.NextDouble();
    75.                 int directionY = rnd.Next(-1, 1);
    76.                 float vY = (float)rnd.NextDouble();
    77.                 _asteroidsVelocities[i] = new Vector2(directionX * vX * _asteroidData.MaxVelocity, directionY * vY * _asteroidData.MaxVelocity);
    78.                 _asteroidsRigidbodies[i] = _asteroids[i].GetComponent<Rigidbody2D>();
    79.                 _asteroidsColliders[i] = _asteroids[i].GetComponent<Collider2D>();
    80.                 _asteroidsSpriteRenderers[i] = _asteroids[i].GetComponent<SpriteRenderer>();
    81.             }
    82.         }
    83.     }
    84. }
    85.  
     
  2. Plystire

    Plystire

    Joined:
    Oct 30, 2016
    Posts:
    142
    Do you need 10,000 asteroids? Biggest boost to speed you're going to get is lowering the number of bodies you have to worry about.

    "Culling" physics based on camera view will introduce some interesting consequences. Mainly it means that only asteroids on-screen are going to be moving. So, if I sit in a spot that the asteroids on-screen aren't going to hit, I can stay there until they leave the screen, and then I'm all alone because nothing will ever come from off-screen. After that, if I move in any direction, those asteroids that left the screen will now be perfectly lined up with the screen edge!

    Object pooling? Sure. You can pool your bullets, and even pool your asteroids. But I don't think that's going to do much for you if you're hurting in performance outside of the realm of constantly creating/destroying objects.

    Something you might consider is changing your asteroid physics "culling" in such a way that you can "turn off" physics and "turn on" simple movement. If your asteroids are off-screen, have them carry on with simple movement and no collision. This would prevent the problem I presented above, but introduce a few more issues (like overlapping asteroids that come on screen). Still, it would help maintain physics performance while not immobilizing them off-screen.
     
  3. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    You will need a really fancy solution for this. Like.... REALLY fancy.

    Basically, you will need to harness the black magic of compute shaders. You will need to write custom physics for your asteroids that will be massively parallelized on the GPU. Your C# and your compute shader will share a buffer that contains the positions and rotations of every asteroid. Your compute shader's job will be to simulate all of this on the GPU, while your script will take care of reading from the pos/rot buffer and applying them to your asteroids on every frame (or fixed frame)

    You are in luck, because if I understand correctly, you'll be using circles (which are the easiest and lightest form of collision to implement). But yeah. This'll be hard work
     
  4. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    So the problem is "simulate an asteroid belt". This should be doable quite easily actually but you'd need to use a compute shader. It's pretty much the perfect fit for it - or use storage textures. This is a general purpose gpu job.

    The performance isn't the hard part for 10k here, but making the required shaders etc...