This is how you can forward transform data from a MonoBehaviour
to an Entity
:
// src* https://gist.github.com/andrew-raphael-lukasik/530e90ee27cd7a9be47351e03b2634d1
using UnityEngine;
using Unity.Entities;
using Unity.Transforms;
using Unity.Rendering;
using Unity.Mathematics;
public class MirrorThisTransformInEcsWorld : MonoBehaviour
{
[SerializeField] Mesh _mesh;
[SerializeField] Material _material;
EntityManager _entityManager;
World _world;
Entity _entity;
void OnEnable ()
{
_world = World.DefaultGameObjectInjectionWorld;
_entityManager = _world.EntityManager;
_entity = _entityManager.CreateEntity();
_entityManager.AddComponent<IsMirroredTransform>( _entity );
_entityManager.AddComponentObject( _entity , this );// optional
_entityManager.AddComponent<LocalToWorld>( _entity );
RenderMeshUtility.AddComponents(
_entity , _entityManager ,
new RenderMeshDescription( UnityEngine.Rendering.ShadowCastingMode.On , receiveShadows:true , renderingLayerMask:1 ) ,
new RenderMeshArray( new Material[]{ _material } , new Mesh[]{ _mesh } ) ,
MaterialMeshInfo.FromRenderMeshArrayIndices( 0 , 0 )
);
#if UNITY_EDITOR
_entityManager.SetName( _entity , $"{gameObject.name} #{gameObject.GetInstanceID()}" );
#endif
}
void Update ()
{
_entityManager.SetComponentData( _entity , new LocalToWorld{
Value = transform.localToWorldMatrix
} );
}
void OnDisable ()
{
if(_world.IsCreated)//prevents error on (editor) game exiting play mode
_entityManager.DestroyEntity( _entity );
}
}
public struct IsMirroredTransform : IComponentData {}
But be aware that this solution will not scale to 100s well. For that you need to go IJobParallelForTransform
/ TransformAccessArray
:
// src* https://gist.github.com/andrew-raphael-lukasik/530e90ee27cd7a9be47351e03b2634d1
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Jobs;
using Unity.Entities;
using Unity.Transforms;
using Unity.Rendering;
using Unity.Collections;
using Unity.Mathematics;
public class MirrorThisTransformInEcsWorld : MonoBehaviour
{
public static TransformAccessArray Transforms;
public static List<MirrorThisTransformInEcsWorld> Instances = new ();
public static Dictionary<MirrorThisTransformInEcsWorld,int> Indices = new ();
public static NativeHashMap<int,Entity> Entities;
[SerializeField] Mesh _mesh;
[SerializeField] Material _material;
EntityManager _entityManager;
World _world;
void OnEnable ()
{
_world = World.DefaultGameObjectInjectionWorld;
_entityManager = _world.EntityManager;
if( !Transforms.isCreated )
Transforms = new TransformAccessArray( capacity:1 );
if( !Entities.IsCreated )
Entities = new NativeHashMap<int,Entity>( initialCapacity:32 , Allocator.Persistent );
int index = Transforms.length;
Transforms.Add( transform );
Instances.Add( this );
Indices.Add( this , index );
Entities.Add( index , CreateMyEntity() );
}
void OnDisable ()
{
int index = Indices[this];
Entity entity = Entities[index];
Transforms.RemoveAtSwapBack( index );
Instances.RemoveAtSwapBack( index );
Indices.Remove( this );
Entities.Remove( index );
if( index<Transforms.length && Transforms.length!=0 )// did these RemoveAtSwapBack changed sb else index ?
{
int affectedIndex = Transforms.length;
var affectedInstance = Instances[ index ];
Indices[affectedInstance] = index;
Entity e = Entities[affectedIndex];
Entities.Remove( affectedIndex );
Entities.Add( index , e );
}
if( Entities.Count==0 ) Entities.Dispose();
if( _world.IsCreated )//prevents error on (editor) game exiting play mode
_entityManager.DestroyEntity( entity );
}
Entity CreateMyEntity ()
{
Entity entity = _entityManager.CreateEntity();
_entityManager.AddComponent<IsMirroredTransform>( entity );
_entityManager.AddComponent<LocalToWorld>( entity );
RenderMeshUtility.AddComponents(
entity , _entityManager ,
new RenderMeshDescription( UnityEngine.Rendering.ShadowCastingMode.On , receiveShadows:true , renderingLayerMask:1 ) ,
new RenderMeshArray( new Material[]{ _material } , new Mesh[]{ _mesh } ) ,
MaterialMeshInfo.FromRenderMeshArrayIndices( 0 , 0 )
);
#if UNITY_EDITOR
_entityManager.SetName( entity , $"{gameObject.name} #{gameObject.GetInstanceID()}" );
#endif
return entity;
}
public partial class UpdateSystem : SystemBase
{
EndSimulationEntityCommandBufferSystem _ecbSystem;
protected override void OnCreate ()
{
_ecbSystem = World.GetOrCreateSystemManaged<EndSimulationEntityCommandBufferSystem>();
}
protected override void OnUpdate ()
{
if( !MirrorThisTransformInEcsWorld.Transforms.isCreated || MirrorThisTransformInEcsWorld.Transforms.length==0 )
return;
var entities = MirrorThisTransformInEcsWorld.Entities;
var ecbpw = _ecbSystem.CreateCommandBuffer().AsParallelWriter();
var updateJob = new UpdateJob{
Entities = entities ,
ECBPW = ecbpw ,
};
Dependency = updateJob.Schedule( MirrorThisTransformInEcsWorld.Transforms , Dependency );
_ecbSystem.AddJobHandleForProducer( Dependency );
}
}
[Unity.Burst.BurstCompile]
public struct UpdateJob : IJobParallelForTransform
{
[ReadOnly] public NativeHashMap<int,Entity> Entities;
public EntityCommandBuffer.ParallelWriter ECBPW;
void IJobParallelForTransform.Execute ( int index , TransformAccess transform )
{
Entity entity = Entities[index];
ECBPW.SetComponent( sortKey:index , entity , new LocalToWorld{
Value = transform.localToWorldMatrix
} );
}
}
}
public struct IsMirroredTransform : IComponentData {}
Note that anything involving GameObject
s will never scale to 1000s well. This is what pure ECS/Dots path is for.