I read description of managed components here General-purpose components | Entities | 0.17.0-preview.42
and about hybrid components here DOTS Hybrid Components | Entities | 0.17.0-preview.42
and both of them helps to use old unity components in ECS.
So what is the difference?
Hybrid Components have a GameObject associated with the entity so things like instantiation and destruction transfer to the Companion GameObject. I don’t know what managed components do in those circumstances.
Hybrid components don’t receive monobehavior callbacks like: Awake(), Start(), Update(), OnAnimatorIK etc… (Edit: they do receive some of them) For that you need to use managed components. I recommend using managed components like this: Have one managed component with a gameobject prefab on it (component A) and one managed system state component with the prefab instance on it (component B). When you want to associate an entity with a prefab instance you add component A to it. A system then detects that it does have component A but not B and instantiates the prefab and sets it as the value of a new component B which is added. When you remove component A or destroy the entity, the same system can also detect if it has B but not A and destroy the prefab instance and remove component B. You can also hook this up into a gameobject pooling system.
I’m checking this right now and I’m sorry to say that I don’t think any of that is true ;D I think you’re confusing managed IComponentData with UnityEngine.Component (which technically is a kind of a component, and is a managed type, but not what Djo_krd indicated with his doc links)
Hybrid components
They look like this:
public sealed class HybridTest : MonoBehaviour, IConvertGameObjectToEntity
{
public void OnEnable()
=> Debug.Log($"{nameof(HybridTest)}.{nameof(OnEnable)}");
public void OnDisable()
=> Debug.Log($"{nameof(HybridTest)}.{nameof(OnDisable)}");
public void Awake()
=> Debug.Log($"{nameof(HybridTest)}.{nameof(Awake)}");
public void Start()
=> Debug.Log($"{nameof(HybridTest)}.{nameof(Start)}");
public void Update()
=> Debug.Log($"{nameof(HybridTest)}.{nameof(Update)}");
public void LateUpdate()
=> Debug.Log($"{nameof(HybridTest)}.{nameof(LateUpdate)}");
public void FixedUpdate()
=> Debug.Log($"{nameof(HybridTest)}.{nameof(FixedUpdate)}");
public void OnDestroy()
=> Debug.Log($"{nameof(HybridTest)}.{nameof(OnDestroy)}");
void IConvertGameObjectToEntity.Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
=> conversionSystem.AddHybridComponent(this);
}
When I run this, I indeed only receive a couple of the implemented callbacks (worth noting that LateUpdate is ran once for some reason, and OnEnable/OnDisable are ran out of order or not at all. But Awake/OnDestroy seem pretty reliable for initialization and cleanup).
This behaviour is on purpose (probably? apart from the LateUpdate thing?) and it doesn’t mean that we can’t update our hybrid component. According to the docs:
This implies that it makes more sense to update HybridComponents from a System. We can add back the Update() callback wherever we want like this:
[UpdateInGroup(typeof(SimulationSystemGroup))]
public sealed class HybridTestSystem : SystemBase
{
protected override void OnUpdate()
{
Entities
.ForEach((HybridTest hybridTest) => hybridTest.Update())
.WithoutBurst()
.Run();
}
}
(This is convenient for porting code but if you want to do this in the canonical ECS way, and to get access to other entities/components/ECS goodness, move the implementation of Update() out of the MonoBehaviour to the System directly)
At runtime, when we select the converted entity in the Entity Debugger, a hybrid component is displayed as a reference to the MonoBehaviour on the GameObject that ECS spawned for us.
Managed IComponentData
Looks like this.
[GenerateAuthoringComponent]
public sealed class ManagedComponentTest : IComponentData
{
public float A;
public int B;
public bool C;
}
This seems to be a somewhat more direct way of using managed types with ECS.
-
There is no MonoBehaviour, and we don’t get an additional GameObject/Transform for it. Hence no need to run the costly CompanionGameObjectUpdateTransformSystem, etc.
-
The component is directly “assigned” to the entity similarly to how regular IComponentData are, but it’s a class, not a struct.
-
We can use it in Entities.ForEach in the same way as HybridComponents - needs .WithoutBurst().Run().
-
Exactly as with IComponentData, we can write a GameObjectConversionSystem, or make things easy for ourselves and use [GenerateAuthoringComponent].
-
We can also add/remove these from entities easily at runtime (while HybridComponents rely on the conversion workflow).
The end result consists of an authoring component:
At runtime, when we select the entity in the Entity Debugger, the data is displayed directly:
I may have been wrong about it not receiving any callbacks. Seems like it receives some of them but not all, as you have shown. Update is never called and late update is only called once. Your approach of calling update form a system could work in most circumstances. But for certain things like OnAnimatorIK, I don’t think we can do this. Since OnAnimatorIK is called from some internal unity code (I guess). Remember, the docs also say:
Also I’m not sure how to use a pooling system with hybrid components. Any ideas on that?
Using managed components instead of hybrid components seems to be suggested by Joachim too for the time being. Check this thread out: Need advice for hybrid architecture with ECS
I agree. I actually recommended this approach in this post . It’s probably the easiest way to use GameObject prefabs that depend on a complicated internal hierarchy (such as animated characters, etc), and it should work with pooling really well.
As far as I understand, there’s currently a spectrum of options for moving existing code to ECS.
-
(Least ECS-like) GameObjects manually managed from ECS.
-
Good for character animators, large component hierarchies in existing GameObject prefabs, weird edge cases (like when you rely MonoBehaviour callbacks), old physics engine, etc.
-
Useful when you’re moving an entire prefab to ECS.
-
Code lives mostly in MonoBehaviours and other UnityEngine.Components, and it doesn’t need much refactoring to move to ECS.
-
This is the DIY solution - you spawn stuff and manage its lifetime by yourself, probably using System State Components and Managed IComponentData (more below). Works great with pooling.
-
(Somewhat ECS-like) Hybrid Components(/Component Objects).
-
Gives you a GameObject/Transform that is synced to the Entity.
-
Useful when you’re moving a single MonoBehaviour to ECS.
-
Mostly intended to be used with the conversion workflow.
-
Code lives either in Systems or MonoBehaviours (or both), and probably needs some refactoring.
-
Unity uses Hybrid Components as a stopgap solution for converting lights, volumes, decals, particle systems, visual effects, etc.
-
(Mostly ECS-like) Managed IComponentData.
-
Pretty much a regular ECS component (not UnityEngine.Component!), except it’s a class, which means that it can support all of ye olde Mono C# code, but doesn’t support Burst/Jobs.
-
Useful if you’re moving a piece of code to ECS, but refactoring it to be compatible with Burst is too much work.
-
Your code lives in systems, and probably needs some refactoring to work, but you can at least re-use some of your old code.
-
(Canonical ECS) Struct IComponentData (and other native ECS component types).
-
Fully DOTS, best performance, the future is here.
-
Be ready to rewrite/refactor nearly all of your code, though.
Have I figured this out correctly? This stuff needs more docs. Technically, you can put this information together yourself just by reading the existing docs, but I really think this should be typed out somewhere explicitly. I should start a blog.
I don’t see why you separate GameObjects manually managed from ECS and Managed IComponentData, because in order to manage gameobjects from ecs you need managed IComponentData no?
Edit: nvm