ECS with ScritpableObjects

Hi, here is a slight issue with ECS, just before explaining, know that I am a bit new with ECS and dots, i don’t really know how everything works…

Goal

I want to create a Voxel engine.

My steps

Issues

  1. I have a memory leak, i think its due to the NativeArray but i don’t know when i need to dispose it, i’m not sure that i should be able to destroy it in the OnDestroy of the monoBehaviour
  2. I’ve got the following error about serializable : https://paste.ofcode.org/R4pySRdbXmJWFL8XsH6SsA
  3. the entity isn’t loaded (surely because of the previous error)

It’s not possible to create a NativeArray in a baker and store it in an IComp. No NativeContainer works in a baker only at runtime.
The reason is that the data has to be serialized into binary subscene data and something like a NativeArray is not inlined/linear data as it’s pointing to a different part of memory. That’s also why you get the 2nd error.

Instead you have 2 options. Store your element in a DynamicBuffer<VoxelType> or create a Blob asset

I’ve been rapidly through the blob assets, so well for dynamicBuffers, IMO its not ideal because its not a thing that gonna change in runtime, its just a static array of values that will be shared accross the app (kind of a config) on read only

Also for the blob, it seems to be what i’m looking for even if i’m not fully understand it and even if its look like some dark magic to have a workaround but i’ll check on it more deeply

thanks

This site has a mechanism to embed code into the page.
9787203--1404309--upload_2024-4-22_16-37-20.png
No need to force people to go to an external site for that.

Yes i know but the message will grow too fast, that why i’m using pastecord or past.ofcode

well, for this example its not that much of code i admit, i have the habbit to use these websites because sometimes i want to upload the whole file to insure people has the context

Scrolling is less of a problem than having some random site profile the visitors.

Depending in which communities, sometimes they hate having a bunch of code right in the middle of explications because you see less the explanations. I understand your point of view

HI again, i’ve implemented the blob asset, but i still have an issue, because i wanted to have an entity singleton (to get it anywhere form the other systems) i need to have a ref, but it seems i need to allocate the size of the ref with persistent which make leaks, i wanted to use it like this :

        VoxelsHolder voxelsHolder = SystemAPI.GetSingleton<VoxelsHolder>();
        ref BlobArray<VoxelType> voxelTypes = ref voxelsHolder.BlobAssetReference.Value.voxels;

        // .... voxelTypes[voxel.voxelId].floatColor ...//

here is my new code :

public class VoxelsHolderAuthoring : MonoBehaviour
{
    public VoxelsHolderSO voxelsHolderSO;

    public class Baker : Baker<VoxelsHolderAuthoring> {
        public override void Bake(VoxelsHolderAuthoring authoring) {
            Entity entity = GetEntity(TransformUsageFlags.None);
            BlobBuilder blobBuilder = new (Allocator.Temp);

            ref BlobVoxelsHolder blobVoxelsHolder = ref blobBuilder.ConstructRoot<BlobVoxelsHolder>();
            BlobBuilderArray<VoxelType> voxelsData = blobBuilder.Allocate(ref blobVoxelsHolder.voxels, authoring.voxelsHolderSO.voxels.Length);

            for (int i = 0; i < authoring.voxelsHolderSO.voxels.Length; i++) {
                VoxelTypeSO voxelSO = authoring.voxelsHolderSO.voxels[i];
                voxelsData[i] = new VoxelType(new float4(voxelSO.color.r, voxelSO.color.g, voxelSO.color.b, voxelSO.color.a));
            }

            BlobAssetReference<BlobVoxelsHolder> blobAssetReference = blobBuilder.CreateBlobAssetReference<BlobVoxelsHolder>(Allocator.Persistent);

            AddComponent(entity, new VoxelsHolder { BlobAssetReference = blobAssetReference });

            blobBuilder.Dispose();
        }
    }
}

public struct VoxelsHolder : IComponentData {
    public BlobAssetReference<BlobVoxelsHolder> BlobAssetReference;
}

public struct BlobVoxelsHolder : IComponentData {
    public BlobArray<VoxelType> voxels;
}

public struct VoxelType {
    public float4 floatColor;

    public VoxelType(float4 color) {
        this.floatColor = color;
    }


}

You can wrap code in a spoiler:
Spoiler with a ton a code

using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Transforms;
using UnityEngine;

namespace HelloCube.Reparenting
{
    public partial struct ReparentingSystem : ISystem
    {
        bool attached;
        float timer;
        const float interval = 0.7f;

        [BurstCompile]
        public void OnCreate(ref SystemState state)
        {
            timer = interval;
            attached = true;
            state.RequireForUpdate<ExecuteReparenting>();
            state.RequireForUpdate<RotationSpeed>();
        }

        [BurstCompile]
        public void OnUpdate(ref SystemState state)
        {
            timer -= SystemAPI.Time.DeltaTime;
            if (timer > 0)
            {
                return;
            }
            timer = interval;

            var rotatorEntity = SystemAPI.GetSingletonEntity<RotationSpeed>();
            var ecb = new EntityCommandBuffer(Allocator.Temp);

            if (attached)
            {
                // Detach all children from the rotator by removing the Parent component from the children.
                // (The next time TransformSystemGroup updates, it will update the Child buffer and transforms accordingly.)

                DynamicBuffer<Child> children = SystemAPI.GetBuffer<Child>(rotatorEntity);
                for (int i = 0; i < children.Length; i++)
                {
                    // Using an ECB is the best option here because calling EntityManager.RemoveComponent()
                    // instead would invalidate the DynamicBuffer, meaning we'd have to re-retrieve
                    // the DynamicBuffer after every EntityManager.RemoveComponent() call.
                    ecb.RemoveComponent<Parent>(children[i].Value);
                }

                // Alternative solution instead of the above loop:
                // A single call that removes the Parent component from all entities in the array.
                // Because the method expects a NativeArray<Entity>, we create a NativeArray<Entity> alias of the DynamicBuffer.
                /*
                ecb.RemoveComponent<Parent>(children.AsNativeArray().Reinterpret<Entity>());
                */
            }
            else
            {
                // Attach all the small cubes to the rotator by adding a Parent component to the cubes.
                // (The next time TransformSystemGroup updates, it will update the Child buffer and transforms accordingly.)

                foreach (var (transform, entity) in
                         SystemAPI.Query<RefRO<LocalTransform>>()
                             .WithNone<RotationSpeed>()
                             .WithEntityAccess())
                {
                    ecb.AddComponent(entity, new Parent { Value = rotatorEntity });
                }

                // Alternative solution instead of the above loop:
                // Add a Parent value to all entities matching a query.
                /*
                var query = SystemAPI.QueryBuilder().WithAll<LocalTransform>().WithNone<RotationSpeed>().Build();
                ecb.AddComponent(query, new Parent { Value = rotatorEntity });
                */
            }

            ecb.Playback(state.EntityManager);

            attached = !attached;
        }
    }
}

Ohhh i didn’t knew, thanks for the tips

Edit, i had to update to Entities 1.2 and use IDisposable in the component