Blob asset benefits

Hello everybody!
Im trying to figure out benefits of blob asset in compare with regular IComponentData and i dont understand.
I have some singleton component (IComponentData) that store static data like game field size, entity prefabs and so on. I get that singleton and read data from it that way:

public class MovementSystem : SystemBase
{
     protected override void OnUpdate()
    {
        var dt = Time.DeltaTime;
        var m_gameController = GetSingleton<GameController>();

        Entities.
            ForEach((Entity entity, ref Translation translation, ref GridPosition gp, in MovementSpeed ms, in MovementDirection movementDirection) =>
        {
            // some calculations with reading m_gameController.FieldSize (int)
        }).ScheduleParallel();
    }
}

I read that blob asset is intended for store static data and accessing it inside jobs. But what is the difference with my way reading static data?

The benefits of blob assets is that you can store a BlobAssetReference on an IComponentData. So say for example, you have two kinds of enemies in your game. One that is slow and one that is fast. Essentially you only have two different values for movement speed, but many more numbers of enemies. Instead of storing the movement speed locally for each enemy you can avoid duplicate data if you just have two blob assets with the two movement speed values, and assign the enemies a reference to either blob asset.

Now, I guess in this case, movement speed would probably be 4 bytes and the blob asset reference would be 8 bytes, so you wouldn’t actually reduce the memory footprint. But imagine instead of movement speed it is something more complicated that takes up way more bytes, then it can be worth it.

As to how this compares to using a singleton entity with that data, I’m not sure. You could achieve similar functionality by storing an entity on the IComponentData as a reference to the data, instead of the blob asset reference. The only benefit I can think of, is that every singleton entity takes up 16kB, since it needs to occupy a chunk, whereas blob assets can be packed together better and should take up less memory. Performance wise, maybe blob assets are faster?

4 Likes

I don’t use ECS anymore, so I can’t comment on that part. But when I ripped ECS out of my game a couple weeks ago, I pulled out BlobAssets in one of my subpackages because they’re extremely useful for regular jobs (launched from MonoBehaviours with no entities involved). What a blob represents is a variable-sized piece of structured data. Within it, you can have arrays of different types/lengths or arrays of arrays. So instead of managing 20 different NativeArray<>s, you have a single blob.

For example, I currently have the following structure which is all stored in one blob (kinda playing loose with C# syntax to help give you an idea, also not exactly how it’s laid out, but close):

struct Building {
    public float wallHeight;
    public float partialWallHeight;
    public MapWalls wallsX;
    public MapWalls wallsZ;
    public MapTiles tiles;
    public PropSet props;
    public MaterialSet materials;
    public LightmapUVLayout lightmap;
    public BlobArray<byte> roomIds; }

struct MapWalls {
    public int sizeX, sizeY, sizeZ;
    public BlobArray<int> elements; }

struct MapTiles {
    public int sizeX, sizeY, sizeZ, xTimesZ;
    public BlobArray<ushort> data; }

struct PropSet {
    public int propCount;
    public BlobArray<int> propIdToHoleId;
    public BlobArray<Hole> holes; }

struct Hole {
    public bool showWallBottom;
    public BlobArray<float2> verticesFace;
    public BlobArray<ushort> indicesFace;
    public BlobArray<float2> verticesInside;
    public BlobArray<ushort> indicesInside; }

struct MaterialSet {
    public int count;
    public BlobArray<float2> tileUv; }

struct LightmapUVLayout {
    public BlobArray<Chart> charts;
    public ChartIndexer chartIndexer; }

struct ChartIndexer {
    public BlobArray<short> tileChartIndices;
    public BlobArray<short> wallChartIndices;
    public int sizeX_X, sizeX_Z, sizeY, sizeZ_X, sizeZ_Z, zStart; }

If that were NativeArrays, I might need something like…

NativeArray<int> wallsX_elements;
NativeArray<int> wallsZ_elements;
NativeArray<ushort> tiles_data;
NativeArray<int> props_propIdToHoleId;
NativeArray<float2> materials_tileUv;
NativeArray<Chart> lightmap_charts;
NativeArray<short> lightmap_chartIndexer_tileChartIndices;
NativeArray<short> lightmap_chartIndexer_wallChartIndices;
NativeArray<byte> roomIds;

// NativeArray<> doesn't support arrays-of-arrays, so none of these would work as-is
// can hack around it by storing offsets and stuff, but if the structure were more complex
// that would be more complex also
NativeArray<bool> props_holes_showWallBottom;
NativeArray<NativeArray<float2>> props_holes_verticesFace;
NativeArray<NativeArray<ushort>> props_holes_indicesFace;
NativeArray<NativeArray<float2>> props_holes_verticesInside;
NativeArray<NativeArray<ushort>> props_holes_indicesInside;

Don’t feel like allocating and disposing those individually, and that wouldn’t be performant either. So having a persistent cached blob with all the data bound together in a logical structure is very helpful.

Another thing you can use Blobs for if you’re feeling brave is for psuedo-virtual types. Unity.Physics uses it this way – a box collider, a sphere collider, a mesh collider, or a whole hierarchy of compound colliders – all of them are just a BlobAssetReference<Collider>.

A final piece of blobs is that they’re easy to serialize/deserialize, since they’re literally just a blob of bytes. I think ECS uses that internally for conversion systems. So if you’re saving things to disk or sending it over the network, blobs have a place there.

8 Likes

I really wanted Unity to do this and split the packages. I’m doing the same in my project and been dying to remove ECS package from the project and keep the Blobs — just don’t want the hassle of keeping my version up-to-date with Unity’s. :smile:

Yep. This is very useful. Also, it allows for a great deal of flexibility depending on how you set up your BlobBuilding flow. I set up my own HotReloading solution for all the State, Moves, Collision data in the fighting game I’m developing in a couple of days by using this and it’s pretty easy to maintain.

The Serialization/Deserialization aspect of it is pretty good as well — although you have to manage versioning of it mostly by hand. By itself, this isn’t isn’t a problem early on in development but can get annoying having to write migration scripts once you have a lot of data built on top of the system. I’m not sure yet if I’d want a bunch of serialization magic on top of this to assist with versioning or if I’d like it to be kept flexible and just handle it on a case-by-case basis :slight_smile:

Just take the source file from there; has everything you need in it for blobs and blob builders to work. As it says at the top, that file is Unity Companion License (since it was literally ripped out of Unity code): https://gitlab.com/burningmime/burning-rpg-framework/-/blob/master/Packages/building/src/external/Blobs.cs . It has no dependencies other than Burst/Collections, so works without any ECS packages (and works with new versions so you’re not stuck on Collections 1.5 or whatever!)

That’s a good point; it would be very hard to support multiple versions of complex structures. Not a problem if sending them back and forth over the network for a multiplayer game (assuming the game versions are checked beforehand), but would be a huge PITA for save game files.