Open ashirviskas opened 1 year ago
I have made a nice reproduction of the weirdness
In my example, the character is the parent of both the pressure plate and the hand. As the character moves, its position and transform are both updated, but on the children only the transform is updated. This results in the children visually following the parent while the physics position not following the parent.
If the child has a velocity, the physics logic takes priority and overwrites the transform, resulting in the child moving with a velocity independent of the parent's velocity.
In the example, I have added ghosts to show the difference between the transform and the position
https://github.com/Jondolf/bevy_xpbd/assets/9356891/8fac3ecc-7886-42e0-a2df-2b96c7cdd005
This is all related to the decision to use components instead of Transform to handle the physics (https://github.com/Jondolf/bevy_xpbd/issues/16 and https://github.com/Jondolf/bevy_xpbd/pull/96)
/// When synchronizing changes in [`Position`] or [`Rotation`] to `Transform`,
/// the engine treats nested [rigid bodies](RigidBody) as a flat structure. This means that
/// the bodies move independently of the parents, and moving the parent will not affect the child.
///
/// If you would like a child entity to be rigidly attached to its parent, you could use a [`FixedJoint`]
/// or write your own system to handle hierarchies differently.
Here's a naïve attempt at inheriting the parent velocity. I changed the pressure plate from static to dynamic so that it also receives a velocity component that we can update.
fn inherit_velocity(
mut kids: Query<(&mut LinearVelocity, &mut AngularVelocity, &Parent), Without<Character>>,
parents: Query<(&LinearVelocity, &AngularVelocity), With<Character>>,
) {
for (mut kid_linear_velocity, mut kid_angular_velocity, parent) in &mut kids.iter_mut() {
if let Ok((parent_linear_velocity, parent_angular_velocity)) = parents.get(parent.get()) {
kid_linear_velocity.x = parent_linear_velocity.x;
kid_linear_velocity.y = parent_linear_velocity.y;
kid_angular_velocity.0 = parent_angular_velocity.0;
}
}
}
https://github.com/Jondolf/bevy_xpbd/assets/9356891/80b17322-8c05-4259-9fd8-d192f26e5d92
This breaks the hand movement logic because the acceleration from its inputs gets clobbered when inheriting the parent velocity. Additionally, this would cause the children to rotate in place rather than pivoting around the parent.
Overall, the decision to have separate components instead of using Transform is because the physics system is inherently flat rather than hierarchical. The ECS itself is also flat, with Parent
and Children
being conveniences. Using a joint, as recommended by the code comment, is probably the best way to solve this issue.
If you're going to manually adjust transforms to have entities follow another, watch out for: Jitter when making the camera follow a physics object
@TeamDman thank you for your response, adding a simple FixedJoint
between parent and child solved the issue.
However, it seems to be degrading the performance by a lot (per my testing, roughly between 30% and 50%) when I have a ton of entities. Is there a way to have something more solid than a FixedJoint
, so it would not try to calculate all the dampenings, but just treat two entities as one regarding the physics?
I've also tried the method of applying the velocities as per the example provided, but it leads to the entities being out of sync.
@Jondolf maybe I could get a bit of your input. How to connect two entities properly, where one is physical and the other is just a sensor without incuring physics calculation costs? As FixedJoint
seems to have a big performance impact compared to naive solutions (but they have other costs). Any other way to basically glue an entity and a sensor together?
Doesn't spawning the sensor collider as a child work? Made a quick demo (on main branch, using Bevy 0.15 RC), seems to work fine
https://github.com/user-attachments/assets/6d241ce8-73cf-4b7d-9c8e-0fdb92ea340c
I noticed in your original code the child collider is also a RigidBody
(link). You generally shouldn't nest rigid bodies like this, especially dynamic ones. Instead, just spawn the child with a collider, but only have RigidBody
on the parent. Not sure if this is related to your actual issue though.
I've uncountered some interesting collision handling behaviour and I am not sure if it is an error on my part, or a bug in
bevy_xpbd
.I have entities that have
Vision
. I implementVision
, by spawning a circle with radius X collider as a child of anEntity
(craber), in the middle of theEntity
(craber).I want to catch when other entities (Food) get into the vision (anywhere in the radius X) and I found a way of doing that by using xpbd's
SpatialQuery.shape_intersections
. However, it was very costly, so I implemented it in such a way, that would only check for intersections if aFood
entity has entered theVision
viaCollision
. However, it seems, that theCollision
event betweenVision
andFood
only happens at a craber spawning time. It does not happen at any other times.Here is the relevant code (excuse how messy it is):
Craber
withVision
spawning linkFood
spawning link