Search Unity

Question What is the proper way of instantiating an entity prefab with child physics bodies?

Discussion in 'Physics for ECS' started by davenirline, Feb 22, 2023.

  1. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    987
    I have an ECS converted prefab that has children with physics bodies. I know that these entities would be unparented. So when the prefab is instantiated at a certain position, the child bodies don't really follow the prefab's root position. So I had to make a system where I forcefully update the position of these children relative to their parent upon instantiation. This feels "awkward". Is there a proper way of doing this? Maybe there's something I don't know. Maybe I don't need to do this system.
     
    RaveBox likes this.
  2. Mockarutan

    Mockarutan

    Joined:
    May 22, 2011
    Posts:
    159
    I too would love to know if there are better ways, I have not found a good solution for it. My solution sounds like yours and I think it's more than awkward, it's quite hacky and fragile for refactors (know this from several personal experiences). I think instantiating an entity prefab with any physics in runtime like that should be handled though a different process than the one used now. The current one is fitting for the subscenes and the baking paradigm, but not at all for runtime instantiation.
     
    Last edited: Mar 3, 2023
    davenirline likes this.
  3. UniqueCode

    UniqueCode

    Joined:
    Oct 20, 2015
    Posts:
    65
    I guess the "correct" way to do this is joints. Afaik the same happens in GameObject world, a child object with a Rigidbody will behaviour as if there was no parent. See the physics samples on how to use joints.
     
  4. yifanchu183

    yifanchu183

    Joined:
    Jul 4, 2019
    Posts:
    41
    I change the child's localTransform in prefab before instantiate
     
  5. daniel-holz

    daniel-holz

    Unity Technologies

    Joined:
    Sep 17, 2021
    Posts:
    278
    @davenirline : If you want the "child bodies" to simply rigidly follow the root, why don't you just use only colliders in the child game objects?
    You could have one root rigid body and a sub-tree with colliders. That would keep them all linked relative to the rigid body as desired and also optimize the whole thing automatically for you by putting all the child colliders into one compound collider under the hood when baking.
     
  6. TheOtherMonarch

    TheOtherMonarch

    Joined:
    Jul 28, 2012
    Posts:
    866
    I don't know what the original poster envisioned. But the way compound colliders handle layers is different than PhysX. Most likely a compound collider did not work as he was expecting. Many people have had issues with this for a while now.
     
  7. daniel-holz

    daniel-holz

    Unity Technologies

    Joined:
    Sep 17, 2021
    Posts:
    278
    I see. I'd like to know more about the specific issue encountered and the setup here to get a better understanding of this case. Hopefully @davenirline can provide some more information.
     
  8. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    987
    The use case is to fake destruction. When an object is shot upon or collided with something, we hide that object and instantiate its prefab equivalent at that position. This prefab represents the object divided into multiple parts with each part having a rigid body. So these parts would be simulated by physics such that they would look like debris flying about. However, the way these prefabs are authored is they have an empty root and the children are the parts. Each part has a PhysicsBody and a Shape. When the prefab is instantiated at the position, the children are unparented and they use their local position as the world position, not the position of their parent. Thus the pseudohack that I have to forcefully set the position of this children parts relative to the parent root.
     
  9. cf-ie

    cf-ie

    Joined:
    Feb 2, 2022
    Posts:
    13
    Chiming in as I'm also figuring this out. I'm finding possible solutions very awkward and anti-ECS.

    The best solution I've come up with so far is to loop through linked entities, check if the entity has a physics component, and then setting the relative transform of the entity.

    It feels like there should be some built-in optimal solution for this.
     
    spinaljack and davenirline like this.
  10. MidnightCow

    MidnightCow

    Joined:
    Jun 2, 2017
    Posts:
    30
    @daniel-holz just for clarity i think what people are asking is this:

    Say you have a prefab that's just a parent gameobject with a bunch of children, and the children are for example a stack of boxes..

    So you spawn the prefab something like this:


    var spawnedPrefab = ecb.Instantiate(StackOfBoxesPrefabEntity);
    ecb.SetComponent(spawnedPrefab, LocalTransform.FromPositionRotation(spawnPos, spawnRot));


    What you get is an empty entity (ie the prefab root entity), at the correct position `spawnPos`, and a stack of boxes at whatever position they were at inside the prefab - ie they don't instantiate to a position relative to the spawnPos like you'd expect..

    So you then have to write an additional system that goes through these children, passing in that original spawn pos/rot and repositions them to where they should be.. If i recall correctly when they're physics objects this additional system also has to run after physics has inited the bodies etc which is kindof an additional caveat that might cause problems or at least be kind of awkward.

    It's even more awkward if the children are constrained to the parent because the parent spawns at the correct spawn point, and the constrained children spawn at their root positions, and then visibly 'snap' to their parent unless you write specific code to reposition them before this happens..

    Not sure what the ideal solution would be in terms of the Unity Physics api but i think ideally the physics system would initialize the prefab children at their local transform values + the parent spawn transform.. I think in the case of constrained objects ( going from memory here and this might have changed ) setting the childrens LocalTransform's before physics initializes the objects doesn't work although i could be wrong about that..
     
    layker90524, daniel-holz and cf-ie like this.
  11. Tigrian

    Tigrian

    Joined:
    Mar 21, 2021
    Posts:
    124
    By reading through this thread, I just realized what is the problem I've tried to answer in this one : Question - Children with colliders being de-coupled from parent - Unity Forum.

    So, I asked myself why did not I encounter such problems, having myself worked with my fake (pre-fractured) destruction plugin, as one of the people of this thread did. The solution I came up with is in my sense a little bit more ecs and less awkward than setting the children positions in the system after the instatiation, but do sacrifice a bit of the ease of hybrid authoring and the baking performance benefit. It won't work for all type of configuration, I suppose (and I would also of course prefer this not be an issue at all, and work out of the box, but hey, it does not).

    The idea is simple, instead of having one prefab with a set of children, you are going to instantiate children prefabs (that can be actual prefabs, that get converted, but I prefer to make the prefab entity manually in the baker), and set their components at runtime (including the transform).

    Of course, having a lot of different children types, with all some very specific components just defeats this method, but in the case of fake destruction, with children being all similar with just physics, rendering and transforms components, this is perfect!

    So, the hassle with this technique is to retrieve each component's values.

    For all the unmanaged IComponent data, with different values per children (otherwise set the value in the child prefab), store them in a BlobArray.

    For the rendering, I just use the render mesh array component, and store the meshes and materials indexes in a blob array :
    Code (CSharp):
    1. Ecb.SetComponent(sortKey, mySpawnedEntity, MaterialMeshInfo.FromRenderMeshArrayIndices(dataBlobValue.BlobArray[index].MaterialIndex,dataBlobValue.BlobArray[index].MeshIndex));
    And finally, the interesting parts are colliders. And there, I just do what I recommended in the other post, I make a huge compound of all my children colliders, and used this unsafe method to get the collider out of the compound :
    Code (CSharp):
    1. public static BlobAssetReference<Collider> GetChildOfCompoundFromIndex(BlobAssetReference<Collider> compoundValue, int index)
    2. {
    3.     var compoundPtr = (CompoundCollider*)compoundValue.GetUnsafePtr();
    4.     var key = compoundPtr->ConvertChildIndexToColliderKey(index);
    5.     compoundValue.Value.GetChild(ref key, out var child);
    6.     return child.Collider->Clone();
    7. }
    The cool part about that is that I can now set the physic mass very easily, because I soon as I get the collider I get all the data I needed (the mass value has been stored in a blob array, it was the massProperties that were hard to get without the collider) :
    Code (CSharp):
    1. var collider = UnsafeBlastUtility.GetChildOfCompoundFromIndex(colliderBlobValue, index);
    2.  
    3. Ecb.SetComponent(sortKey,leafEntity, new PhysicsCollider()
    4. {
    5.     Value = collider
    6. });
    7. var aabb = UnsafeBlastUtility.GetAabbOfCompoundChildFromIndex(colliderBlobValue, index);
    8. if (isDynamic)
    9. {
    10.     Ecb.SetComponent(sortKey,leafEntity,
    11.         PhysicsMass.CreateDynamic(collider.Value.MassProperties, dataBlobValue.BlobArray[index].Mass));  
    12. }
    13. Ecb.SetComponent(sortKey,leafEntity, new RenderBounds()
    14. {
    15.     Value = new AABB()
    16.     {
    17.         Center = aabb.Center,
    18.         Extents = aabb.Extents
    19.     }
    20. });
    So, this method might not be a perfect workaround for this problem, but if it can help for at least the case of fake destruction of davenirline, I'll be glad.
     
    daniel-holz likes this.
  12. Blargenflargle

    Blargenflargle

    Joined:
    Feb 24, 2019
    Posts:
    92
    Hello. I'm also dealing with a similar (the same?) problem. Based on this message I'm not sure we're really on the same page here as to what the problem is, so I will explain steps to reproduce as well as why it's a problem.

    • Create a Unity 2022.3.5f1 project
    • Add a gameobject to the scene (We'll call it "test")
    • Add a 3D Object > Cube child to the gameobject. Make sure the child is at (0,0,0) relative to the test gameobject
    • Turn the test gameobject into a prefab
    • Add a subscene for conversion
    • Create a component/system to reference the prefab/instantiate it at runtime
    • Change the localtransform of the instantiated prefab to any arbitrary coordinate, say (5,5,5)
    You will notice the cube is at (0,0,0) despite the test instance being at (5,5,5). This is because the cube is no longer a child of test after baking. This is a problem for me because I wanted to create little floating islands that I would dynamically spawn in as the player moves through the world. Naturally, these islands need to have colliders for the player to stand on them. Currently my islands have a top level gameobject that acts as a container for all of the rocks that make up the islands, similar to the test example above, where the island is the empty "test" gameobject and the cube is a rock. So when I instantiate a prefab of an island, then move it to (5,5,5) or any other arbitrary coordinate, the actual island moves but the rocks all stay around (0,0,0). This is basically unusable so please tell me I'm doing something wrong. Based on your reply I tried adding a rigidbody to my top level "island" gameobject but that didn't do anything. Thanks.
     
  13. Blargenflargle

    Blargenflargle

    Joined:
    Feb 24, 2019
    Posts:
    92
  14. Tigrian

    Tigrian

    Joined:
    Mar 21, 2021
    Posts:
    124
    Glad you found it out, Blargenflargle!

    About the custom solution I provide here, in my previous post, the more I think about it, and the less I see it as practical if you exclude the case of pre-fractured destruction. It is just very specific to a destruction plugin, where every child is a fragment that has a physic body, and I can not see another use case where you need a prefab with children physics body that should be free of movement upon instantiation.

    So for future readers, if you are not making fake destruction, but you still encounter the problem of having some unparented physics bodies that does not move upon instantiation of prefab, it is probably easier to just do the trick of going through the prefab linked entity group and check if entities have physics body to move them after instantiation.

    It is hacky, but should be relatively rare, as I don't see many use cases (beside as I said pre fractured destruction, that can use my method) for a physic body (not constrained by any joints) to be child of an empty root prefab.
     
  15. daniel-holz

    daniel-holz

    Unity Technologies

    Joined:
    Sep 17, 2021
    Posts:
    278
    @Blargenflargle: Sorry for the delay in answering, and thanks for providing more details on the issue. Very helpful!
    I will try to summarize where we are at here and provide a fairly simple solution that could work for you.

    The issue here is that, as was pointed out above and as is currently part of the design in the Unity Physics engine, all rigid body entities get "de-hierarchized", that is, disconnected from their parents. That includes, specifically, the root game object in a prefab containing child rigid bodies. The consequence of this cause that when moving an instance of a baked (and "de-hierarchized", as explained above) entity prefab, the child rigid bodies will not follow as the hierarchy that was present in "game object world" is not represented in "entity world".

    An easy way to get around this is as follows:
    1. Make sure the root game object in your prefab is at 0,0,0 translation and rotation (identity transformation)
    2. At runtime, after instantiating the baked entity prefab into an entity prefab instance, use the root entity's LinkedEntityGroup in order to obtain all the entities contained in the entity prefab instance. Note that the root entity of a prefab always contains a LinkedEntityGroup which contains all the entities that are present in the entire entity prefab hierarchy, including the root itself.
    3. Knowing that the root entity's transformation is identity after instantiation (see point 1 above), in order to move itself and all the de-hierarchized child rigid body entities to the desired target transformation, simply multiply the desired target transformation matrix the already present transformation in the
      LocalTransform
      component for all the entities in the LinkedEntityGroup.
    Here is an example of how to access said buffer and iterate over all contained entities.

    Note that if you have a mix of "dehierarchized" rigid body entities and actual child entities within the prefab, you need to filter out the child entities when applying the transformation to the entries in the LinkedEntityBuffer, since these child entities will be moved through the transformation change of their parents.
    To detect child entities, you can look for presence of the
    Parent
    component. The entities with such a component are modelled as children in the prefab hierarchy (not "dehierarchized") and can be safely excluded from the transformation in point 3 above.

    Let me know if that works in your case.
     
    Last edited: Aug 8, 2023
    davenirline likes this.