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.
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.
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.
I change the child’s localTransform in prefab before instantiate
@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.
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.
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.
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.
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.
@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…
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 :
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 :
public static BlobAssetReference<Collider> GetChildOfCompoundFromIndex(BlobAssetReference<Collider> compoundValue, int index)
{
var compoundPtr = (CompoundCollider*)compoundValue.GetUnsafePtr();
var key = compoundPtr->ConvertChildIndexToColliderKey(index);
compoundValue.Value.GetChild(ref key, out var child);
return child.Collider->Clone();
}
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) :
var collider = UnsafeBlastUtility.GetChildOfCompoundFromIndex(colliderBlobValue, index);
Ecb.SetComponent(sortKey,leafEntity, new PhysicsCollider()
{
Value = collider
});
var aabb = UnsafeBlastUtility.GetAabbOfCompoundChildFromIndex(colliderBlobValue, index);
if (isDynamic)
{
Ecb.SetComponent(sortKey,leafEntity,
PhysicsMass.CreateDynamic(collider.Value.MassProperties, dataBlobValue.BlobArray[index].Mass));
}
Ecb.SetComponent(sortKey,leafEntity, new RenderBounds()
{
Value = new AABB()
{
Center = aabb.Center,
Extents = aabb.Extents
}
});
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.
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.
For the sake of thread continuity, [mention|MurxXh8mXKl4SHK4KHI1FQ==] and [mention|x5SBy4nH3LX5v3ARkrl+Ug==] solved my question in this thread.
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.
@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:
-
Make sure the root game object in your prefab is at 0,0,0 translation and rotation (identity transformation)
-
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.
-
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.
If you are using Joints, you may also have horrible behavior on instantiation (parts flying away at insane speeds or just disappearing) when following the methods above.
That’s because updating only LocalTransform to match the parent location will not update LocalToWorld automatically, which will be incorrect until LocalToWorldSystem runs, or you update it manually.
So you can either:
- Instantiate your prefabs in between the FixedStepSimulationSystemGroup and the TransformSystemGroup.
- Update LocalToWorld manually too based on the new entity local transform.
If the rigid bodies are dynamic, they don’t use LocalToWorld in the engine.
This is only the case for static rigid bodies that don’t have a LocalTransform component.
Maybe you have explosions because your joints are with a “none” body in which case the joint attachment information for that body are considered to be in world space.
For use in prefabs I suggest to connect with static or kinematic bodies instead.
I have a prefab for a wind turbine, with multiple physics bodies inside, some are static, like the concrete base and big pole, and others dynamic (engine box, rotor center and blades). The blades have a joint to the rotor center, the rotor center to the engine box, and the engine box has a joint to the pole.
When I instantiate the prefab on runtime (it’s a procedurally generated world), all the pieces have LocalTransform and LocalToWorld.
I read last week somewhere in the documentation that LocalTransform and LocalToWorld are added to all the entities in a prefab automatically, even if they don’t need them according to TransformUsageFlags.
Should I be removing LocalTransform or LocalToWorld after instantiation or something?
When the entity is considered dynamic (see Baker.GetEntity(TransformUsageFlags.Dynamic)
) it will have both a LocalTransform
and a LocalToWorld
component.
This is also the case for renderable child entities of dynamic entities. These will also receive a LocalTransform
and a LocalToWorld
component.
You don’t need to remove these components, but unfortunately all the dynamic or kinematic rigid bodies contained in the instantiated prefab will have to be moved individually to the correct target transformation after prefab instantiation. You can achieve this by moving the root entity of the prefab instance by modifying its LocalTransform
, and then moving all its dynamic or kinematic child rigid bodies (you can access them via the LinkedEntityGroup
buffer located on the root entity; note though that the first entry in the buffer is the root entity itself) according to the LocalTransform
you applied to the root entity.
Simply obtain the LocalTransform
childTM of each child and apply the LocalTransform
you applied to the prefab instance (say deltaRootTM) to the child transform using childTM.TransformTransform(deltaRootTM)
.
You don’t need to manually update the LocalToWorld
component. This will be done automatically by the transformation system.
If the root entity’s LocalTransform is identity during baking (and therefore also after prefab instantiation), you can simply set the target transformation of the root entity to deltaRootTM to shift it. If it’s not identity, you will need to shift it just like its children using the LocalTransform.TransformTransform()
function mentioned above.
@daniel-holz ok, that’s what I am doing. But for some reason, running the exact same prefab instantiation code in BeginInitializationSystemGroup causes a mess, and doing it in between FixedStepSimulationSystemGroup and the TransformSystemGroup works just fine. I thought it was because the LocalToWorld wasn’t updated yet, but it seems like when they don’t have a parent they should just be using the LocalTransform component directly for physics.
I will try to double check again moving it to BeginInitializationSystemGroup and see what’s going on there and what components are assigned to each entity.
The joints are added as a child of the rigid body, so maybe that joint entity has a parent set and is causing LocalToWorld issues?