GameObject conversion and SubScene code tour

GameObject conversion and SubScene code tour

This article is outdated for Entities preview.33, it had several class name changes and various additions..

You can just give your GameObject to GameObjectConversionUtility static class and witness the magic. But it's always nice to know what it does inside so you are sure it really suits your need, ready to extend when it isn't, and not caught off guard by some unexpected behaviour or new patch notes.

What is a conversion

We are trying to change any GameObject to Entity successfully. This is difficult as ideally you want something like :

  • Converted Entity has all MonoBehaviour as component object and all proxies as IComponentData like what GameObjectEntity can do. BUT I don't want the link. Whatever happen to the original GameObject should not affect converted Entity. (Or just destroy it, I wanna go pure)
  • If that GameObject is a prefab, I want the converted result to be attached with Prefab (the component type name). Because I want to instantiated them ECS-style.
  • If that GameObject has children, I should get many Entity that somehow connected to each other transform-wise, and also destroy-wise. Also I want to operate on only the top level Entity not having to care about any of its child. They should somehow comes together automatically.
  • Combining 2 points together, a prefab with children should have its converted counterpart instantiate together by just instantiating the top prefab entity. Oh, and all child should have Prefab as well so those do not show up in queries.
  • Maybe the original GameObject should be destroyed too, or sometimes not.
  • My xxx : MonoBehaviour should be 10D fourier transformed/*insert your custom transformation* into yyy : IComponentData
  • As a principle I want to work on my things in edit mode in hierarchical presentation, but in runtime I want them flat and fast (But somehow works together like they are hierarchical) rather than caring about how it looks on the Hierarchy in play mode.
  • Maybe each converted Entity should know something about scenes as well? GameObject are scene being after all! ECS scene?
  • etc.

The conversion ecosystem can address all these problems!

GameObjectEntity

You used to need GameObjectEntity to take Unity things to ECS. You used to paste those proxies (formerly "wrapper") on the GameObject to make them turn into ECS. However GOE is not really conversion. It is linking so it exists on both side at the same time.

The new conversion tool, by not having to worry about maintaining link with regular Unity we can do a full blown heavylifting to make a "really Unity" ECS Entity. Conversion is a one-way operation.

Conversion world & destination world

The general idea of this ecosystem goes in this order :

  • Create a new "conversion world" programmatically.
  • Add some specialized systems to it as workhorses.
  • Desired GameObject are added to conversion world as Entity in a dumb way. This is like putting them on cutting board.  From now when I said "add", it is different from "convert". "Convert" means changing into final product. Adding is not quite there yet.
  • Teach the conversion world more about the real, intended relationship of those dumb Entity what they were when they were GameObject.
  • "Run" the conversion world. All dumb Entity in conversion world are processed and finally converted, sent to destination world. They are now sophisticated and no longer dumb.
  • Conversion world disposed.
  • Optionally you may destroy the original GameObject if you don't need it anymore.

"Conversion world" is full of GameObjectConversionSystem subclasses and one GameObjectConversion*Mapping*System. That one mapping system collects all knowledge to make the final product.

Also this convesion world will be added some Entity to work on. "Dumb Entity" is from this point, a made-up term referring to incomplete Entity converted from GameObject, staying in conversion world. It looks like what you get after attaching GameObjectEntity and just call it a day.

If you are not familiar with GOE, it make 1 Entity with all proxies as real IComponentData and also all Unity MonoBehaviour as "component object". It also maintains sync but we are only talking about conversion ability of GOE not synching ability. This conversion is quite useful, but not quite enough the real conversion we need.

This pipeline is just for your understanding, you will never need to do this yourself. You should let GameObjectConversionUtility static class (the entry point of all) to do it. It knows how to prepare conversion world with your custom conversion system added, then run the whole thing. (explained later)

GameObjectConversionSystem

An abstract class for you to use by inheritance, that would become one of the workhorse in the conversion world.

On this abstract class there is no OnUpdate, you have to make one. You will be doing conversion work in OnUpdate. Additionally you get GameObjectConversionMappingSystem to do your work on your OnUpdate. This mapping system is shared for the entire conversion world.

Then when you "run" the conversion world (that is to update them all) it is like everything (GameObject) got moved pass a processing pipeline into final, converted product (Entity). "Everything" has to be moved into conversion world first, like putting them on cutting board.

How to register your own system as one of the conversion systems

  • By having [WorldSystemFilter(WorldSystemFilterFlags.GameObjectConversion)] on the class, but fortunately this attribute will be inherited from subclassing GameObjectConversionSystem. So just subclassing works.
  • Your system will be put into one of 3 system groups : init, conversion, after conversion. By saying nothing, you get to be in conversion group. By saying [UpdateGroup(typeof(GameObjectAfterConversionGroup))], your system will run in the after conversion group. You cannot add your system to init group.
  • override the OnUpdate, because otherwise your system do nothing when the update group runs.

Unity-made inheritances as of March 2019 :

  • (init group) ConvertGameObjectToEntitySystemDeclarePrefabs : GameObjectConversionSystem
  • (init group) ComponentDataProxyToEntitySystem : GameObjectConversionSystem
  • (conversion group) ConvertGameObjectToEntitySystem : GameObjectConversionSystem
  • (conversion group) TransformConversion/MeshRendererConversion/... : GameObjectConversionSystem

Nothing (yet) are preconfigured to be in the after conversion group, so if yours are in the GameObjectAfterConversionGroup, you can make sure you can do something on top of Unity's built-in conversions.

For now let's not worry how to turn your GameObject into a dumb entity in the conversion world. Imagine that they are already there. There are interface you could add to MonoBehaviour you could prepare to pass on some knowledge to the conversion system. They will tag along with the dumb entities.

IDeclareReferencedPrefabs & ConvertGameObjectToEntitySystemDeclarePrefabs

Added this contract to your MonoBehaviour. It is featuring :

void DeclareReferencedPrefabs(List<GameObject> gameObjects);

What you should do in your implementation is adding GameObject prefab from your exposed field to that list. You can add multiple.

Then, when that GameObject is converted to "dumb entity", it brought along this interface.

The conversion world then will have Unity's built-in  ConvertGameObjectToEntitySystemDeclarePrefabs , a  GameObjectConversionSystem subclass that search for all IDeclareReferencedPrefabs to collect them together on OnUpdate. (Remember that the conversion world "runs" once, so OnUpdate is not really running constantly but just once.) It is one of the 4 Unity-made conversion systems I mentioned.

As I said all GameObjectConversionSystem shares a single GameObjectConversionMappingSystem. All prefabs needed are now usable by all conversion system to make the final non-dumb Entity. All prefabs are collected into "List #2" that I will explain later.

IConvertGameObjectToEntity & ConvertGameObjectToEntitySystem

An another dumb entity's guide to civilization. This interface is featuring

public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)

Again you add this contract to your MonoBehaviour, to describe how to include something more in this MonoBehaviour to its Entity counterpart so it is not as dumb. When the dumb entity is made it would carry over this interface. The conversion system would then ask them all to Convert itself.

The given entity in Convert is in fact the converted product of this GameObject. And dstManager are of the destination. Convert occurs once when the pipeline runs, so you can do something like dstManager.AddComponent without worrying about the duplicate component error. While in Convert, please keep reminding yourself "I am still in the middle of conversion".

conversionSystem is also given just in case you need more power. You could access the "power tool" GameObjectConversionMappingSystem inside that.

The built-in system called ConvertGameObjectToEntitySystem will be added later to your conversion world to work on ALL IConvertGameObjectToEntity in existence. Calling Convert on each. This system is NOT in your default world thanks to the new system flag feature.

GameObjectConversion*Mapping*System

From this point when I say "mapping system" I meant this one. When I say "conversion system" I meant the other subclasses of GameObjectConversionSystem.

An internal ComponentSystem class. It acts as a big brain on the conversion along with providing "power tools" for you to do something in your own code during conversion. You can't make this directly being internal but there are several points you can access the mapping system :

  • In your IConvertGameObjectToEntity 's Convert. Remember that you get GameObjectConversionSystem as the 3rd argument, which contains the mapping system inside as a public field. So use dot notation to get to it.
  • In your any of your own subclass of GameObjectConversionSystem. You get the shared mapping system in its public field.

It also contains several public methods for you to add any/more GameObject as "dumb entities" to conversion world.

The mapping system maintains 3 lists.

List #1 : The GameObject to entities mapping

It is Dictionary<GameObject, List<Entity>>. That is a one-to-many mapping from every GameObject to its potentially many Entity.  Value of this dictionary should be a live, converted entities in the destination world. The reason why it is a List rather than a single Entity because this tooling supports converting one GameObject to many Entity.

It is representing a history of conversion. It is important because we want to post-process the converted Entity a bit more.

The first Entity in that list of any particular GameObject is called the "Primary Entity". All other Entity means that you converted one GameObject to many Entity that looks identical. This list can keep track of them all.

Because GameObjectConversionMappingSystem is shared in a conversion World, you can imagine this Dictionary containing all the knowledge about which GameObject turned into which Entity in the destination world.

Some of GameObject (key) in this list are a prefab, and some are not. It will be important to use with List #2...

List #2 : Referenced prefab

It keeps a List<GameObject>. This time it's a prefab collection. It's purpose is, when the conversion happen, we want the converted ECS side of all GameObject to have Prefab component correctly (if it was a prefab), then we gain ECS's fast "memory instantiation" and never having to touch the original GameObject prefab again.

While the conversion world is running, all List #1 's key (GameObject) will be checked with List #2 to confirm their prefab status. If it is, its (potentially many) Entity will all get Prefab component after the conversion.

So it is important that this List #2 is properly populated for what GameObject you want to be reborn as "instantiable Entity" in ECS side. (That is with Prefab component, but just Prefab is not quite enough as I will explain soon.)

ConvertGameObjectToEntitySystemDeclarePrefabs that you have already learned will look through all dumb entities and collect all prefabs as declared in IDeclareReferencedPrefabs to this List #2 while you are running the conversion world.

List #3 : Linked entity group

A List<GameObject> again despite the name says "entity". Any GameObject added to this list will have a relationship with all their children carry over to its Entity counterpart.

Thanks to the new LinkedEntityGroup built-in IBufferElementData, it makes every Entity in that buffer instantiate and destroys together with the one with LinkedEntityGroup. This mirrors the behaviour of GameObject hierarchy. Ideally that entity should have Prefab attached so it doesn't show up in queries.

The Prefab component does nothing about chaining instantiate, it just hide from query along with remove the Prefab component after instantiate. The new LinkedEntityGroup complement it, so that instantiation chains to related entities and also destroys together. Now instantiation feels very Unity, but in ECS. Prefab and LinkedEntityGroup completes the workflow. That's why I said just Prefab was not quite enough.

You could add that magical buffer component yourself after conversion, but wouldn't it be great if the conversion ecosystem do that for you?

You see where this is going. By registering a GameObject to this LEG list before conversion, when the conversion happen, its Entity will get LinkedEntityGroup setup nicely along with the buffer pre-populated with its related Entity. (Which they comes from asking list #1 !) List #2 will add Prefab to all of them which works together with List #1. 3 lists together, get you a "very Unity" converted Entity.

At last we can see a glimpse of future of Unity. Where in edit mode is kinda GameObject but in play mode suddenly they are all turned into Entity while retaining all GameObject 's strength.

Public methods on the mapping system

These are 3 helper tools you can use during conversion :

  • public Entity GetPrimaryEntity(GameObject go) : Get the 1st Entity from the list #1. You don't need to worry if the conversion had happened or not, because if the conversion didn't happen yet, you get the 1st conversion right now and also get back the Entity result because that's now your primary entity. So this "get" is actually a "create" built-in.

    This "create" is a dry create. Like it is really just an empty Entity! However the point is to get you that Entity reference to the final result while still being in the middle of conversion pipeline.

    In the pipeline, there are several Unity built-in system that ensure all your GameObject got "primary entitied" at least once.
  • public Entity CreateAdditionalEntity(GameObject go) : Add more dry entity from the same GameObject by doing the same "create" routine as GetPrimaryEntity, but this is repeatable. Errors when you haven't GetPrimaryEntity to ensure at least one converted Entity first.
  • public IEnumerable<Entity> GetEntities(GameObject go) : Get all converted Entity so far for any GameObject. That is the whole List per one GameObject.

Primary Entity revisited

During conversion, if the conversion world knows enough we can now make use of primary entity in a meaningful way in our own conversion. (Make use = somewhere in IConvertGameObjectToEntity 's Convert or our subclass of GameObjectConversionSystem's OnUpdate )

I mentioned that one of the key in List #1 can be a prefab. For example, if you use that as a key to ask its primary entity (index 0 of that list) you get the correct converted Entity of that original prefab, that is ready to be instantiate by ECS.

If you are looking to instantiate the converted Entity right away by someone, here's your chance to grab that converted Entity during conversion and assign as an Entity field to that someone. You can do this early even before the conversion is finished because you are given that final Entity reference which will be completed down the line.

Remember 2 points where we could use the mapping system? One of them is the IConvertGameObjectToEntity 's Convert, it is already later than IDeclareReferencedPrefabs collecting by ConvertGameObjectToEntitySystemDeclarePrefabs by the pipeline's fixed ordering (more on this later).

So you are ready to use that mentioned trick in the Convert method scope. Convert gives you the mapping system as the 3rd argument. You got your original prefab GameObject somewhere here also. That conversion system already known about prefab declaration. Finally, you got everything to do GetPrimaryEntity with the prefab GameObject.

See this official example which pass on the prefab conversion result to ECS. Without this you will have trouble looking for the converted Prefab + LinkEntityGroup Entity of a particular original prefab GameObject. Now you get the correct one thanks to the mapping system. (Now the word "mapping" make sense right?)

https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/Samples/Assets/HelloECS/HelloCube_06_SpawnFromEntity/HelloSpawnerProxy.cs

But there are more than those 3 public methods. This 2 "add" public method are different that it adds any GameObject to conversion world as a dumb entity. But they are added with care, it recurses and do things you are expecting :

  • public void AddReferencedPrefabAsGroup(GameObject prefab) : Keyword is "referenced". Manually do the thing you could have done at IDeclareReferencedPrefabs, just in case you don't have a chance to bring those GameObject with the interface to conversion world.

    Add that prefab to List #3, then itself and all its children to List #2. By "add", the conversion do not happen unlike with GetPrimaryEntity, but rather the add is still in conversion world. This is the gateway to populate conversion world so that all the conversion systems get something to work on in the first place.

    The "add" is by GameObjectEntity.AddToEntityManager, a static method available from long ago that make a dumb entity with all MonoBehaviour to component object + all proxies to IComponentData. "ToEntityManager" 's entity manager is now referring to conversion world's entity manager not the destination world ones.

    Duplicate add of the same prefab is prevented by checking with List #2 and no addition will occur, as it does not make sense to have duplicate "blueprint entity" generated in the destination world, who is intended to be the "duplicator" itself later. Why do you want multiple prefab entities? You usually want multiple normal entities.
  • public void AddGameObjectOrPrefabAsGroup(GameObject prefab) : Similar but this time it works for non-prefab too. It is added to List #3 to indicate that you explicitly want the converted Entity side to be grouped with LinkedEntityGroup even if it is not a prefab. If it is a prefab, itself and all its children will be registered to List #2 to remember they are all prefabs. Again, the conversion didn't occur yet and still in conversion world. And again duplicate prefab add is prevented.

This 2 will be called automatically at some point but you could be doing it manually too. That makes it 5 public methods of the mapping system. Next, we will finally use everything we learned so let's take a small break with this divider.


GameObjectConversionUtility

metal pipe between trees at daytime
Photo by Quinten de Graaf / Unsplash

static utility that accepts any GameObject you throw at it. Finally we can apply all knowledge so far to understand this power tool!

Conversion world revisited, how it REALLY works in steps

I said that a conversion world is full of GameObjectConversionSystem subclasses and one GameObjectConversionMappingSystem. From now on when I say "create a new conversion world" these systems are prepared first together as a group.

1st group : GameObjectConversionInitializationGroup

  • ConvertGameObjectToEntitySystemDeclarePrefabs added. Explained, works with IDeclareReferencedPrefabs on the dumb entities brought into conversion world to collect prefabs together to one shared mapping system.
  • ComponentDataProxyToEntitySystem added. Not explained yet but I will explain now real quick.

ComponentDataProxyToEntitySystem

Remember that the conversion world is likely populated with either AddReferencedPrefabAsGroup or AddGameObjectOrPrefabAsGroup of the mapping system. Those "add" are dumb with components kinda copied from the original GameObject in the same style that GOE did.

On the other hand when something like GetPrimaryEntity is getting called, the destination world is getting populated with the real, wanted Entity result. However, those result are dry entity as in just an empty Entity. We need someone to make the result looks better.

This ComponentDataProxyToEntitySystem which runs in the pipeline will make those dry entities in the destination world not dry anymore with GameObjectEntity.CopyAllComponentsToEntity, an another GOE static tool besides GameObjectEntity.AddToEntityManager. The copy source are the dumb entities in conversion world. The "to entity" target is the conversion result in destination world (the currently dry entity) known thanks to List #1, and thanks to GetPrimaryEntity.

Also because this system is doing GetPrimaryEntity on all dumb entities, it is the first one that ensure the final Entity in the destination world are there because GetPrimaryEntity has built-in dry create.

Those two are in the same group, updates first on conversion world "run". You can imagine that at this point if dumb entities are properly placed in the conversion world, you get (an unfinished) Entity result in the destination world already.

Then we have the 2nd group following it to finish them up.

2nd group : GameObjectConversionGroup

  • All other subclasses of GameObjectConversionSystem that's not those 2 added. It finds all built-in and also your custom subclasses thanks to [WorldSystemFilter(WorldSystemFilterFlags.GameObjectConversion)] flag inherited to your class. This is assuming you didn't put in any UpdateInGroup.

    Currently in addition to your own you will get Unity's :
  • ConvertGameObjectToEntitySystem : GameObjectConversionSystem , this is essentailly a IConvertGameObjectToEntity 's Convert runner. Because this is in later group, you have all the knowledge of "declared" prefab too. Of course you could "declare" more because you got the mapping system here, and you have that AddReferencedPrefabAsGroup method. This let you modify the destination world's entity with something more. More than what GOE scan-and-migrate static methods can do for you.
    Maybe you don't have to make any your own custom GameObjectConversionSystem subclass! Just put a custom logic per-GO type in Convert, then let this one system run them all.
  • TransformConversion : GameObjectConversionSystem put necessary transform related components so they are linked together transform-wise (One of the good stuff is Parent which ensure things go together in hierarchy like in normal Unity). This make it as "Unity" as it can get for an Entity.
  • Some conversion system from the other packages?

Finally when all steps are done (2 groups had passed)...

3rd group : GameObjectAfterConversionGroup

If you use UpdateInGroup on your conversion system you could instead put them in this conversion pass. I think the point is so you can process what Unity-made conversion system did one more time. Or maybe just categorize your own conversion into 2 passes.

I believe you could not put any other group on conversion system than GameObjectConversionGroup and GameObjectAfterConversionGroup. (You cannot force your systems in the init group!)

Imagine you want the transform Parent chain to change material together the same way. You can put your MaterialLinkingConversionSystem in this group and expect to find all Entity got the Parent linked up already.

Prefab and LinkEntityGroup finalizing step

The really last step is it will add Prefab and LinkEntityGroup correctly to the finished product in the destination world.

This is possible by List #1 #2 #3 in that single mapping system working together, having know everything possible from all interface prefab declaration, you custom declaration by mapping system's public method, etc.

At this point you better make sure nothing you want the convertor to do is unknown or you may get a "disconnected" converted Entity. Remember that at this step all your custom Convert are executed. Also all your custom GameObjectConversionSystem are executed. Use those chance to finish remaining missing links by telling the mapping system something.

With that explained, let's go through all static methods of this utility.

public static Entity ConvertGameObjectHierarchy(GameObject root, World dstEntityWorld)

Single conversion.  You prepare your own world that is to receive converted entities. Also give you back the reference to the result's primary entity.

Create a new "conversion world", then add that single root GameObject  to the conversion world with AddGameObjectOrPrefabAsGroup of the mapping system. It would then recurse add everything down the line.

Prefab or not, all its children are now eligible to be in LinkedEntityGroup's buffer of the top level Entity.

Run the conversion world, everything happen as explained then dispose the conversion world. Done!

public static void ConvertScene(Scene scene, Hash128 sceneHash, World dstEntityWorld, bool addEntityGUID = false)

This one looks OP. All GameObject at root level in the scene will be recursive converted down to their last child in the same way as ConvertGameObjectHierarchy.

But wait, there's something more about sceneHash and addEntityGUID. I mentioned at first, ideally the converted Entity should have some sophisication about the scene. After all, its original form was GameObject who is a scene being. If you use ConvertScene you are eligible to receive these kind of data tagging along with your converted Entity!

Sub scene

There's a catch, those scenes that entities can belong to must be a SubScene. The converted entity can belong to one SubScene if you use ConvertScene on a Scene containing SubScene. Entity cannot belongs to that classic, top-level Unity "scene".

Sub scene has that "ECS" smell. First of all it is just a GameObject inside the scene with SubScene component. This make the conversion tooling able to recurse the same way. See this, it looks like an another scene nested inside but looking at the Inspector RotatingCube is really just a GameObject when I click on it.

Sub scene entity : allows scene streaming

By the conversion pipeline running through this sub scene game object, this means you will get that one bonus  Entity that represent this GameObject with SubScene : MonoBehaviour too! It is an Entity with component object SubScene. There is no dedicated IComponentData version of SubScene.

However this is not just a junk. Meet the public struct RequestSceneLoaded : IComponentData.

That bonus Entity converted from GO with SubScene:MonoBehaviour will have this added based on Auto Load Scene check box. If not checked you can add it later yourself. There is a system called SubSceneStreamingSystem : ComponentSystem, that looks for RequestSceneLoad and load the scene. The request seems to be removed after the load.

The subscene entity will also have SceneData : IComponentData remembering various metadata.

Scene hash

How an Entity belongs to any scene is by remembering the scene hash. Because originally Unity differentiate scene by its string name we need something compatible with ECS. So we are using Hash128 instead.

SubScene component contains public GUID SceneGUID property. The GUID data type is usable in place of Hash128.

With sceneHash as something not default inputted into this magical ConvertScene method you will pleasantly notice some more scene related components popped up on the ECS side. If you do not provide the scene hash, you are not getting any scene features in the converted Entity. It just convert the entire scene.

Scene section

This new feature allows an entity to not only belongs to a (sub) scene, but as well belongs to an integer section inside that.

To make scene section happen, we need to know about these :

  • public class SceneSectionComponent : MonoBehaviour - Holding an integer. You could further divide the sub scene in to sections by adding this component to GameObject to be converted, that is a parent of something more. After GameObject conversion, all the Entity converted children of that GameObject now belongs to that integer section marked by SCD SceneSection. If there is an another section nested down the hierarchy, each child get its nearest section recurse upwards. (The same fasion as asmdef folder if you had used it) I guess it does not do anything if outside of a sub scene.
  • public class SceneSectionProxy : SharedComponentDataProxy - Seems to be for use instead of SceneSectionComponent if you don't want to do conversion, but want to do hybrid style linking and wanted a scene section at the same time.

There's one more thing you can add along with SceneSectionComponent to the GameObject to be converted :

  • public class StaticOptimizeEntity : MonoBehaviour - Add it the same style as SceneSectionComponent but this time it make all its child get Static component after the conversion. (you could say all its children are now optimized?) Static entities update once to compute transforms then add Frozen, which make it receive no more transform updates.

And finally, meets the public struct SceneSection : ISharedComponentData.

The component tags integer section in the converted Entity. Maybe you can plan to do something fun to everything in the same section later.

However this is not only remembering the integer section but also sub scene's scene hash as well. Ensuring that you could use the same integer in a different sub scene and not conflicting when you query the SCD.

This also doubles as to say which entity belongs to which sub scene even if it doesn't belong to any scene section. If you provide scene hash in ConvertScene, but as it recurse it found an object under subscene but not in any scene section parent it will still get SceneSection added but with section integer = 0. Don't name your section 0!

Also everyone will get public struct SceneTag : ISharedComponentData along with SceneSection, it can be used to get back to the "sub scene entity".


ConvertToEntity : MonoBehaviour

With this you don't even need to find somewhere in the code to call any of  GameObjectConversionUtility 's static method, because it will throw itself to the utility on Awake. Has an option to delete the original object too!

Modes

  • ConvertAndDestroy : Convert the entire hierarchy and destroy itself.
  • ConvertAndInjectGameObject : Do things like GameObjectEntity. Convert just itself (not drilling down) and then add all mono component of itself to that new entity. (Except GameObjectEntity, ConvertToEntity, and proxies since those will add its counterpart on their own.)

But that's not all, this class is not just MonoBehaviour but comes with several public static methods..

  • public static void ConvertHierarchy(GameObject root) : Exactly what it did at Awake with ConvertAndDestroy mode but manually.

    VS GameObjectConversionUtility.ConvertGameObjectHierarchy(GameObject root, World dstEntityWorld) ?

    This one uses World.Active as the destination. Also this one returns void where the utility ones returns Entity. This one also destroy the original. And lastly this one do not handle the case where root is a prefab. (You get a normal product, not Prefab component attached product)
  • public static void ConvertAndInjectOriginal(GameObject root) : Exactly what it did at Awake with ConvertAndInjectGameObject mode but manually.

You can imagine using those static method multiple times with disabled GameObject (I think that's the use case?)

  • public static void AddRecurse(EntityManager manager, Transform transform) : Add recurse is a subset of ConvertHierarchy. It just make a tons of Entity without putting them into conversion pipeline for the whole hierarchy. (So you get just the GameObjectEntity style behaviour, not any other transform linking, etc.)
  • public static bool InjectOriginalComponents(World srcGameObjectWorld, EntityManager simulationWorld, Transform transform) : This one I have no good idea how to use it!

    The internal use while in the conversion pipeline is that srcGameObjectWorld is the temporary conversion world (Which must contain GameObjectConversionMappingSystem with all the history of conversion). So it could look for converted Entity by giving it transform.gameObject, then after found one, migrate all component objects from that game object to that entity. Well, you know you have already got that kind of result from the conversion so it must be that this is already used internally.

    However by using various conversion opened to public, you are never given the conversion world at any moment. It is created and destroyed before you know it. So I don't know how this public static method could be used to great effect? Also note that GameObjectConversionMappingSystem needed to be in srcGameObjectWorld to work is also an internal class. Hmm 🤔

Other built-in conversion systems added to the conversion world

TransformConversion

It adds LocalToWorld, Translation, Rotation, NonUniformScale to all. There are optimization paths, if it has Static then only LTW will be added. If it has exactly 1, 1, 1 GO scale NonUniformScale is not added. It is usually the case, so this optimization is welcomed. (And uh, go clean up those (0.9998, 0.9998, 0.9998) scales on your objects to take advantage of this) But remember you cannot scale later without adding NonUniformScale by yourself first.

MeshRendererConversion

MeshRenderer and MeshFilter, two bread and butter classic mono component which make you see something since forever. They can be now converted to RenderMesh SCD component. Finally you can comfortably see something tangible in edit mode and that become an efficient entity-based rendering in play mode.

LODGroupConversion

I didn't mention this but it is hidden in hybrid rendering package.. it converts the LODGroup MonoBehaviour component to MeshLODGroupComponent, the ECS version.

HLODGroupConversion

This HLOD mono component comes with the hybrid rendering package. This time it converts to MeshLODComponent. The GO with HLOD should also use LODGroup.