Search Unity

2D arrays of ints/enums VS. a single 2D array of class - Best Practices

Discussion in 'Scripting' started by RidgeWare, Jan 17, 2019.

  1. RidgeWare

    RidgeWare

    Joined:
    Apr 12, 2018
    Posts:
    67
    Hey guys, I have a fairly straightforward map design in my game, where my maximum size of each level will always be within a 100x100 tiles. So creating a 2D array [100,100] to represent it and using the index for coordinates is perfect for me.

    Within the game code, I need to store some limited information as to the state of each tile - basic things which simple ints/enums will provide.

    But I can't decide whether to create one 2D array of a class - where each class instance itself contains the ints/enums - or a few separate 2D arrays of ints/enums (with no class involved).

    The easiest thing would be to simply create one array, with a class that holds everything, but what's best in terms of efficiency/memory management? 100x100 would be 10,000 class objects created each level... (although I won't need to iterate over all of them, just go to each of them directly as & when needed).

    Am i worrying needlessly?

    Cheers!
     
    Last edited: Jan 17, 2019
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,537
    I wouldn't use a class.

    I'd use an array of a struct.

    Like this:
    Code (csharp):
    1.  
    2. public struct Data
    3. {
    4.     public int SomeValue;
    5.     public SomeEnum SomeEnumValue;
    6.     public float SomeOtherValue;
    7. }
    8.  
    9. public Data[] AllData = new Data[100];
    10.  
    Note that because classes are ref objects, they'd be allocated on the heap individually, and the array would just be a bunch of pointers to each class instance.

    Where as a struct is value type and will be packed into the array. The array is on the heap, but the entirity of the structs are in the array. This makes for a large linear track of memory that is faster to access, and in edge cases can reduce cache misses when linearly looping it. (though arguments could be made about how it taking up a larger contiguous block of memory can make large memory management a problem... but at 100 entries of about about 4 bytes per property/field... it's not going to be a big deal).

    Also, technically a 1d array is more performant than a 2d array to access. But the difference is so small that it's not really a high concern... but hey, another point for 1d array of struct.

    Finally... and this is the main reason I say struct.

    Readability.

    You can give names to the fields so you know what they are resulting in self-documenting code. Your various data isn't spread out, but is instead is readably in one place. And you don't have to create a new array for every field just because it changes type (you can't have a 2d array of int AND float, you'd need 2 different arrays).
     
  3. RidgeWare

    RidgeWare

    Joined:
    Apr 12, 2018
    Posts:
    67
    Thanks for the reply. I hadn't considered structs. Although it would be 10,000 not 100 entries. But still, for a game that's only going to need to create the array one time only on Awake (with no further iteration) I suppose that's still pretty reasonable.

    It obviously would be a lot more straightforward to access "tileArray[12,14].type/occupied/etc" than access several different explicit arrays called tileType/tileOccupied/etc.
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,742
    First of all, I highly recommend you do NOT access the tiles directly all over your code. You will one day hate yourself for it when you decide to change how it is stored.

    Instead have a manager class that takes coordinates (ideally also in a struct) and returns the struct, then you can modify it, then write it back, since structs are value types.

    Alternatively, have a 2D array of classes so you get a reference type and can modify the in-place data directly. But still I would recommend having a manager class to store them rather than indirecting on them everywhere in your code.

    If your tile coordinates are in TileCoordinates struct, your map dimensions are in MapDimensions, your tile contents are in a TileContents class, then your TileManager would have methods like:

    Code (csharp):
    1. // factory class for making TileManagers of a particular size:
    2. public static TileManager Create( MapDimensions dimensions);
    3.  
    4. // instance methods for getting at tiles:
    5. // this would be the one place in your code that knows how everything is stored.
    6. public TileContents Get( TileCoordinates coord);