I have a compound entity whose collider is composed of colliders from entities A, B, and C merged together. After merging, I removed the PhysicsWorldIndex from EntityA, B, and C. I want to continue applying the PhysicsVelocity.Linear of EntityA or B or C to the compound entity.
When you say compound entity, what do you mean by this?
Note that dynamic rigid body entities in a hierarchy are always de-hierarchized (their parent/child relations split) and each rigid body has a LocalTransform component defining its world transformation and a PhysicsVelocity component defining its linear and angular velocity in world space.
Note also that during baking collider authoring components within hierarchies of game objects are combined into a CompoundCollider blob asset assigned to the PhysicsCollider component of the nearest ancestor rigid body entity in the hierarchy on the path to the root.
Any collider within a child game object will therefore always follow the ancestor rigid body.
So, given all that, what is it exactly that you are doing and that you want to achieve?
During runtime in my game, there’s a weld tool that merges selected entities into a compound entity (creating a new Physics Collider using CompoundCollider.Create), and the PhysicsWorldIndex of the specified entities is removed. To ensure that the behavior of these entities follows the movement of the compound entity, I set the parent of the entities to be the compound entity.
As a result, child entities are no longer involved in physics simulation. However, I still want certain child entities’ physical properties, such as PhysicsVelocity, to continue affecting the compound entity.
some case :
child a is a thruster, it’s PhysicsVelocity.Linear = Forward * force
Physically speaking, you are creating a single rigid body.
So your compound body can only have one linear and one angular velocity (that is one PhysicsVelocity), since that single body moves along one single trajectory at all times, as per the definition of a rigid body.
You need to define more what the physical effect is you are trying to achieve here.
How should the child entities still affect the entire compound body?
My understanding based on the details provided above is this scenario:
If, for example, you want the child to act like a thruster which applies an impulse (or force) at its center with the expected effect on the compound body, you can simply apply an impulse to the parent rigid body entity (the one that holds the PhysicsVelocity component) at the position of the child entity.
This can be done using the PhysicsVelocity.ApplyImpulse() function.
I tried using PhysicsVelocity.ApplyImpulse, but it resulted in accumulation every frame, eventually causing the PhysicsVelocity to become very large. I also attempted to limit the impulse based on the current PhysicsVelocity, but the simulation results were not satisfactory.
here is some of my code :
Entities
.WithStructuralChanges()
.ForEach((Entity entity, ref PhysicsVelocity physicsVelocity, in PhysicsMass physicsMass, in LocalTransform localTransform, in CompoundGraph compoundGraph) => {
foreach (var kv in compoundGraph.graph) {
Entity nodeEnt = kv.Key;
var nodeVelocity = EntityManager.GetComponentData<PhysicsVelocity>(nodeEnt);
var nodeMass = EntityManager.GetComponentData<PhysicsMass>(nodeEnt);
var pos = math.transform(new RigidTransform(localTransform.Rotation, localTransform.Position), compoundGraph.childToCompound[nodeEnt].pos);
ApplyImpulse(ref physicsVelocity, physicsMass, localTransform.Position, localTransform.Rotation, nodeVelocity.Linear * math.rcp(nodeMass.InverseMass), pos);
}
}).Run();
public void ApplyImpulse(ref PhysicsVelocity pv,
in PhysicsMass pm,
in float3 t,
in quaternion r,
in float3 impulse,
in float3 point) {
// Linear
var linearImpulse = impulse * pm.InverseMass;
pv.Linear = ApplyLimitImpulse(pv.Linear, linearImpulse);
// Angular
{
// Calculate point impulse
var worldFromEntity = new RigidTransform(r, t);
var worldFromMotion = math.mul(worldFromEntity, pm.Transform);
float3 angularImpulseWorldSpace = math.cross(point - worldFromMotion.pos, impulse);
float3 angularImpulseInertiaSpace = math.rotate(math.inverse(worldFromMotion.rot), angularImpulseWorldSpace);
float3 angularImpulse = angularImpulseInertiaSpace * pm.InverseInertia;
pv.Angular = ApplyLimitImpulse(pv.Angular, angularImpulse);
}
}
public float3 ApplyLimitImpulse(float3 velocity, float3 impulse) {
return new float3(
ApplyLimitImpulse(velocity.x, impulse.x),
ApplyLimitImpulse(velocity.y, impulse.y),
ApplyLimitImpulse(velocity.z, impulse.z)
);
}
public float ApplyLimitImpulse(float velocity, float impulse) {
float signImpulse = math.sign(impulse);
if (signImpulse > 0 ? velocity >= impulse : velocity <= impulse) {
return velocity;
}
velocity += impulse;
float signVelocity = math.sign(velocity);
if (signVelocity * signImpulse > 0) {
velocity = signVelocity * math.min(math.abs(velocity), math.abs(impulse));
}
return velocity;
}
You said you had removed the PhysicsWorldIndex. That would lead to accumulation if you apply the impulse on an Entity without it.
Make sure that when merging these into a single Entity that you have all the necessary components (see this documentation section).
For how to correctly apply impulses see also the “Motion / Impulse” sample scene in the PhysicsSamples project.