Search Unity

2D Efficiency With Sprites and Game Objects

Discussion in '2D' started by Slev, Apr 15, 2014.

  1. Slev

    Slev

    Joined:
    Sep 8, 2012
    Posts:
    148
    I'm generating a map that uses game objects containing sprites as the base of the map. This is an isometric tile system. Based on research and checking the stats I can see that if sprites are not in view they are automatically hidden/not drawn. However, the encapsulating Game Objects still exist. My question is, if I have, let's say, 10,000 tiles. At most you might see 100 drawn to the screen, but all 10,000 encapsulating objects would exist. Over time (if let's say we expand out to >100,000 tiles) would these encapsulating game objects create a slowdown? Or do they exist solely as memory footprints for when a sprite needs to be rendered?
     
  2. Deleted User

    Deleted User

    Guest

    Well I would avoid so many game objects, they will add to memory usage and probably will also crawl the dynamic batching, consider that dynamic batching have to check what is in sight and what not, and may do it on many objects at once.

    I tried too using individual sprites objects for a tile system (topdown), on a 128x128 grid, wasn't that happy with performance so I opted for another method, generating meshes in cunks of 16x16 as single gameobjects, so a total of 8x8 objects, and fps sky rocket to the sky. Surely doing that way you lose the ability to use sprite renders, but as I was reading on some post the next update of unity should bring the ability to tile single sprites so it can save you some gameobjects.

    My suggestion is to try your method first, see how it performs and in case look for an alternative.
     
  3. SiegfriedCroes

    SiegfriedCroes

    Joined:
    Oct 19, 2013
    Posts:
    569
    I don't really know the answer but you can easily test this :) Make a new scene, add a new script to camera and in the start function of this script you do something like:

    Code (csharp):
    1.  
    2. void Start()
    3. {
    4.     for(int i = 0; i < 100000; i++)
    5.     {
    6.         new GameObject("Test");
    7.     }
    8. }
    9.  
    Just to see what happens when you there are a lot of objects in your scene.
     
  4. wondyr

    wondyr

    Joined:
    Nov 30, 2013
    Posts:
    36
    You could try OnBecameVisible and OnBecameInvisible as well as object pooling with instantiating them.
     
  5. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Yes, because they all have to be checked to see if they should be culled. Also Unity can't really handle that many GameObjects.

    --Eric
     
  6. Slev

    Slev

    Joined:
    Sep 8, 2012
    Posts:
    148
    Alright thanks, I have a chunking system in mind for fixing the memory issue, but I'm glad for the information.
     
  7. rubenedra

    rubenedra

    Joined:
    Oct 22, 2012
    Posts:
    8
    @Neurological...
    I'm trying exactly to do what you specify ("..., generating meshes in chunks of 16x16 as single gameobjects, ..." ) but I'm rather clueless on how to do this. Can you maybe explain it in a bit more detail on how to combine lets say 2 sprites as a single gameobject?
     
  8. Deleted User

    Deleted User

    Guest

    Well for first I don't combine sprites, you can't combine sprite renderer, I use procedurally generated meshes, simple planes. I use a 2D array as reference for where to start adding the vertices.

    So all I do is to add vertices, uvs and triangles to three Lists, then add all of them to a Mesh component. I use some static functions to go trough a loop and read a text file from where the 2d array is. Is a bit complicated for me to explain, I just share a part of my code so you can take a look:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.IO;
    4. using System.Collections.Generic;
    5.  
    6. public class UDData {
    7.  
    8.     static private List<Vector3> verts = new List<Vector3>();   //List of vertices needed for the mesh
    9.     static private List<Vector2> uvs = new List<Vector2>();     //List of uvs needed for the mesh
    10.     static private List<int> tris = new List<int>();            //List of triangles needed for the mesh
    11.     static private Vector4 uvOffset;                            //UV offset calculated from the 2D array ints
    12.    
    13.     static private int cCount = 8;                              //Number of cells
    14.     static private int cSize = 16;                              //Cell size (16*16)
    15.    
    16.     static private int tWidth = 128;                            //TEMP texture width
    17.     static private int tHeight = 128;                           //TEMP texture height
    18.     static private float tSize = 16;                            //TEMP base tile size (16*16 pixels)
    19.    
    20.     // Build geometry of each cell
    21.     static public void CreateCellMesh (string mPath, string ID, Transform parent) {
    22.         verts.Clear (); uvs.Clear (); tris.Clear ();
    23.        
    24.         int[,] cGrid = ReadGridData (mPath, ID, cSize);
    25.        
    26.         Vector3 p = Vector3.zero;
    27.         int i = 0;
    28.        
    29.         for(int x = 0; x < cSize; x++) {
    30.             for(int y = 0; y < cSize; y++) {
    31.                 p = new Vector3(x * 1 + 0.5f,y * 1 + 0.5f, 0);
    32.                
    33.                 if(cGrid[y, x] != 0) {
    34.                    
    35.                     verts.Add (new Vector3(-0.5f + p.x, -0.5f + p.y, p.z));
    36.                     verts.Add (new Vector3(-0.5f + p.x, 0.5f + p.y,  p.z));
    37.                     verts.Add (new Vector3 (0.5f + p.x, 0.5f + p.y, p.z));
    38.                     verts.Add (new Vector3( 0.5f + p.x, -0.5f + p.y, p.z));
    39.                    
    40.                    
    41.                     Vector4 uvOffset = new Vector4(
    42.                         ((cGrid[y, x]) * tSize)  / tWidth,
    43.                         ((cGrid[y, x]) * tSize) / tHeight,
    44.                         tSize / tWidth,
    45.                         tSize / tHeight
    46.                         );
    47.                    
    48.                     uvs.Add (new Vector2 (uvOffset.x - uvOffset.z, 1f - uvOffset.w )); //0, 0
    49.                     uvs.Add (new Vector2 (uvOffset.x - uvOffset.z, 1f )); //0, 1   
    50.                     uvs.Add (new Vector2 (uvOffset.x, 1f)); //1, 1
    51.                     uvs.Add (new Vector2 (uvOffset.x, 1f - uvOffset.w)); //1, 0
    52.                    
    53.                     Debug.Log ("0.0 = " + uvs[0] + " 0.1 = " + uvs[1] + " 1.1 = " + uvs[2] + " 1.0 = " + uvs[3]);
    54.                    
    55.                     /*
    56.                     uvs.Add (new Vector2 (0,0 )); //0, 0
    57.                     uvs.Add (new Vector2 (0,1 )); //0, 1   
    58.                     uvs.Add (new Vector2 (1,1)); //1, 1
    59.                     uvs.Add (new Vector2 (1,0)); //1, 0
    60.                     */
    61.                    
    62.                     tris.Add (i);
    63.                     tris.Add (i + 1);
    64.                     tris.Add (i + 2);
    65.                     tris.Add (i);
    66.                     tris.Add (i + 2);
    67.                     tris.Add (i + 3);
    68.                    
    69.                     i += 4;
    70.                 }
    71.             }
    72.         }
    73.        
    74.         Vector3[] vertices = verts.ToArray ();
    75.         Vector2[] uv = uvs.ToArray ();
    76.         int[] triangles = tris.ToArray ();
    77.        
    78.         if(vertices.Length != 0 || uv.Length != 0 || triangles.Length != 0) {
    79.            
    80.             GameObject o = new GameObject (ID);
    81.            
    82.             Mesh mesh = new Mesh ();
    83.            
    84.             if (!o.GetComponent<MeshFilter> ())
    85.                 o.AddComponent<MeshFilter> ();
    86.            
    87.             mesh = o.GetComponent<MeshFilter> ().mesh;
    88.            
    89.             mesh.vertices = vertices;
    90.             mesh.uv = uv;
    91.             mesh.triangles = triangles;
    92.            
    93.             if (!o.GetComponent<MeshRenderer> ())
    94.                 o.AddComponent<MeshRenderer> ();
    95.            
    96.             MeshRenderer mr = o.GetComponent<MeshRenderer> ();
    97.             mr.castShadows = mr.receiveShadows = false;
    98.            
    99.             mesh.RecalculateBounds ();
    100.            
    101.             o.transform.parent = parent;
    102.         }
    103.     }
    104.  
    105.     //Return a 2D array of ints
    106.     static private int[,] ReadGridData (string fPath, string id, int size) {
    107.         string data = NMPath.mPath + fPath;
    108.         int[,] a = new int[size,size];
    109.  
    110.         using (StreamReader sr = new StreamReader(data)) {
    111.             string line;
    112.  
    113.             int[] aInt = new int[size * size];
    114.  
    115.             while ((line = sr.ReadLine()) != null) {
    116.  
    117.                 if(line.Contains(id + "_GRID")) {
    118.                     string[] fLine = line.Split('=');
    119.  
    120.                     string[] iString = fLine[1].Split (',');
    121.                
    122.                     for(int i = 0; i < aInt.Length ; i++)
    123.                         aInt[i] = int.Parse(iString[i].Trim(' '));
    124.                 }
    125.  
    126.                 int j = 0;
    127.                 for (int x = 0; x < size; x++){
    128.                     for (int y = 0; y < size; y++){
    129.                         a[x,y] = aInt[j];
    130.                         j++;
    131.                     }
    132.                 }
    133.             }
    134.         }  
    135.         return a;
    136.     }
    137. }
    138.  
    Is not self explanatory enough I guess.
     
    VrTechEx likes this.
  9. VrTechEx

    VrTechEx

    Joined:
    Aug 4, 2013
    Posts:
    40

    I really think the same as your algorithm. I was going to try your code but

    Code (csharp):
    1.  
    2. string data = NMPath.mPath + fPath;
    3.  
    the "NMPath.mPath" is missing ? Can you please explain more about this :D Thanks !
     
  10. Deleted User

    Deleted User

    Guest

    Thats the path to the file I read data from.