DOTS Netcode: (tutorial) syncing values between monobehaviour and ecs.

After much difficulty starting off with dots Netcode, I finally found a pretty easy way to sync data between mono behaviours and ecs components. I thought I’d share it to help fellow noobs in the future.

See code below.

The motivation: I was using AR foundation and wanted to sync tracking over dots Netcode. But so far, AR foundation uses only monobehaviours.

The mono script is attached to a game object with ConvertToEntity Client Server script on it, set to “Convert and inject”, and “Client”. It stores an ecs component as a field called data. Upon conversion it stores a reference to its converted entity.

The goal is to modify one value from mono, and have it sync to ecs, and the other value modified from ecs, and sync to mono.

On the mono’s update method, it looks for the client world, and then retrieves the data component from its converted entity using GetComponentData. It then copies the modified value from it. It then copies its own modified value back into the entity using SetComponentData.

I haven’t yet found a way to ensure that one value can’t be modified from the wrong end, but I suppose you could create methods to do the work for you with very explicit names like ModifyValueFromECSOnly(value)

Anyway, I hope this simple example saves others the huge headache that I experienced trying to get this working.

using Unity.Entities;
using Unity.NetCode;
using UnityEngine;

public class HybridTestMono : MonoBehaviour, IConvertGameObjectToEntity {
    public HybridTestData data;
    public Entity entity;
   
    void Update() {

        data.MonoModifiedValue += 1;
       
        foreach (World world in World.All) {
            if (world.GetExistingSystem<ClientSimulationSystemGroup>() != null) {
                HybridTestData component = world.EntityManager.GetComponentData<HybridTestData>(entity);
                data.EcsModifiedValue = component.EcsModifiedValue;

                component.MonoModifiedValue = data.MonoModifiedValue;
                world.EntityManager.SetComponentData(entity, component);
            }

        }
    }

    public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) {
        HybridTestData data = new HybridTestData {EcsModifiedValue = 3000};
        dstManager.AddComponent<HybridTestData>(entity);
        dstManager.SetComponentData(entity, data);
        this.entity = entity;
    }
}

public struct HybridTestData : IComponentData {
    public float EcsModifiedValue;
    public float MonoModifiedValue;
}


[UpdateInGroup(typeof(ClientSimulationSystemGroup))]
public class HybridTestSystem : SystemBase {
    protected override void OnUpdate() {
       
      
        Entities.ForEach((ref HybridTestData hybridTest) => {
            hybridTest.EcsModifiedValue += 1;
        }).WithoutBurst().Run();
    }
}
2 Likes

Instead of using:
world.GetExistingSystem<ClientSimulationSystemGroup>() != null

The 1.0+ version of this check is simply:
world.IsClient()

Making use of the extensions: Class ClientServerWorldExtensions | Netcode for Entities | 1.0.17

1 Like