#Avian Physics
1 messages · Page 7 of 1
lemme check
ragdolls are usually done via joints though
compound colliders aren't really meant to be disjointed, but I guess you could
for further context, i have a lizard/snake that i don't want objects to be able to squeeze through its individual colliders
hmm
do you want them to be able to move separately?
by that description I'd assume the colliders would be "dragged" behind the head of the snake
yes its something like that
I'd suggest joints in that case
Oh I'm already using joints! How do they help here?
Just applying enough force to keep things from pushing the circles apart?
no worries!
I am also interested in the overlapping circle colliders because I think those produce the best lizard shape aesthetically
I think you should be able to make it so joints let you overlap with the things it is jointed to
or maybe not?
I guess you could do it through collision layers
but idk how you'd deal with multiple snakes in that case
@vestal minnow is there a way to do the equivalent of this in xpbd? https://docs.rs/bevy_rapier3d/latest/bevy_rapier3d/dynamics/struct.FixedJoint.html#method.contacts_enabled
ohhhh yeah that's exactly what i need
well maybe not exactly but can do 90% of what i need
Not yet but I'll try to add it for 0.4
I'll do this next week, I have an exam week rn so I'm kinda busy unfortunately
Yep it's a bit misleading. I originally mostly followed the naming used in parry/rapier, and they also seem to use direction for raycasts despite it actually being velocity
Ahh fair enough. I'd considered making a PR but looking at #math-and-physics message this conversation it looks like you're planning on changing the API anyway
Good luck with your exams. Hope it goes well!
I'm not seeing examples in the brain repo. Apologies if that's been asked a million times but are there available examples?
What examples are you looking for? Anything specific or just anything about using bevy_xpbd?
is there anything on the background that actually cares about distance as f32, or would removing it from params and just multiplying velocity work exactly the same?
Is there an elegant solution for multiple ShapeCasters on 1 Entity or am I restricted to 1 ShapeCaster in 1 direction and any more will need to be manually done via a SpatialQuery? I'm looking for collisions along -Y, -X, and +X in a platformer setting.
You should be able to have as many shape casters as you want if you add them as child entities
You'll just need to handle queries a bit more annoyingly if you want to do something to the parent when one of the child shape casters detects a hit
Gotcha, thank you!
That would actually be a really good use for bevy_event_listener, it allows you to send events to a child, but handle them in a parent with "bubbling"
I'm trying to setup a movement system where there can be acceleration and deceleration with different values. This is what I have so far:
pub(super) fn walk(
mut walk_event: EventReader<WalkEvent>,
mut query: Query<(
&mut LinearVelocity,
&WalkSpeed,
&WalkAcceleration,
&WalkDeceleration,
)>,
time: Res<Time>,
) {
for event in walk_event.read() {
for (mut velocity, speed, acceleration, deceleration) in query.iter_mut() {
// NOTE: Acceleration: 50.0, Deceleration: 100.0
// walk_event's value will be either -1, 1, or 0 depending on whether the user
// is trying to move left, right, or not at all.
let direction = event.0;
// Increase x velocity by acceleration value
velocity.x += direction * acceleration.0 * time.delta_seconds();
// If no movement direction, but entity is still moving, apply deceleration
if direction == 0.0 && velocity.x != 0.0 {
velocity.x -= deceleration.0 * time.delta_seconds();
dbg!(velocity.x);
}
// Make sure velocity doesn't exceed walk speed
velocity.x = velocity.x.clamp(-speed.current(), speed.current());
}
}
}
I'm having a bit of trouble though. After I've pressed a movement key, my character starts sliding to the left, I think I understand why (the if direction == 0.0 && velocity.x != 0.0 isn't right), but I don't know what condition to use to make this work.
wdym different accel/decel? curve?
or just the variable?
So I'm having some general stability/behavior issues with bevy_xpbd_3d in a new XR project. There's a few different specific issues:
- First, generally, collisions between dynamic bodies are being weird. They're sleeping really aggressively, and when they get woken up by other collisions they tend to jump/hop in random directions. This got worse when I tried f64 precision.
- Second, I am trying to make some velocity-tracked 'physics hands' by controlling a dynamic rigidbody hand's velocity based on its position relative to the controller, like so:
fn move_hands(
mut hands: Query<
(&PhysicsHand, &GlobalTransform, &mut LinearVelocity),
Without<OpenXRController>,
>,
controllers: Query<&GlobalTransform, With<OpenXRController>>,
) {
for (hand, hand_global_transform, mut hand_local_transform, mut velocity) in hands.iter_mut() {
let controller_transform = controllers.get(hand.controller).unwrap();
let delta_position =
controller_transform.translation() - hand_global_transform.translation();
velocity.0 = (delta_position * hand.follow_strength).into();
}
}
However said hands don't interact with other objects in a reasonable way. Instead of pushing them out of the way, they phase through, with some resistance. They do bump the other objects slightly, in random ways, sometimes. If the hand ever fully overlaps another object they both disappear to NaN-land.
They do have a collider, and if I take the same objects but just don't modify their velocity, they collide as normal.
that reminds me of this #1124043933886976171 message @brisk bay
no idea if it got resolved though
The deceleration code is pushing the object to the left regardless of the direction of the velocity. I would suggest trying to emulate drag by exponentially decaying the velocity when there's no acceleration applied, instead of reducing it linearly. That's simple, physical, and more well-behaved when velocity is close to 0.
I see. How would I do that?
This would be a basic way to implement it
let drag_constant = 2.0; // Tune this to control the behavior
// If no movement direction, but entity is still moving, apply deceleration
if direction == 0.0 && velocity.x != 0.0 {
let drag_acceleration = -velocity.x * drag_constant;
velocity.x += drag_acceleration * time.delta_seconds();
dbg!(velocity.x);
}
Good initial value for drag_constant would be (desired deceleration at top speed) / (walk speed)
If the deceleration speed is good initially but it takes too long to come to a complete stop, or vice versa, you could raise the velocity to an exponent (like 1/2, or 2) to change the shape of the deceleration curve
But there might also just be a built in drag mechanism in bevy_xpbd
Automatically slows down a dynamic rigid body, decreasing its linear velocity each frame. This can be used to simulate air resistance.
there's a LinearDamping component
ya
but it's applied uniformly in all directions
whch probably isn't what is desired here
Thank you 🙂
@vestal minnow i didn't check, did you end up fixing that framerate-dependent drag issue? because knexer just handed out the solution to it there
@sleek thicket this?
yes
not yet, that issue is just in the example afaik
LinearDamping uses a different formula that should be framerate-independent I believe
yep
wouldn't it be possible to just change lin_damping into a vec2 btw
yes
that way you can configure it for sideways and custom gravity
in unity i ended up making everything custom though
same could be done in xpbd with a custom system
yeah that's what i'm doing here too 🥲
especially because of different movement slowing effects, idk if changing lindamp would be good for that
drag as a vector is just kinda weird physically, like why would it be different when going forwards/backwards vs. left/right (assuming that shape doesn't affect things)
but for gameplay purposes it'd probably be good
esp for hovering collider
gravity and spring handle Y, but not X
unless i check ground for friction 🤔
i'm still not done transferring the whole movement system though, so i might have a better opinion later anyway
and btw
#math-and-physics message
i was constantly switching layers in unity, is doing that bad in xpbd for some reason?
wait did i brainfart
that's what the error is basically suggesting, it's not a component so you can't query for it
it's stored in RayCaster/ShapeCaster or given in SpatialQuery methods
that way you could also have different filters for the different casters if they were on the same entity
i'm still brainfarting, changed to collisionlayers but can't figure out how to replace it
i guess i'd have to start using physicslayer and just add/remove instead of replacing group/mask
but why not just replace them ._.
I was curious, how does linear velocity ever reach zero with this? Doesn't the drag deceleration get smaller and smaller as the velocity decreases, so it would never get it to zero?
Yet I see it hitting 0.0 eventually, so I guess I'm just confused how that's happening.
it'll be practically zero, plus floating point precision will eventually cut it to 0.0
and also bodies can be marked as sleeping if they're still long enough, which will set velocity to 0
just tested knexer's one, and it definitely ends up stopping
or i can't tell, my char doesn't sleep because of spring anyway
Yeah, it definitely does stop, I was just asking how, but floating point precision cutting it off makes sense.
Oh sorry, were you talking about something else @sleek thicket ?
floating point precision + sleeping + it'll be close enough to zero that any movement would be unnoticeable anyways
nope, other than the previous topic of CollisionLayers' mask/group not being pub
should expose some more APIs for that probably
or make it pub.
yeah
all that API does is bitshiftflip so i don't see a reason why i can't just swap the whole thing
you can probably do *collision_layers = CollisionLayers::from_bits(...) or whatever
or *collision_layers = old_layers.add_layer(...) or smth
yeah let me try
oooh yeah that works
i guess i didn't understand how deref works on components
i can actually continue working now ty \o/
no worries
although i probably shouldn't work at night
ideally you could just add/remove individual groups/masks without replacing and copying things ofc
but for now replacing the component value should work
in unity it was basically replacing just the groups
except in unity it was literally just 1 layer, not groups
ye and I still need to read for tomorrow's Swedish exam
btw @vestal minnow any brief thoughts on this issue? Basically I'm seeing:
- lots of penetration when I'm setting the velocity every frame during Update (which should be before physics runs in PostUpdate by default right?)
- chaotic behavior around sleeping, e.g. bumps when waking or sleeping really early
Mostly wondering if there are any tweakables I should try tweaking, or maybe if I should be scheduling the velocity setting differently, or if there's just something basic I'm missing
I could record a video next time I have a headset with me if that'd be helpful
- sleeping can be disabled
Sleeping can be disabled for specific entities with the
SleepingDisabledcomponent, or for all entities by setting theSleepingThresholdto a negative value.
- You could try increasing the
SubstepCountbut I'm not sure if that'll help much. Your scheduling looks fine since you're modifying velocity; if you were updating the position directly, running in `SubstepSchedule´ could be more stable, but that shouldn't be needed here
could also enable debug rendering to see the hand colliders in case you don't already
Thanks, okay, I'll try those! I could probably be ok with sleeping disabled across the board as there shouldn't be too many bodies at once in what I'm doing
Substep counts I've already bumped to... 30 I think? I can try going higher
hmm that should definitely be enough I believe
what kind of collider(s) is the hand made of?
For the colliders, I can confirm in game that they line up nicely with the rendered mesh, like if I gently touch the hands to one another they respond reasonably
like trimesh, convex hull, etc.
hmm yeah I don't see why that would have any clipping issues or other stability issues
kinda weird
what if the follow strength is a lot weaker?
then the hands follow behind the controllers very slowly, but still penetrate other objects in the same way
could try giving them a constant velocity and see how collisions behave with that
like set the initial velocity and let the sim go?
yeah
I am doing something similar with 'press b to spawn a cube' which then falls under gravity to the ground
that's where some of the other weird collision/sleeping behavior is coming from
but they do mostly behave sensibly-ish, like there's not the same kind of penetration happening at all
could it be an issue with the scale of the numbers I'm using somehow? I know that can be an issue with physics engines, if they expect certain sizes/masses they can be weird outside that domain
closer to 1 would be ideal but I doubt it
The cubes are .1 meters each way, hands have a mass of 1kg, default cubes have... probably much less mass now that I think about it
At least I assume Mass(1.0) means 1kg?
oh? interesting
or most likely not, but I believe it means that your angular inertia values could be off unless you specify them as well
not entirely sure
https://github.com/knexer/bevy-vr-test/blob/main/src/velocity_hands.rs#L44 is spawning the hands
oh
hmmm
lol that would though
I assumed that was being added like Position/Rotation/LinearVelocity were
it is, but it's zero since collider density is 0 and you don't specify it
huh, I'd expect the nonzero mass to still mean it has inertia, but i guess not angular inertia?
inertia == angular inertia in this case
Okay I'll have to try this - wish I had my headset on me, oof
engines often like to omit the angular part from the name even tho it's not as accurate physically
yeah tested cubes example and it definitely explodes without inertia
okay yeah, that does make sense. Thanks for all the help!
I'll report back once I try out these fixes
if you want to add Inertia explicitly then something like Inertia(Mat3::from_diagonal(Vec3::ONE)) seems to work
scale the diagonal vec to increase it
alternatively, you could compute mass props from a collider shape with MassPropertiesBundle::new_computed
initializing multiple interrelated components seems like a hard thing to do API-design-wise without this kind of footgun / rough edge sneaking in somewhere, huh
easy to do something that's in some sense invalid / not internally consistent
yeah there's the MassPropertiesBundle but it doesn't help that much with that issue currently
I guess that's why computing from the collider is the well-lit path, it guarantees things are sensible
in general the easiest way is to just use a collider with some density yeah
specifying them manually can be inherently error-prone and unphysical since e.g. inertia depends on the shape and offset of the object and also the mass
it might be nice to have an alternate new_computed that takes a mass instead of density, in a lot of cases that's the more fundamental number game design wise
yep
I would often find it really unexpected if I shrunk a collider to make something easier to dodge or whatever and it suddenly weighed 1/3 as much
I believe that should be possible, although parry's mass property method just takes a density
would probably need to compute the volume first somehow
parry's shapes should have volume methods, but I'm not sure if there's one for the dynamic shape so maybe I'd need to handle all of the variants separately... idk I'll have to check
ouch that looks painful
yikes. well for now it's not hard to calculate densities on a case by case basis, as long as I know that I need to, haha
and tbh thinking in terms of densities actually helps a fair amount in setting realistic masses, like it prompts me to think in terms of what material the object is composed of and go from there
or it might be doable, I think I can just compute some valid inertia and then scale that based on the mass
but I don't think I can compute the correct one directly since I'd need the volume which is annoying to handle
isn't there already volume calculation happening to get the mass from the density + collider?
yes but it's handled by parry, and it's complex enough that it has an entire folder for the different shapes
I couldn't find a singular shared volume method, instead it does stuff like this
let (cyl_vol, cyl_unit_i) = Self::cylinder_y_volume_unit_inertia(half_height, radius);
let (ball_vol, ball_unit_i) = Self::ball_volume_unit_angular_inertia(radius);
(for capsule)
weird how the shapes don't have volume methods 🤔
they're pretty straightforward for the most part, I already implemented them for Bevy's shape primitives
yeah it is odd that it doesn't have a uniform interface
cute budgie pic btw 🙂
haha thanks, it's better when animated but github doesn't do it: https://imgur.com/SqZlt1I
yep this 100% was the issue; switching to specifying density instead of mass fixed the hand collisions
and disabling sleeping made the other cubes much better behaved as well
How do we make this answer the first google search result? You should add this to a notice or special bullet in the ReadMe... Thanks anyways, solved all my issues!
probably would be better to include it in examples with an explanation
Yep
Yeah I'll try to remember to add docs or an example for this. There's also an issue with the fix
https://github.com/Jondolf/bevy_xpbd/issues/211#issuecomment-1789342920
Is there a convenient way of controlling the model's rotations and offset inside bevy. I downloaded som models and I notice that their "origin" and "base rotation", or whatever you would call them, are not where I would want them. I can adjust this by changing the model in Blender, but I feel it would be nice to control it by just setting some offsets in code instead. My problem is that the loaded scene and the model do not have the same center. Or is it perhaps advisable to go with a child entity with some transform of its own?
The reason I ask here is because it pertains to xpbd which I use...
If the origin of the model is off, that's something you'll need to do in blender beforehand.
If the model is just translated that's something pretty easy to fix in-game by setting it to zero.
It seems to me that the origin for the colliders cannot be set to anything but the center / origin of the entity, which is what I'm getting at. Maybe I just don't understand all this well enough. I will try to just change the origin in Blender, should work well enough for me.
I just found the answer, maybe, in the code, regarding child entities and colliders. I think I might have what I need now.
Why same colliders are rendered with different wireframe?
These are all Collider::ball(0.2)
are you sure? those other ones look like trimesh colliders
I spawn then in a for cycle with same spawn command
let ball = Collider::ball(
collider.radius,
);
info!("Spawning {:#?}", ball);
let rg_entity = commands
.spawn((
Name::new(format!("COLLIDER_{:?}", *bone_id)),
TransformBundle::from_transform(transform.compute_transform()),
VisibilityBundle::default(),
RigidBody::Dynamic,
ball,
Restitution::new(0.7),
*bone_id,
CollisionLayers::new(
[collider.layer],
[RagdollLayer::Floor, collider.layer],
),
))
.id();
In the logs:
2023-11-27T00:27:48.822403Z INFO main::ragdoll: Spawning Ball { radius: 0.1 }
2023-11-27T00:27:48.822499Z INFO main::ragdoll: Spawning Ball { radius: 0.1 }
2023-11-27T00:27:48.822573Z INFO main::ragdoll: Spawning Ball { radius: 0.1 }
2023-11-27T00:27:48.822657Z INFO main::ragdoll: Spawning Ball { radius: 0.1 }
2023-11-27T00:27:48.822714Z INFO main::ragdoll: Spawning Ball { radius: 0.1 }
2023-11-27T00:27:48.822779Z INFO main::ragdoll: Spawning Ball { radius: 0.1 }
2023-11-27T00:27:48.822890Z INFO main::ragdoll: Spawning Ball { radius: 0.1 }
hmm, could you log the transform scale?
ball colliders are currently turned into convex hull colliders if they have non-uniform scaling, because ellipsoids aren't supported as a built-in shape at the moment (same in rapier)
so if your transform.compute_transform() scale isn't perfectly uniform (x, y and z have the same exact value) it won't be counted as a real ball anymore
Well yeah, it is not uniform
Almost all of them have value similar to this... except the root which is not touched
scale: Vec3(
0.9999999,
1.0,
0.9999996,
),
I'll try to fix, thanks a lot.
Trimesh balls are spawned in infinity with joints sometimes 😄
yeah 😄 you can probably just round the values or use something like Vec3::splat(scale.x) to make sure it's uniform
and yeah GlobalTransform::compute_transform can have numerical issues because global transforms use an Affine3A which basically has scale and rotation in one matrix and it needs to do some math things to compute the scale
tbh I think xpbd and rapier should clamp to uniform scaling within a threshold by default
having a non-uniform scale is already a weird concept for physics and having it be off by a minor amount shouldn't really matter all that much
Yeah it should probably use some small threshold
I think for the ball case we could technically add support for ellipses and ellipsoids too 🤔 at least ellipses seem to have a pretty trivial support map
Hello, I recently switched to bevy_xpbd to try things out, and I was wondering if there was a crate for picking, similar to bevy_mod_picking with its backends, or if there's a reason for there being none (such as having xpbd handle it itself with some ergonomic plugin). I could write it up myself but I figured I don't have to reinvent the wheel if I can.
Also, is there a way to search for text in posts such as this? I tried using the search bar at the top right of the Discord client but I don't seem to be able to filter through messages in this particular post, it just shows me all of them.
I think the only thing you cando is filter to crate-help 🙃
If you just need some way to pick an element on screen that has a collider, you can just use https://docs.rs/bevy/latest/bevy/render/camera/struct.Camera.html#method.viewport_to_world and do a https://docs.rs/bevy_xpbd_3d/latest/bevy_xpbd_3d/plugins/spatial_query/struct.SpatialQuery.html#method.cast_ray with that origin/direction
Looks like you could also provide your own backend (or even upstream an xpbd one?)
Figured, I'll write it myself then, thanks.
Hello. Sorry, I think I have asked this before but I didn't understand my own question and/or answer. If I want to make a bounding box... It is just a box? How does it know whether to be inside or outside?
I'm not sure if I fully understand the question, but a bounding box is just a box that fully contains some shape. An AABB is an Axis-Aligned Bounding Box, meaning that it's orientation aligns with the world's axes (i.e. it's not rotated). For example, if you had a square shape rotated by 45 degrees, the AABB would be a box where each side is at the furthermost parts of the shape, in this case the corners. I'm not sure what you're trying to do with bounding boxes though, they're mostly a thing used for internal acceleration structures
For colliders, you can compute the AABB with collider.compute_aabb(position, rotation)
And here's a Wikipedia article on bounding volumes for good measure https://en.wikipedia.org/wiki/Bounding_volume
Yeah, I am not that smart. I guess I am going to try asking in a way that makes it apparent what I do and do not know.
I understand I can make a box collider and a circle collider.
But can I make it so that if I need to frame my level, I use a single bounding box?
And something can happen inside of a circle collider instead of a bunch of balls colliding?
Colliders are meant to keep things out of them if im not mistaken. So to frame your level, you'd have something like a cube collider for the floor, each wall, and roof. Something like that
Any ideas on how to eliminate jitter when I have my camera following a rigidbody?
https://github.com/TeamDman/Cursor-Hero/blob/main/examples/jitter.rs
wow that's exactly the problem, thank you
Hey!
I have a sword slash that I'm modeling as a box collider moving along an arc. I need to detect when the sword hits other colliders (but will pass through them). What is the best way to handle the movement here?
- just put collider on and use transform directly? (Would be my preferred, but unsure if collisions handled okay with other rigid bodies)
- have a kinematic rigid body and do angular vel + linear Vel to control movement? (A bit tricky to ensure the correct movement)
- use a revolutejoint constraint and kinematic rigid body (again, unsure how accurate I can get the movement speed along the arc)
- a custom implementation of the first idea collision carefully
3 won't work because kinematic rigid bodies aren't affected by joints, forces etc. since they're user-controlled. It'd need to use a dynamic rigid body
If the sword should detect collisions but just pass through everything (not cause a collision response), you could make it a sensor collider and move it manually with transforms. To make it rotate around a specific point (like the sword "handle") you could make the collider a child entity that is offset, and move the parent instead
so I would probably do your first idea if I interpreted it correctly
Yes, it sounds like it.
But basically I can use a non-rigidbody collider and still have all collisions detected correctly?
yeah I'm pretty sure it should still detect collisions
Thanks! I'll implement that and update if I run into trouble
Hello. I have a player that can rotate and move. I need to add velocity to my character but when i give velocity, lets say velocity to x, it goes to world x but i need it to go local x. Does bevy_xpbd have local_x velocity or something like that? if it doesnt, how can i implement this?
There is no built-in helper for this, but you can just rotate the desired local velocity by the transform rotation to get the corresponding global velocity and apply that to LinearVelocity.
Something like this
let global_velocity = global_transform.rotation * local_velocity;
If you're using 2D then you just need to convert between Vec2/Vec3
If it has a Rotation component is it preferred to use that instead of the GlobalTransform rotation?
Yeah you can use rotation.rotate(...) as well
Transforms might generally be considered more idiomatic/familiar in Bevy which is why I often use them in examples, but Rotation will give the most accurate result since it is what is actually used for physics. The difference for users should be basically negligible in most cases tho
Looks like Sensor colliders send CollisionEnded events when the imposing entity velocity reaches zero, here I use CollisionStarted and CollisionEnded to mark the colour
fortunately, looks like CollidingEntities can be used to do what I want instead
A component that stores the entities that are colliding with an entity.
well, that simplified things a lot
#[derive(Component, Default, Reflect)]
struct CanInteract(pub HashSet<Entity>);
#[allow(clippy::type_complexity)]
fn can_interact_update(
mut commands: Commands,
mut button_query: Query<(Entity, &CollidingEntities), With<MyButton>>,
) {
for (button_entity, button_touchers) in button_query.iter_mut() {
if button_touchers.0.is_empty() {
commands.entity(button_entity).remove::<CanInteract>();
} else {
commands.entity(button_entity).insert(CanInteract(button_touchers.0.clone()));
}
}
}
fn reset_interaction_color(mut query: Query<&mut Sprite, With<MyButton>>) {
for mut sprite in query.iter_mut() {
sprite.color = Color::rgb(0.0, 0.0, 0.0);
}
}
fn update_interaction_color(
mut query: Query<&mut Sprite, (With<MyButton>, With<CanInteract>)>,
) {
for mut sprite in query.iter_mut() {
sprite.color = Color::rgb(0.0, 1.0, 0.0);
}
}
oh wait
it still does the thing where it stops detecting at zero velocity
rip
Created this issue for it https://github.com/Jondolf/bevy_xpbd/issues/264
Hey, when combining RigidBody and AsyncSceneCollider I get a warning about it not having mass during the period where the model isn't loaded
Is there a better way to do this? i.e. loading the model(s) on startup, and spawning the entities later?
just zero velocity or sleeping?
I believe it's zero velocity because the broad phase ignores collisions between bodies that aren't moving and the events don't handle that correctly
(but sleeping can also set velocity to 0 so it's somewhat connected)
I made a PR for renaming cast_ray to raycast and so on
https://github.com/Jondolf/bevy_xpbd/pull/269
However, changing time_of_impact to distance requires normalizing direction, and we probably don't want to do that for each raycast. I guess we could just mention in docs that it must be normalized, but it'd be nice if it "just worked".
I think ideally the methods would take Bevy's Ray2d/Ray3d primitives (just noticed, they're not added yet despite being in the RFC lol). That way, it wouldn't need to normalize as often because users can reuse the same ray for multiple casts or even bypass normalization if the provided direction is known to be normalized
on toi: why not just remove distance as float, and let people multiply direction? then there's no ambiguity
What exactly do you mean? What would the result of a raycast be
normalized dir with 10 distance is same as normalized dir * 10, which is same as e.g. non-normalized dir with magnitude of 10
a vector from the origin to the hit point?
yeah, getting distance is just getting magnitude
I haven't seen any engine handle it like that
yeah, but it makes sense, doesn't it?
e.g. i often need to send a ray towards target, there could be a linecast, or i could just get dir towards target and without normalizing just use it for raycast without needing extra distance
basically that gives more control so i'm curious if it would actually be good in practice
also i didn't check what raycast does to get toi, does it just return a fraction of a distance if vector isn't normalized?
toi is basically direction.length() * distance
(direction is velocity currently, which is unintuitive)
so i guessed right?
the main problem is wording but removing distance as parameter makes it mostly intuitive anyway
returning a vector is more expensive technically
if you had 1000 hits, that'd be 1000 vectors vs. 1000 scalar distances
RayHitData only needs to store the distance since the direction is stored in the ray itself
don't have to return a vector, i mean it doesn't really change the result if you normalize before and input distance, or just do it afterwards knowing that nothing is normalized
you don't even have to normalize, it's just length, isn't it
both involve sqrt which is the expensive part
("expensive" in the micro-optimization sense)
if you don't return a vector, where are you removing distance from?
raycast(dir*dist..)
instead of raycast(dir, dist)
and returned result.length instead of direction.length * dist? i don't know what's going in on the background
In that case you mean max_distance? raycast doesn't have a distance
and that'd just be confusing
oh, is there a difference?
max_distance is an argument controlling where to stop the raycast, i.e. a collider 2 units away from the origin wouldn't be detected with 1
distance (currently time_of_impact) is the actual distance from the origin to some hit point, stored in RayHitData
just call it toi, i get what you mean <_<
instead of returning correct distance instead of toi, you wanted to normalize direction and then multiply it by distance, then return direction.length() just hitDistance, correct?
how do you get hitDistance?
it's just time_of_impact if the direction is normalized
yeah, i basically don't understand the difference between that, and removing max_distance and just using non-normalized vector pre-multiplied, then returning hitDistance
If you remove max_distance, you'd need to have a vector like Vec3::X * f32::MAX to get all hits in the X direction
the vector representation would be confusing imo
yes
but making the direction be a line segment is more confusing in my opinion than having it be just a direction and storing the max distance separately
it's essentially just moving a number around for users, but it's very clear that it's a velocity and not direction that needs to be normalized
This is kinda the API I was imagining
let ray = Ray2d::new(Vec2::ZERO, Vec2::X);
let result = spatial.raycast(&ray, f32::MAX, true, default());
println!("Point: {:?}", ray.point_at(result.distance));
and you get a choice of using something vectors of correct length that don't need to be multiplied
or you already have a normalized vector and multiply it as usual anyway
there won't be a need to double-check if it's normalized either
so in my mind it'd be better if nothing on the background gets worse because of that
so the vector would represent both the velocity (direction + speed) and maximum toi?
so 3 things
pretty much
then all of the toi results are kinda wack
if you use Vec3::X * f32::MAX for the vector, time of impact would always be 0 or close to 0
so you wouldn't be able to query for all hits in a direction and get toi values that make sense
(to query for all hits in the direction, the vector length in that direction must be f32::MAX if the vector also controls the max toi)
i'm not sure how often it's needed though
it makes sense to get a fraction of path and then just multiply by length as return
might not even be a point to multiplying by length actually
i can actually think of a case where damage is multiplied by distance, and 0~1 is way more useful for that
aka damage falloff... a really common mechanic for shooters.
idk that approach just seems overly complicated with no significant benefits, people find velocity and time of impact for raycasts confusing
direction and distance are more intuitive
i can confirm it's confusing, but only because you have both the velocity AND max distance
dir * dist just makes it more obvious that you don't have to normalize, and the results will vary if you don't
Velocity and max distance control two very separate things, with max distance being more important to have configurable than the velocity magnitude
dir * dist confuses me
I assume you mean dir * speed = velocity
but also |velocity| = max distance somehow
yeah, pretty much
i can't think of any case where it wouldn't be interchangeable though
e.g. line-of-sight raycast (assuming there's no linecast added) is basically just velocity * 1 right now
trying to think of anything else where max_distance varies but i think i'll need to look through the code
yeah, i just can't think of a raycast case that needs variable max_distance
almost everything?
ground/wall-check ray/shapecast is a fixed distance, guns are all fixed, vision checks are either lines or fixed
with a grounded cast, you probably want to get the distance to the ground, but restrict the raycast to a max distance of something like 0.1
and having to divide the result by 0.1 to get the real distance would be annoying
and this issue
in my case i had an anti-gravity char controller with a spring, so at least here i can confidently say it wouldn't matter to me
what would the vector even be called, velocity_with_max_toi?
does it really matter? it's easy to document as direction * speed
but also max_toi
a line.
a line segment
a vector representing a line segment, where 0 is the origin.
velocity doesn't imply that the ray ever stops
Hello peeps,
Is there a way to have a 2D physics sim, but then ouput positions on (x,z) axes instead of (x,y) axes? To better suit a top down game.
Probably related to altering SyncPlugin somehow.
yeah, velocity is confusing
direction implies being normalized
speed doesn't really imply that it's the end
linecast is between 2 points though
i can't think of any real use case for infinite rays in a game at least
and anything that isn't infinite is a line by that definition
physics are 2d but game is 3d?
Yessir
🤔 coordinate problems stike again
i think it'd be easier to just change the 3d perspective, should be more comfortable to have Z-up
totally, but I'm not comfortable with Z-up 🤮
it makes more sense than -Z forward though 😂
As the saying goes, y to sky. Not z to the sky
never heard of that but i like it
i don't particularly like Z pointing out of my ass though
you didn't get any bugs with Z being reversed yet?
I haven't started. What do you mean z is reversed?
I can cope with that. That chart is really funny, Unreal is sticking it out there by itself.
Swapping translation coords is relatively easy but rotation is more of a pain
oh @indigo scarab are you using bevy_xpbd_2d?
in that case it'd be easy I think
yes i am
alr I'll test it real quick
famous last words
estimated time: 2 seconds
actual time: errrrr
even if i'll never really get to use it, i'll still really appreciate the coordinate-system-agnostic system 😂
I think it's working? This is a horrible demo, but the camera is looking down towards the XZ plane and the cubes are only moving in XY directions physics-wise
and they bump into a wall but I just didn't position things logically so it looks weird
and the cubes can be offset in the Y plane which is like the Z depth in 2D
I'll post code in a bit, will just clean it up a little
is it for any coordinate system or just an option for XY->XZ?
XY->XZ but it's easy to make generic I think
might be useful to make it generic considering how many people came out speaking for different systems
or not generic as in some arbitrary [0.1, 0.5, 0.3] axis but on the XYZ axes
@indigo scarab
Here is a plugin for the top-down physics thing. Haven't tested much, so idk how well it actually works. I can try more tomorrow if it has issues
Annoyingly, I think it's necessary to replace the entire SyncPlugin because too many things in it are private. I might make more of them public so that you'd only need to replace specific systems
you can just copy that file somewhere
and then configure plugins like
.add_plugins((
DefaultPlugins,
PhysicsPlugins::default().build().disable::<SyncPlugin>(),
TopDownSyncPlugin::default(),
))
the actual changed parts are in position_to_transform lines 502-512
that just swaps y and z around for translation and handles conversion from Rotation to Quat differently (rotate around Y instead of Z)
could easily change the axes too
Thank you so much
Is with_max_time_of_impact framerate dependent? It appears to be acting differently on my 165Hz monitor than on my 60Hz one.
everything is frame rate dependent on variable timing instead of fixed timestep
but overall it shouldn't have massive differences
unless you mean differences of objects in the simulation itself over time
My shapecast is acting weird on my 165Hz monitor. It's detecting the ground at odd times, when that doesn't happen on my 60Hz monitor.
pub(super) fn update_grounded(
mut commands: Commands,
query: Query<(Entity, &ShapeHits), Without<Grounded>>,
) {
for (entity, hits) in query.iter() {
// If the ShapeCaster detects a ground hit, the entity is grounded
if !hits.is_empty() {
commands.entity(entity).insert(Grounded);
}
}
}
ground_shapecaster: ShapeCaster::new(
// Shapecaster should be slightly smaller than the
// player, to prevent colliding when hugging a wall.
Collider::cuboid(1.9, 1.9),
Vec2::ZERO,
0.0,
Vec2::NEG_Y,
)
// Max distance it will travel to detect the ground
.with_max_time_of_impact(0.05),
Is there some plugin to draw 2d colliders for debug?
PhysicsDebugPlugin::default(),
I see, thanks
Hello, what is a good way to convert a vector of trianlges to a trimesh?
Do I need to care about duplicate vertices, or can I just slap TriMeshFlags::MERGE_DUPLICATE_VERTICES and don't care about it?
Automatic collider generator: mostly done
I wish physics debug options had some sort of "collider fill color" so I can see what's inside and what's outside the tri-mesh
@vestal minnow Is this change detection prevention for 3D rotations really correct?
https://github.com/Jondolf/bevy_xpbd/blob/main/src/plugins/integrator.rs#L229-L233
I'm getting Changed<Rotation> every frame despite having every axis locked and all code that modifies it disabled. If Quat::IDENTITY was the "no rotation" value, wouldn't that (rot.0 + delta).normalize() always drift you back to the default rotation, since + is not how you apply rotation and all 🤔
Blocked on this https://github.com/bevyengine/bevy/pull/10621
Objective
Add support for rendering meshes to bevy_gizmos.
Solution
Adapt the mesh rendering code from bevy_pbr and bevy_sprite for gizmo rendering.
I copied the mesh.rs files from both bevy_pbr an...
It looks wrong, I can test later today
how do i check if a RigidBody::Dynamic is touching the ground?
Usual solution would be checking what it touches and seeing if anything it touches has a normal that could be considered up
Alternatively you could specifically mark the ground and see if it's touching that, or shapecasting down to see if it's close enough to the ground
how do i do shapecasting?
also, for an unrelated issue, how do i do raycasting?
There's two approaches, you could spawn a RayCaster/ShapeCaster component which will do a ray/shapecast each frame and update the component with the results, or you can use the SpatialQuery system param to do raycasts/shapecasts on demand
Spatial queries are a way to get information about the environment. They perform geometric queries on colliders and retrieve data about intersections.
component seems easier
wait its not working how i thought it would
i thought it would tell me if theres something inside the collider
Tried to search for this answer but not sure I'm using the right keywords... Is it possible to prevent a collider from inheriting a sprite's rotation? (I'm trying to animate the player with a tween , but I want the capsule to remain vertical)
Making the sprite a child of the collider is the usual approach I think
Ahh that makes sense lol, ty!
Is it possible to make it so when 2 dynamic rigidbodies collide they despawn?
I'm getting motion blur when moving my player with velocity instead of transform (camera follow)
thanks but I had that issue earlier and fixed it after finding that issue
the player itself looks fine but other objects are blurry when it moves
fn collider_despawn(mut commands: Commands, mut collision_event: EventReader<CollisionStarted>){
for collision_entities in collision_event.read() {
commands.entity(collision_entities.0).despawn();
commands.entity(collision_entities.1).despawn();
}
}
thanks
you'll have to filter based on what components you want to actually disappear
i know, the hard part is finding a cool explosion effect
Fixed the change detection I think
https://github.com/Jondolf/bevy_xpbd/pull/272
I'm not that invested in quaternion math but I think checking delta.w != 0.0 is enough? Since it's the angle around a rotation axis
it seems to work at least
and logging the quat, it's always [0, 0, 0, 0] when the rotation shouldn't change
the math is kinda confusing anyways since idk what adding quats with + even semantically means
I think w is supposed to be some magnitude of the rotation or something like that ... But I'm not really sure on the specifics either, I always ask in off-topic and @plain lion explains how it works 🙃
ya it's the angle around some axis I belive
or probably not mathematically, but for rotation quaternions it's an intuitive-ish way to think about it
(and how Unity describes it)
gonna be honest - my entire understanding of quaternions is based on multiplication being composition
i have not put in the time to understand any other part of quaternions 🙃
Yet you're probably already in the top 1% of understanding quaterions in the bevy community :')
Maybe we need to find someone that actually understands quaterions and have them write some solid docs 😂
I've mentioned quaternions to several people with CS degrees and they hadn't even heard of them before
apparently uni doesn't talk about them
or not CS degrees but some engineering degrees
I had a math book that mentioned their existence in some tiny aside tho
I have a CS degree and my knowledge of quats comes solely from game dev
Does anyone that had classes on quats remember what course they took for them? Only thing I skipped was phys with calculus, is it like abstract algebra or some other physics class that teaches them?
I thought linear algebra but I don't remember
Sorry for being lazy, I have done so much tonight. And it is a Sunday. Is there an example of doing hit-testing for 2D with a camera projection? As in, just clicking the screen to select an object?
Are there any benchmarks comparing bevy_rapier to bevy_xpbd for large-scale collision handling?
Not that I'm aware of, but I'm pretty sure rapier has more optimizations to make a high number of collisions cheaper. It's definitely an areas that could use some improvement, tho I also haven't seen many people run into issues because of it
You can get the cursor position with window.cursor_position(), convert that to world space with camera.viewport_to_world_2d(), and then do spatial_query.point_intersections() to get the entities (that have colliders) intersecting the cursor position
If you need data like the exact point where the cursor is intersecting, you can instead use spatial_query.project_point()
bevy_mod_picking is also great for this stuff if checking hits against meshes is enough
(it's primarily 3D but I think 2D works too?)
I will try this tomorrow.
BTW, my game is up, using xpbd.
I was wanting to make an advent-thing, but it is really hard making interesting levels.
So I am working on the level editor, so I need to punch that through the camera.
I guess "tomorrow" is today. When you say spatial_query, is this what we're talking about? https://joonaa.dev/blog/03/bevy-xpbd-0-2-0
Spatial queries, Bevy 0.11 support, improved scheduling, damping, gravity scale, better forces, locked axes, bug fixes...
I guess I want to cast a ray from the camera given the coordinates I got after converting, into the world, right?
Sorry, I didn't mean to be badgering you guys, but I got this far:
But the transform to the ray_hits just turns out like jibberish in my mind.
How do I check if the player is inside a specific Collider? (the player is a RigidBody::Dynamic and the collider is just a Collider and a TransformBundle without a RigidBody)
A system parameter for performing spatial queries.
The code example in the docs uses bevy_xpbd_3d but it's basically the same in 2D
Raycasting might not make sense here because you probably want to just check a specific 2D point and not cast an actual ray in some direction
Query for the player's CollidingEntities component and check if it has the specific collider's entity
Or if you don't know the specific collider's entity but it has some marker component, you can iterate through CollidingEntities and check if collider_query.contains(entity)
There's also a Collisions resource where you can access all collisions and/or collision pairs
So, if with_max_time_of_impact is framerate dependent, should I multiply it's argument by Time.delta_seconds?
Can I just run a piece of code by you guys, because it feels a little off. It does work, I am just curious if I am doing something wrong. Is this "the right way" do check for collisions? It feels like using contains is not very effective, but it is just a feeling. This code is trying to see if the player entity collides with a crystal entity. And it works both in app and on WASM.
I believe you could use CollidingEntities to make it cleaner and more efficient:
pub fn crystal_despawner(
players: Query<&CollidingEntities, With<Player>>,
...
) {
for colliding in &players {
for crystal in colliding.iter() {
if crystals.contains(crystal) {
info!("Normal crystal!");
// ...
} else if last_crystals.contains(crystal) {
// ...
}
}
}
}
this will count all collisions, not just the ones that started, but since you're despawning the crystals I don't think it matters much
OK, but you are still leaning on contains. It is just my reptilian OO-brain that gets into fight/flight when I see that, because it looks like an O(n) thing.
yes but a lot less contains
and query.contains(entity) is O(1) just like query.get(entity)
because it's not just iterating a vec
Ah, perfect. Exactly what I wanted to hear, thanks again.
@merry tide FYI here's a table for the time complexities of query methods
https://docs.rs/bevy_ecs/0.10.1/bevy_ecs/system/struct.Query.html#performance
System parameter that provides selective access to the Component data stored in a World.
It doesn't explicitly mention contains, but looking at the code, it's essentially sugar for query.get(entity).is_ok() (but with some unsafe for performance)
so it's O(1) like get
That is super helpful, my fight/flight is turning into fawn.
I have a problem that isn't necessarily xpbd-related, but I am asking you first because I like you more. And also, you know my situation. I got the selection stuff to work and also the moving-part, but I cannot seem to understand how to make it relate to the camera transform. It looks like this.
And I am already taking the camera transform into account, but perhaps not correctly.
The "whoops" is TimBeaudet, I was watching his stream.
Are you snapping the rectangle position to the cursor position or doing some interpolation?
No, this is just super simple translating deltas from the mouse to objects.
I can mess with the values to make it overshoot and undershoot, but ideally it would just be correct screen-space translated to the physics world.
Could maybe try to set the rectangle position to the world position like point here
You could even have a resource like MousePos and a system that updates it so that you don't have to handle the camera stuff in every system that needs the position
That sounds good. I am all for making it more streamlined, but I feel I just need to get a bunch of stuff to work first.
Can I share that code, tho? It isn't super much.
Translating by mouse deltas seems like it could cause problems in some cases, at least I think I had similar issues in some web dev stuff but I don't remember
ye
I just need to flip branches. And that means I need to also flip the version of the Rust compiler. 🙂
It is just this:
pub fn move_selected_entity(
camera_query: Query<&mut Transform, (With<Camera2d>, Without<LevelObject>)>,
mut entity_transform_query: Query<(Entity, &mut Transform), With<LevelObject>>,
ui_state: Res<UiState>,
mut motion_event_reader: EventReader<MouseMotion>,
) {
if ui_state.dragging {
let camera_transform = camera_query.single();
// Iterate the selected entities
for (entity, mut transform) in entity_transform_query.iter_mut() {
// Check to see if this is the selected entity
if ui_state.selected_entity == Some(entity) {
// Iterate the motion events
for ev in motion_event_reader.read() {
// Move the entity
transform.translation.x += ev.delta.x * (camera_transform.scale.x / 2.);
transform.translation.y -= ev.delta.y * (camera_transform.scale.y / 2.);
}
}
}
}
}
And me dividing it by 2. is just an act of desperation, really. 🙂 I tried all of it.
But you see, I am getting better at splitting the systems up.
caveat, this would make the center of the object be at the cursor unless you take the offset into account, but you probably want to grab corners and sides and stuff
so that wouldn't work in a super simple way
No, but given how you can select and go into dragging, you would think that the translation in X/Y would be a relatively simple function of the camera projection.
If I just do this transform.translation.x += ev.delta.x * camera_transform.scale.x; it overshoots by a bit all the time, no matter the zoom level.
yeah I'm getting the same (side note: this is kinda cool)
camera_transform.scale is probably 1 so it's not actually doing anything there
Ah, cool. I am not alone in this cold winterscape.
Let me know if you figure it out. I am going to try a little on my own, but I am going to look at something else. Because I cannot even get the asset server to work on WASM.
And yes, the demo is super cool and should be the core of the Bevy Jam #4 for any sensible human.
Won't solve your problem entirely but I'd add motion_event_reader.clear(); at the end of your system so you don't read a bunch of old events the frame you start dragging
This makes me very uncertain. Is the EventReader not deterministic?
It is, but you need to mark events as read by reading them or clearing them. You're not reading the events when you're not dragging
It seems like it is a math problem. Because it consistently moves stuff too much or too little.
Won't solve your problem entirely
I know
Sorry, I am not being a Negative Nelly. I am just trying to say that it is odd that what I consider "everyday things" are often a little more difficult that I thought. 🙂
But when you know them it is all simple and the waveform collapses into pure consciousness.
Then again, a lot of the things I envision as almost unreachable are done in a couple of lines of code.
Don't events get dropped after 2 frames anyway? (after being received by FixedUpdate on main branch)
ah this #engine-dev message
Bevy doesn't yet have a convenient method for converting vectors from viewport to world space(only positions) but it's possible to hack around it.
pub fn move_selected_entity(
camera_query: Query<(&Camera, &GlobalTransform), (With<Camera2d>, Without<LevelObject>)>,
mut entity_transform_query: Query<(Entity, &mut Transform), With<LevelObject>>,
ui_state: Res<UiState>,
mut motion_event_reader: EventReader<MouseMotion>,
) {
if ui_state.dragging {
let (camera, camera_transform) = camera_query.single();
// Remove the translation component from the camera transform
let mut affine = camera_transform.affine();
affine.translation = Vec3A::ZERO;
let camera_transform = affine.into();
// Iterate the selected entities
for (entity, mut transform) in entity_transform_query.iter_mut() {
// Check to see if this is the selected entity
if ui_state.selected_entity == Some(entity) {
// Iterate the motion events
for ev in motion_event_reader.read() {
// Convert viewport vector to world space vector
let Some(world_space_delta) = camera.viewport_to_world_2d(&camera_transform, ev.delta) else {
return;
};
// Move the entity
transform.translation += world_space_delta.extend(0.);
}
}
}
}
motion_event_reader.clear();
}
I've done this before but haven't tested this code here c:
It's become much more noticeable but I did notice it with just the 1 frame of old events before
ahh yeah the world space conversion is different for delta vector vs. position, makes sense
Did you figure it out?
Ah, sorry, I forgot to watch history. 🙂
the camera transform in the above code doesn't quite work right it seems
the world_space_delta is always down and to the left even if camera_transform and ev.delta are zero/default
so idk if camera also requires some modification or smth
hhhh I figured that out but now the original issue of overshooting is back
and @pulsar bone it required this btw
let Some(world_space_delta) = camera.viewport_to_world_2d(
&GlobalTransform::from(Transform::from_translation(
camera.logical_viewport_size().unwrap().extend(0.0)
* 0.5
* Vec3::new(1.0, -1.0, 0.0),
)),
ev.delta,
) else {
return;
};
wow that formatting
or wait no that's kinda wrong
@pulsar bone this
let mut affine = camera_transform.affine();
affine.translation = Vec3A::from(
camera.logical_viewport_size().unwrap().extend(0.0) * Vec3::new(0.5, -0.5, 0.5),
);
let camera_transform = affine.into();
the camera transform needs to have the viewport center coordinates apparently, but with y reversed
it makes the thing mostly work, but the original overshooting isn't fixed
and I don't think it's entirely related to the camera anyway like I originally suggested
If you move the mouse slowly, the platform moves faster in relation to the mouse, but with fast movements the mouse can skip ahead
I had similar issues with web drag APIs at some point
with event.movementX/event.movementY I think
or velocity, I don't remember
I was making some board-based notataking app with draggable cards iirc and they were moving at the wrong speed
probably a different issue tho
But is it me being a dumbass, or might it be somewhere else?
I have the same issue so idk
Oof, you're right. It's like MouseMotion itself is buggered when you move the mouse quicly
Is there any way to rotate the collider of an entity without also rotating it's sprite, other than making the collider a child of the entity?
I'd just make the collider a child here
you could use Collider::compound(vec![(position, rotation, collider)]) but idk if that's ideal
Yeah that sounds worse than having to deal with Parent/Children components in the query 😄
Time for an issue? :P
seems like a weird oversight if it is a bug though
maybe there should be a mouse input example with dragging or something
Looks like we get these events directly from winit, so it's got an issue there
Might be good to track the issue on our side as well though
Vaguely related: https://github.com/rust-windowing/winit/issues/2884
yeah, maybe make a winit issue + a Bevy issue that links to it with S-Blocked
Sounds good c:
Now this should actually work ™️
@merry tide
(updated: now handles cursor leaving the window properly betterer)
@vestal minnow ^ In case you're interested.
ya I was trying to make something similar but didn't bother to finish it lol
cool if it works
btw @pulsar bone this is a follow-up to the fallible direction constructor PR if you're interested, just impls TryFrom and adds a custom error type for the different failure cases
https://github.com/bevyengine/bevy/pull/10884
Thank you so much for looking at this. I will test it tomorrow.
Or right now.
to me this smells like some kind of mouse acceleration thing - I'd guess bevy's inputs are raw inputs, and the os cursor has acceleration. if you disable acceleration in your os settings do they track more linearly?
I think it is solid, let me test @pulsar bone thing first. But it should be simpler, perhaps?
You're right. Didn't think I had that enabled.
(Why the hell would you call it "enhance pointer precision" Windows? :V)
Wait, what. Are we finding something simpler?
Nope. It's just that the MouseMotion event is not bugged.
There's just no guarantee it matches the cursor movement, which makes sense thinking about it now
I guess we need CursorMotion events 😔
I like that there's "mouse", "cursor" and "pointer" which are all subtly different but can also mean the same thing
Hey, is there a way to make a regular polygon collider?
And/or create a collider from a Mesh
I'm creating a Mesh::from(shape::RegularPolygon { sides, radius }) for my 2d game and I need a collider for it, basically
It looks like there's features for this, but only for 3d?
I don't see why it wouldn't also work in 2d
I copy pasted the code and it seems to work fine, so it should be possible to implement for 2d as well. Might be a bit harder from a library perspective though
Maybe I can contribute a PR sometime for this
Yeah we could maybe support trimesh_from_mesh in 2D
Convex decomposition of 3D meshes into 2D probably isn't trivial though
https://github.com/dimforge/bevy_rapier/pull/428
As for regular polygon colliders, I think we could easily just add that as a built-in collider type
It's not built-in to Parry, but we could use custom shapes to support it since the support map (used by collision detection algorithms) for regular polygons is very simple
Do you already support arbitrary shapes into collision objects? Or is it still Lyon and doing your own thing?
I had a pretty good day yesterday, I had a bunch of people from Microsoft playing my game.
Not yet in 2D, but we could maybe do it for trimeshes
actually?
Honestly, I am not even kidding. Can I share it?
Yes
OK, so this is going to make no sense unless I take you back in time a little. But see this.
But a couple of years ago I made a joke that would really work in a Deadpool-movie.
This is the comment. 😄
I pitched it years ago on Twitter or whatever.
But the likes seem to be actual accounts.
if they're real accounts then that's really cool
This is almost as good as Reuben Bond from Microsoft working to set a time in Ponkatris.
And he did that yesterday.
I already have players from Microsoft playing the game, shaving off time introduced because the backend is in Microsoft Orleans.
We'll see when the movie comes out. God damn, I hope they do it.
The MS people mostly set time on this level: https://www.ponkatris.com/levels/2023-12-02 The old one, I probably shared.
But I think it is simple to see my issue with making new levels all the time.
The help I got will be super helpful, tho.
Are the links at the top supposed to work?
I guess in the future. 🙂 This is all a house of cards.
My 2d shapes keep exploding apart randomly
I've been trying to solve this for longer that I'd like to admit... Is there any way to force collider translations (i.e. manually changing Position) to take effect for the purpose of spatial queries while Time<Physics> is paused? For some context, I'm using raycasts for a 3d ship editor sort of thing, but would rather keep the physics paused in the background.
Just checking that I haven't missed something obvious
Does spatial_query.update_pipeline() help?
Probably won't update child colliders though because that requires transform propagation
That worked perfectly, thanks 🙂 There's no child colliders in the editor setup, just a few static colliders that I move around depending on which layer of a grid should be editable
Is there a built-in way to get the velocity at a specific point on a rigid body? Meaning factoring in angular velocity
I believe it's like this
In 2D:
lin_vel.0 + Vec2::new(-ang_vel.0 * point.y, ang_vel.0 * point.x);
In 3D:
lin_vel.0 + ang_vel.cross(point)
I guess the information for that is split across components
yeah if they were under Velocity component then you could have a helper
So not sure how you'd add that as a function somewhere... you could do AngularVelocity::velocity_at_point(&self, Vec3)
Then just add that to linear
Originally I felt like separate components are more ECS-like, and it'd also make it possible to have e.g. particles without rotational properties like angular velocity, but I'm open to combining them under Velocity if people prefer that
I was just thinking about that
Kinda reminds me of when Transform was originally Translation, Rotation, and Scale
Which was maybe a good idea in theory, but less usable in practice
That's not to say it's the exact same problem, tbh I find the way it works in bevy xpbd to be fine
Yeah I'll have to think about the API more, grouping things like linear/angular velocity and different mass properties would make some nicer APIs possible and also help with docs
The main cons are that it technically reduces parallelizability and not all entities need e.g. angular velocity, but in practise the difference is negligible
I'm trying to work my way through understanding the code in the 2D kinematic character example's kinematic_controller_collisions function. This (https://github.com/Jondolf/bevy_xpbd/blob/7bc9883251cf9a2a60776ebddf63ba38f5fef96b/crates/bevy_xpbd_2d/examples/kinematic_character_2d/plugin.rs#L297C2-L297C2) part in particular is tripping me up a bit:
// Get the rigid body entities of the colliders (colliders could be children)
let Ok([collider_parent1, collider_parent2]) =
collider_parents.get_many([contacts.entity1, contacts.entity2])
else {
continue;
};
What does it mean by "colliders could be children." I'm assuming that's why we're using the entity ID's from the ColliderParents instead of just using contacts.entity1 and 2 directly.
It just makes it so that if the character controller had child colliders (child entities that have a collider), those would be handled too
I think originally the code was different and doing this was more important but it's probably rare for character controllers to have child colliders
Okay, that makes sense. Thank you.
I had another question about it. For this line (https://github.com/Jondolf/bevy_xpbd/blob/7bc9883251cf9a2a60776ebddf63ba38f5fef96b/crates/bevy_xpbd_2d/examples/kinematic_character_2d/plugin.rs#L319), why does the rigid body being kinematic need to be verified? Wouldn't it always be kinematic if it's on an entity with a CharacterController?
IIRC the code mostly works for both kinematic and dynamic rigid bodies, so nothing stops you from making a CharacterControllerBundle with a dynamic body. There's also a dynamic version of that example in the repo
If there happened to be a CharacterController on a dynamic body, it could cause duplicate collisions
but you don't need to verify if you already know it's kinematic ofc
also fyi, the character controller examples are definitely not production-ready and have some issues, for a proper controller I recommend bevy_tnua
mostly they just showcase how to handle some common things like basic input and grounded checks
Does bevy_tnua have a kinematic body example? I could only find dynamic ones.
only dynamic yeah
Ah okay. Well, I'm only having trouble with the collision part anyway. In that case, are the examples here fine?
It should be fine for most things yeah
but beware of stuff like this
https://github.com/Jondolf/bevy_xpbd/issues/273
That's some beautiful jankiness right there. I might try fixing it if I ever get a grasp on collisions.
It's mostly because the kinematic body is treated like it has infinite mass, so in general it can't get pushed by anything
So if the cube gets pushed to clip the ground, it can't push the player up, so it's just stuck there getting pushed further to some point
until the player moves away
Interesting. Is there any way to allow dynamic rigid bodies to push back against the player?
I wonder how Godot's KinematicBody deals with this, or if they even do.
The collision code kinda does that but not very well
I believe character controllers usually use raycasts or shapecasts for collisions instead of actual colliders
and try to maintain a small margin (extra space) between the character and its environment
like offset in rapier
https://rapier.rs/docs/user_guides/bevy_plugin/character_controller#character-offset
it's mostly for numerical stability though
for example when walking over two cubes, you shouldn't hit the edge between them
Oh, so have the player hover just a bit?
yeah
Would you happen to know why my character is vibrating? I modified the collidision code a bit to fit my uses:
collision system should run here instead of Update
Remarks that violate the community standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner).
Mod hat: This is both off-topic and in violation of the rules. Don't do it again.
Perfect, thank you 🙂
I just tried this and it also seems to have the same issue for me. Hm.
You solving all my problems with this idea ty
Eyyy glad to be of help 😄
I'm running into some weirdness with different tick rates
128 hz with 1 velocity will move faster than a 32hz one
which shouldn't be happening
the higher the hz the faster the speed
there was an issue with that but I thought I fixed it
lemme try updating then
are you running in PostUpdate or somewhere else?
FixedUpdate with
let tick_hz = 300.0;
app.insert_resource(Time::<Fixed>::from_hz(tick_hz))
.insert_resource(Time::<Physics>::new_with(Physics::fixed_once_hz(tick_hz)));
it was in 0.3.0 but should be fixed in 0.3.1 and 0.3.2 (or so I thought)
Does 60.0 Hz for fixed_once_hz work?
but 300.0 Hz for Time<Fixed>
still seem to have the issue on 0.3.2, I'll check
yeah that seems to work but that's a bit weird
at least, it is moving at the same rate as I modify the Time::<Physics> hz
but I'd expect the opposite here
huh
this is the only thing that changed/fixed scheduling since 0.3.2 I believe
https://github.com/Jondolf/bevy_xpbd/pull/276
I think that'd be the issue then
makes sense to me at least
I was adding the Time::<Physics> before I added the plugin
so it was probably overwriting my fixed hz with the default
ya, works perfectly now 🙂
Just to clarify, does SubstepSchedule run before the next frame is rendered, so you can't see the object being moved out of the colliding object?
And the reason you can with Update is because the frame is being rendered while the object is simaltaneously moved out of the colliding object?
I believe rendering runs independently of app scheduling in a separate SubApp.
SubstepSchedule runs in a loop in PhysicsSchedule which by default runs in PostUpdate.
Collision detection and the solver are run in the substepping loop, so if you instead run in Update (or elsewhere outside SubstepSchedule), you wont handle all collisions, which can cause jitteriness like what you experienced. There could be other factors that contribute to it too though
Dang, that's much more confusing than I assumed.
Physics engines typically use iterative solvers to be able to handle multiple simultaneous collisions or other interactions in a stable way. This just means running some parts of physics multiple times a frame
and substepping is a form of this iteration
which the SubstepSchedule handles
Does bevy_xpbd have physics interpolation, or something similar, so physics running at a lower framerate than the monitor won't look choppy?
@vestal minnow thanks for making the xpbd plugin! I got my jam entry done early! (https://craghammer.itch.io/queen-beevy) The last jam I participated in was spent slamming my head against rapier and losing 😢
I suppose a more apt question would be, how does bevy_xpbd keep the physics looking smooth at high framerates?
There's a 3rd party plugin for this but it requires using separate entities for the rendered mesh and the actual physics object
https://github.com/rubengrim/bevy_xpbd_interp
Increasing the timestep might also help:
// Default is 60 Hz
app.insert_resource(Time::new_with(Physics::fixed_hz(144.0)))
Note that this might impact performance a bit because it's running physics more frequently. You can lower SubstepCount to gain back some performance, but lower values can make the simulation a bit less accurate
Thank you!
I went the lazy way and set the physics time step to variable
yeah that works too, just makes it frame rate dependent
I don't love variable because it isn't great for networking or consistency
physics will also more likely explode with variable
e.g. if you move a window it'll stutter the program
and if you don't have a good cap on it then it'll explode because the delta is high
You can set a cap with xpbd and luckily my game isn't very focused on physics so it should be fine for me
Is it possible interpolation will ever be in the main plugin?
hopefully yes
not entirely sure how it'd work though, haven't looked into it much yet
ideally it would Just Work™ with some setting without requiring separate entity shenanigans
I believe it should be possible though, especially because there are separate physics Position and Rotation components that can work independently of visual Transforms
just need to keep it possible for users to also use Transforms directly for most physics stuff
Awesome 🙂
BTW, I solved that problem I had with moving the objects with the mouse by converting to world space and tracking the deltas. If anyone is interested in that code, I am happy to share it. But it is strange that it was hard to just use the mouse deltas as some function of the camera transform. I feel the code I ended up with so far is a little clunky, Sorry, again a little off-topic, but might be relevant to some previous discussion.
Working on an ECS-ified joint and constraint rework, going pretty smoothly 👀
Joints are split into components like ConstraintEntities, JointDamping, JointAnchors, and then the actual joint type like DistanceJoint. Previously, each joint stored all of this in its own massive struct that contained several properties that are only important for outside usage.
This required a bunch of annoying trait methods and impls, and some systems had to include generics like joint_damping::<DistanceJoint>. With the new component approach, none of that is required and everything is much more reusable and composable! In the future we should also be able to neatly add e.g. joint motors, joint breakage and so on via components without having to handle each joint separately.
API-wise, it might even look nicer and more Bevy-like than the old one that required builders (because the actual struct was massive):
// Before
commands.spawn(
DistanceJoint::new(entity1, entity2)
.with_local_anchor_1(Vec2::NEG_Y)
.with_local_anchor_2(Vec2::ONE)
.with_rest_length(1.5)
.with_limits(0.75, 2.5),
);
// After (WIP)
commands.spawn(JointBundle {
entities: [entity1, entity2].into(),
joint: DistanceJoint::new(1.5).limits(0.75, 2.5),
anchors: JointAnchors::new(Vec2::NEG_Y, Vec2::ONE),
..default()
});
This also removes a ton of constraint code duplication that has been haunting me for a while
Need to update docs and examples but otherwise PR should be ready soon
Also I've locally implemented collision disabling for joints (#274), it basically requires this component approach because otherwise we'd have to run the same system separately for each joint type (because generics were previously needed) and iterate over collisions an unnecessary amount
I am spawning a SceneBundle with a scale of 0.2. This has a children entity with a ball collider. The issue is that, on spawn, the collider is enormous, probably because it does not get the scale. You can view it in the debugger, and also because it also causes collisions. Then, after a frame, it properly resizes and works as intended. Am I doing something wrong?
How would I do something like this for the colliders?
Disregard the code at the bottom, it is just "for fun", but it seems to change the scale for a frame or something, then it reverts.
Is there anything like the sprite-code to do something like this?
Should I grab it back as a Shape and set a new one?
Hi, I'm curious about the broadphase collision detection optomizations in bevy_xbpd. Do you use a bounding volume hierarchy like rapier does? I think parry provides some utilities for it, but from what I understand, the responsibility still lies on the developer to implement the details, and I'm wondering if those broadphase collision sweeps are implemented into bexy_xbpd like they are for rapier
Rapier doesn't use a BVH for it's broad phase anymore. It uses a Hierarchical Sweep and Prune which basically does SAP on several differently sized grids. bevy_xpbd has a very basic SAP implementation, but it could definitely be improved.
(SAP basically sorts bodies on an axis and uses their min/max extents to filter out impossible collisions)
Any idea why it doesn't use a BVH anymore? I think bepu uses a BVH so I can't imagine it's a slow approach
Here's a link to the broad phase for reference; rapier also has a BVH version but it's commented out in the parent module and not exported
https://github.com/dimforge/rapier/blob/master/src/geometry/broad_phase_multi_sap/broad_phase.rs
Not sure, I guess they found it to be faster for Rapier? idk
I mean I guess I can kind of see that when you have a broken bvh 
yeah 😂
I don't have much to compare it against, but I was honestly surprised just how fast bevy_xpbd's SAP is
yeah based on tracy I think it barely takes any frame time in most cases
solver is mostly the issue
- the large amount of
query.get_many(...)
Depends a bit on your usecase too. Having 10k entities that don't overlap doesn't run super great, but if you have less entities but they're all touching it won't be the bottleneck
Biggest optimization that could be done would be if the things looking for collisions are only the non-sleeping dynamic/kinematic bodies and sensors. Chances are if you have 10k entities that don't touch they're all asleep
I think we could do some things slightly more efficiently with sequential impulses https://discord.com/channels/691052431525675048/1184646007317860512
not sure tho, need to test more
Yeah, once they start overlapping a lot even the broadphase starts to crawl. The early out when X has passed over the limit is doing a lot of the heavy lifting until things start overlapping
Are there any examples of changing the collider size? I have a version that compiles and runs here, but "nothing happens". This is the basic code:
if let Some(cuboid) = cuboid_shape {
info!("Cuboid");
let new_shape = SharedShape::cuboid(
cuboid.half_extents.x + 100.0,
cuboid.half_extents.y,
);
collider.set_shape(new_shape);
} else {
info!("Not a cuboid");
}
Hmm, Collider contains an "unscaled" shape and a scaled shape (that takes transform scale into account). Maybe set_shape doesn't trigger change detection for updating the scaled version?
You could do something like collder.set_scale(Vec2::ONE * 1.001, 16) in addition to what you're doing now, just to see if it's a change detection issue
Adding collider.set_scale(Vec2::ONE * 1.001, 16); makes it update!
Yeah, that's a bug then, set_shape should just work; I'll try to fix this later today
Great, thanks for the quick reply!
Couple questions wrt sensors:
-
Is there a way to filter shapecasts to ignore sensor collisions? From what I see I could filter by the sensor entity, but I don't have access to that at that point.
I tried filtering out according to the hit entity if it has a sensor attached, but that then removed every shape hit. Are sensors attached to more things than I expect them to be on? -
Is there a reason why colliders made from TriMesh or ConvexHull asyncscenecolliders seem to show up in a query like: Query<&ColliderParent, Without<Sensor>>? Directly creating the collider by hand seems to work as I'd expect.
-
Not yet, but I can try to add flags for this like the ones in Rapier's query filters. You could add some sensor collision layer for the sensors and filter by that for now. Sensors are only on entities you add them to.
-
Bevy scenes loaded from e.g. glTFs create multiple entities, each with their own meshes and transforms.
AsyncSceneCollidercreates colliders for these entities, but doesn't attach any other configuration by default. If you have aSensoron the root/parent entity, it only applies to that entity, not the child entities.
AsyncSceneCollider has some options for specifying the layers or densities of the spawned entities, but no such option for "sensorness" yet. I could add that if it'd be useful? Ideally, we'd have something like bevy_scene_hook built into Bevy for this though
I see, thanks for the fast reply!
For 2: Just to make sure, the children created are attached to the same parent? So if the mesh creates two children, they both have a collider on their own, but their ColliderParent would still be the same? I.e. it would be possible to iterate over the collision children and add a sensor if the parent has one?
Yeah, they should have the same ancestor/parent, but they could be nested deeper depending on your scene hierarchy. The ColliderParent should always be the same for all of them though, i.e. the entity of the rigid body that they affect.
Perfect, I think that'll work for my case then. scene hook would indeed be the cleanest way though.
Though if there were a way to pass arbitrary components that should be attached to every child made through asyncscenecollider that'd also work very well.
Not sure if that'd make much sense tho in a general
I think arbitrary components would require us to wrap it in a Box or something since it needs to be stored in AsyncSceneCollider and the size and type is unknown
could be viable though
idk I might test if that works, I'm not a huge fan of the current approach that we use
Didn't have time yesterday but this should be fixed now (#278)
(once CI compiles and I hit merge)
Awesome! 🙂
Just to be clear, this isn't supposed to work in 0.3.2, right?
Nah, just on main branch
Does xpbd have a collider similar to Rapier's heightfield?
same as Rapier, Collider::heightfield
https://docs.rs/bevy_xpbd_3d/0.3.2/bevy_xpbd_3d/components/struct.Collider.html#method.heightfield
A collider used for detecting collisions and generating contacts.
I should read the docs before asking 🙂
I like asking before I read the docs. You make more frenemies that way.
Also, I think I might be able to contribute to docs in the future. But I still feel like that toddler in that movie 2001.
Sorry, wrong channel!
hey, I want to make a fluid simulation that interacts with xpbd, short term I will just make fluids that flow over colliders, but long term I also want the interaction the other way around (like drag inside a fluid), if anyone has any advice (specifically for the long term goal) let me know!
Depends on your specific requirements. I personally haven't tried fluid sim yet, but I made a response to an issue about it here in case you haven't seen it. It mostly just covers some common approaches and problems that I know of
My response was way too thorough for what the issue was made for 🙈 I felt kinda bad after lol
got nerd sniped real hard
It's fine, it's just 1 screen of text per word in the issue 👀
Yeah 😂 I knew the person who made the issue didn't require anything super thorough and broad like that, but for an issue titled "Fluid Simulation" I felt like I needed to give some proper answer that covers different approaches, first steps, etc., mainly for people wanting to see if we support fluid sim
Not entirely sure what you mean by "fluids that flow over colliders". My general advice here would be to figure out the requirements and then see if you can somehow achieve those requirements without actually using fluid simulation
@vestal minnow btw the fixed grid thing (in your gh link) definitely isn't an issue
it might be somewhat a problem on the GPU but a basic hashmap of grids would be able to fix it
also I'm implementing a bevy gpu sph fluid sim although it's a bit opiniated
Ooh that sounds exciting 👀
hmm, I'll see if I can find the fixed grid thing
I think it was specifically FLIP
but I'd expect a hashmap to work yeah
there's also things like SPGrid
although I think that's eulerian?
and whatever complex magic taichi does
https://taichi.graphics/ python dsl that allows for writing spatial computing
Taichi is a domain-specific language embedded in Python that helps you easily write portable, high-performance parallel programs.
Also:
Getting SPH to not randomly just explode is surprisingly hard
although it's kinda neat how you can just like
randomly staple one pressure solver to a surface tension by another person and viscosity bu a third author and it generally works
and then you touch it wrong and it explodes
I think I got the fixed grid thing from this
https://youtu.be/i4KWiq3guRU?si=IAQgdAw2gk0aETGo&t=70
❤️ Check out Weights & Biases and sign up for a free demo here: https://www.wandb.com/papers
The shown blog post is available here:
https://www.wandb.com/articles/visualize-lightgbm-performance-in-one-line-of-code
📝 The paper "Fast Fluid Simulations with Sparse Volumes on the GPU" and some code samples are available here:
the vid shows FLIP sim clips
but maybe the sparse approach is algorithm agnostic, idk
its basically algorithm agnostic
there's no reason you can't just splat a hashmap onto it
yeah
Excited to see your sim in action though if/when you release it
Running on GPU is also really cool, I'd like to try some GPU stuff eventually
maybe something like NVIDIA's FleX 🤔
Although particles for rigid bodies is annoying, but maybe they could still be modeled as "normal" rigid bodies somehow
@vestal minnow you don't need to simulate the rigid bodies using particles
just like put particles on the edges
but if you wnat it to be easily parallelizable using gpu you kinda need some sort of discretization. I guess you could use voxels but eh
@vestal minnow If I was going to create a moving platform where my character is kinematic
could I keep my moving platform static
and then change the position of it
then I would of course do things to find the players riding it
so they follow it correctly
but would I be breaking anything by directly changing the position
(I ask this because in Rapier you can't but upon some light testing it seems to work?)
You can, and I think it should work normally, but is there a reason it shouldn't be kinematic instead?
Wouldnt I have to handle the collisions for it then?
Kinematic bodies affect dynamic bodies normally but they themselves aren't affected by collisions by default. The same thing is true for static bodies
Kinematic bodies are basically just static bodies that can move and have velocity
And semantically, static bodies aren't meant to move, but for now you could do it if you wanted to (idk if we could later do some optimisations by disallowing it tho)
So I'm not sure what the difference would be if you were to use a kinematic body instead
Alright 👍 and again you can use static if you want to but I think kinematic just feels more "correct" for a moving platform
how would i query for one face of a 3d mesh containing the entire face of another 3d mesh? i see the Aabb.contains method but i'm missing how to get the bounding boxes for individual mesh faces
figured it out, just scaled the y component of the bounding boxes to 0 and then used Aabb.contains
would it make sense to add a AsyncSceneCollider::from_collider method so non computed colliders can be added to scenes? i was able to copy the system for computed colliders but perhaps it should be added to the api? i was thinking of adding a scene collider enum with computed and non-computed
I am writing a ground checker and I can get it to find the ground
but it does not detect walls at all
Why is this exactly?
Are you using shapecasts?
Yes I am
So you're trying to cast a shape at a wall but it's not returning any hits?
Yeah but does it cast the shape at the wall and not directly downwards like for ground checks
Huh, why's the arrow there 🤔
I'll try to make a super quick repro of this
want me to send what I have?
Yeah that'd be nice
Can your character jump? I think the issue could actually be that the shapecaster only gets the closest hit by default, and when it's hitting both the ground and the wall, it just always chooses the ground
for ShapeCaster, you can use with_max_hits to allow more hits
it's 1 by default because shapecasts can be quite expensive with many hits and I'm not sure what a sane default would otherwise be
Oooh I see that makes sense
I think 1 is probably sane for a default
I think what got me is that in the example for the character it’s looping and one thinks it probably has more then one in the data structure lol
thank you
What about the arrow thing
It's correct when the shape isn't intersecting at the origin (i.e. the time of impact is greater than zero), but I'm not sure why that is yet
maybe parry doesn't compute the hit points properly for the origin intersection case
or maybe the debug rendering logic just does something weird, I'll test
Do you mean that you could manually specify the collider used for meshes in a scene instead of always computing it from the mesh automatically?
Something like this:
// Every mesh in the scene will have a cuboid collider
AsyncSceneCollider::from_collider(Collider::cuboid(1.0, 1.0))
// Every mesh in the scene will have an automatically computed
// trimesh collider except the "Statue" mesh that has a cuboid
AsyncSceneCollider::new(Some(ComputedCollider::TriMesh))
.with_collider("Statue", Collider::cuboid(1.0, 2.5))
So adding with_max_hits
didnt seem to help
ShapeHits { vector:
[ShapeHitData { entity: 2v0, time_of_impact: 0.0, point1: Vec2(400.0, -45.0), point2: Vec2(15.347168, 133.99568), normal1: Vec2(-0.0, 1.0), normal2: Vec2(0.0, -1.0) }, ShapeHitData { entity: 1v0, time_of_impact: 0.0, point1: Vec2(0.0, -200.0), point2: Vec2(-384.65283, -21.004318), normal1: Vec2(-0.0, 1.0), normal2: Vec2(0.0, -1.0) }
],
count: 2
}
It seems to have the wall in its shapehits
but the normal is very wrong
yes exactly so "Statue" only gets a super basic physics hitbox
interesting, even with only the wall colliding it still has a normal of (0.,-1.0)
When I get in the corner I can get the normal to be: [-0.000008588889, -1]
I suppose this could be happening because the the impact on the y-axis occurs first?
yeah something must be wrong
I know my ShapeHits has two items in it like I showed in the last screenshot
I suppose the issue is that the direction is going downwards
Hm the ShapeCaster its self needs some work
One issue is that if I wanted to run multiple casts
I am unable to atm because I would need to create a new entity
can't you use child entities for that
Is it possible to turn off collision for a collider in bevy_xpbd, like turning off visibility for a sprite? Without just removing it from the entity?
If you're fine with it still getting collision events, you could give it the Sensor component to make it pass through everything. But there's no component like ColliderDisabled, although it should be very easy to add if it'd be useful
Hey thanks! I’m really enjoying the crate by the way, great work! My use case is that I have an attack that I don’t want to spawn in, and so I just have it as a child of the attacking mob and make it visible when the attack actually happens. I want the same thing for the collider attached to it too, so that I can just get the collisions while the attack is occurring.
Yeah I think that's a good example of a use case where a ColliderDisabled component could be useful; I'll try to add it next week.
For now, a manual workaround could be to have a collision layer for active/disabled colliders and to change the collision layers during the attack
Awesome, thanks!
@vestal minnow For two way platforms I was thinking of using sensors
however I noticed you had an example of them in the 2d_examples
however you say that your example is not "robust"
what other methods could you think of doing two way platforms
there are sensors
maybe using some ColliderDisabled component when that gets added
@prime shadow I didn't make that example, so I'm not entirely sure why it wouldn't be robust. I can't think of a nicer approach right now, maybe there are existing tutorials or other resources somewhere?
Sensors or ColliderDisabled wouldn't really work since it'd allow everything to pass through when the collider is in a pass-through state, not just the player
Also ColliderDisabled wouldn't work because you need to detect when the player exits the platform's collider
Hi, why the little flag in the video bounces and flies away?
// flag
Collider::convex_hull(vec![
Vec2::new(-0.5, 0.) * size,
Vec2::new(0.5, 0.) * size,
Vec2::new(0.5, 1.) * size,
Vec2::new(-0.5, 1.) * size,
])
.unwrap(),
RigidBody::Dynamic,
Friction {
dynamic_coefficient: 0.5,
static_coefficient: 0.5,
..Default::default()
},
Mass(100.),
// the ground
collider.insert((
Collider::cuboid(aabb.width(), aabb.height()),
RigidBody::Static,
));
if let Some(coe) = physics.frictions.as_ref().and_then(|f| f.get(&i)) {
collider.insert(Friction {
dynamic_coefficient: *coe,
static_coefficient: *coe,
..Default::default()
});
}
and the gravity: ```rust
Gravity(Vec2::new(0., -98.))
Hmm, it looks like the flag is initially going through the ground and detects the collision super late
Maybe try with the flag as a Collider::cuboid? Just to see if that works
No it doesn't.
That's really weird
Is there a way to get global space normal from a shapecast hit instead of local. I'm trying to get kinematic bodies to slide against other colliders but the rotation of the entity makes the normal wrong
Could you insert the PhysicsDebugConfig::all() resource so that it shows all debug info including AABBs?
let global_normal = transform.rotation * local_normal;
But i would need a way to get the collider that was hits rotation
I dont this there a way to get components from entity ids
Even after I put the flag much higher, the problem still exists.
I think you can also get the negated version of the normal on the shape that you're casting
// Or if you're using ShapeCaster, you'd just query ShapeHits
let hit = spatial_query.cast_shape(&shape, pos, rot, ...).unwrap();
let global_normal = rot * -hit.normal2;
Not entirely sure if this will always match the normal on the collider that was hit, but I'm pretty sure it should.
To get the rotation of the collider that was hit, you can just have another query in your system
fn my_system(q: Query<&Transform, With<Collider>>, spatial: SpatialQuery) {
// ...
let transform = q.get(hit.entity).unwrap();
}
I haven't seen this issue before so I'm a bit confused
My guess would be mass or scale related somehow
But it's weird that it's just going through the ground for a couple of frames
Does it detect the collision at the initial ground intersection even though it does nothing? Could check e.g. CollidingEntities for that
or print Collision events
and maybe lower gravity to make it slower
The collision event appears after the flag sinks into the ground for many frames. The gravity is changed to -9.8 in the video.
Could you see if you can make a minimal reproduction? I have no idea why it wouldn't detect the collision in that case
unless there's some negative scaling or something, haven't tested that though
Ok I will try that. But this is an example in a crate, so if you don't mind to read extra code, you can go to the github and clone it. I'm not sure if a reproduction is possible. Please give me some time.
cargo run --example ldtk --features="debug serializing physics_xpbd" in the dev branch btw
In serializing/ldtk/layers.rs at line 196, I instantiated the colliders for grounds and walls.
That worked thanks
Just wrote a similar demo and it works pretty fine. That's really weird
https://pastebin.com/CLUgetPT
Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.
What about layers
Have rays that sit only one the two way platform layer and make the player go off the two way platforms layer when they need to ignore the platform
(mb, posted in wrong channel)
Is there a way to make kinematic bodies behave sort of like dynamic bodies when it comes to interacting with other other kinematic bodies and static bodies? At the moment there are no physics interactions between kinematic bodies and between kinematic and static bodies. I the kinematic bodies to slide off walls and other bodies while not being affected by external forces.
Sort of like this bu without the rotations
A kinematic body would just go through instead
There's a kinematic character controller example in the repo that has a system for handling collisions for kinematic bodies. It's kind of a naive way of handling it, but should work for most cases
Just make sure to add the system to the SubstepSchedule as shown in the example
@fiery mortar Re books: I haven't read books on physics engines but I've heard that this is decent
https://www.amazon.com/gp/product/0123819768/ref=as_li_ss_tl?ie=UTF8&linkCode=sl1&tag=haroldserrano-20&linkId=56751a95680e54b77828297329e548d9&language=en_US
And for collision detection, this is the most popular and comprehensive one afaik
https://www.amazon.com/gp/product/1558607323/ref=as_li_ss_tl?ie=UTF8&psc=1&linkCode=sl1&tag=haroldserrano-20&linkId=e775edabb16bb41e92f9d6559301fb55&language=en_US
There are good online resources too though, Erin Catto (author of Box2D) has a lot of good slides
Wow thanks, question wasn't really appropriate for this channel so I removed it, thanks for replying anyway
For XPBD specifically, I started with Johan Helsing's tutorial and continued with the XPBD papers and taking reference from other engines
Is this about https://github.com/Jondolf/bevy_xpbd/blob/main/crates/bevy_xpbd_2d/examples/one_way_platform_2d.rs ?
If so, one issue with that example is if you have a bunch of boxes in a line to form a floor, it can fall through because of I assume float inaccuracies: when going from one collider to the following one, it sometimes detects that you hit it sideways instead of from the top so it cancels the collision. You can get around that by merging the colliders into one long wide one.
That behaviour improved with one of the latest versions of xpbd, so it’s possible that it’s been completely resolved by changes in the underlying engine. If so, it may be good enough as-is.
I’m using the example code as-is in a relatively simple run-and-gun platformer but have always been merging colliders so I’m not sure whether it’s still an issue or not, tbh
This project is so cool, any idea on how much work is needed to get joint motors working?
Thanks!
I made a basic WIP version of them a while ago, but iirc it currently doesn't quite work in some cases where the motors are driving several different degrees of freedom. I'm also doing some other joint reworks that I'd like to finish first. It's planned though, should be possible with XPBD
Here's a basic functioning motor for a revolute joint
ah okay I alway have colliders merged so I guess I don’t have to worry
Hi, I hope you're having a nice holiday if you celebrate. I tried using collision layers and something weird is happening: In the below code, it never asks for collision_layers to be mutable (I had it all marked mutable before removing it to show), and it doesn't seem to have any effect when running. Any idea what's going on? I'm very new, so I'm sure I just missed something obvious. Thanks and cheers!
pub fn collision_damage(
p_query: Query<(Entity, &CollidingEntities, &DamageEffect, &DespawnsOnCollision, &CollisionLayers), With<Projectile>>,
mut despawn_event_writer: EventWriter<Despawn>, // despawn the projectile
mut damage_event_writer: EventWriter<Damage>, // and apply its damage effect to the other entity.
) {
for (
projectile,
colliding_entities,
damage_effect,
DespawnsOnCollision(despawns),
collision_layers
) in p_query.iter() {
if !colliding_entities.is_empty() {
// deactivate the collider on the projectile
collision_layers.remove_group(Layer::Projectile);
collision_layers.add_group(Layer::Inactive);
// if the projectile despawns on collision, mark it for despawn
if *despawns {
despawn_event_writer.send(Despawn(projectile));
}
// trigger the damage event on the colliding entities
for entity in colliding_entities.iter() {
damage_event_writer.send(Damage { entity: *entity, damage_effect: *damage_effect });
}
}
}
}
This is a very unfortunate combo of add_group taking self by value and the type being Copy.
*collision_layers = collision_layers
.remove_group(Layer::Projectile)
.add_group(Layer::Inactive);
This is how you need to use the API, but you'll also need to change the query to ask for &mut CollisionLayers and use iter_mut instead
@vestal minnow I'd recommend marking these methods on CollisionLayers as #[must_use]
Ah thanks! I'm not sure I really understand how it happens but is it that it is just silently copying it and then adding/removing on the copy?
With that change it works, thanks!
Ah, good point, thanks. I think ideally the methods would just mutate the original value though instead of returning a copy
But then I believe the methods would need to return a &mut, which means you can't use them when initially creating the component unless you move it onto a separate line
i.e. you wouldn't be able to do this
commands.spawn((
// ...
CollisionLayers::default().add_group(...),
));
I suppose there could be a with_group when initializing and an add_group when mutably adding layers later
That's how bevy does children right?
IIRC this is very close to how the API was in 0.1, but I later changed it to be closer to how systems, plugins, etc. are added in Bevy 😅 might change it back, we'll see
yeah it's close
but with_children takes a closure with a builder
By the way, I think just using an "inactive" collision layer was a perfectly fine solution for "disabling" a collider, so I'm not sure it's really worth adding another way. Thanks for the suggestion! And the crate 🙂
This might be one of those obscure Nix errors, but anyone know why this is happening:
> Checking bevy_xpbd_2d v0.3.2 (https://github.com/Jondolf/bevy_xpbd.git#db224138)
> error: couldn't read /nix/store/zg2avqgns1hl5hmjmgyb0jn3bdcdskf0-vendor-cargo-deps/583744e0486b5f34e5bf97e225074f254339834801fd96c4fa627719ab14eb1d/bevy_xpbd_2d-0.3.2/../../src/lib.rs: No such file or directory (os error 2)
The error only happens on bevy_xpbd_2d's check, but not in the others for some reason.
Alright, I can now reproduce the issue in a non-Nix environment. It looks like that bevy_xpbd's workspace relative paths breaks when you use cargo vendor. To reproduce:
- Run
cargo vendorin a project that depends onbevy_xpbd(I'm using the git dependency, not sure if it matters) - Follow the instructions in the end of the output and modify your
.cargo/config.tomlaccordingly - Run
cargo check
The check will fail onbevy_xpbdwith errors like these:
error[E0433]: failed to resolve: use of undeclared crate or module `bevy_mod_picking`
--> /home/$USER/Documents/rust/bevy/rope_test/vendor/bevy_xpbd_2d/../../src/pointer.rs:3:5
|
3 | use bevy_mod_picking::{
| ^^^^^^^^^^^^^^^^ use of undeclared crate or module `bevy_mod_picking`
error[E0433]: failed to resolve: use of undeclared crate or module `bevy_mod_picking`
--> /home/$USER/Documents/rust/bevy/rope_test/vendor/bevy_xpbd_2d/../../src/retro.rs:14:5
|
14 | use bevy_mod_picking::{picking_core::PickSet, pointer::InputMove};
| ^^^^^^^^^^^^^^^^ use of undeclared crate or module `bevy_mod_picking`
(truncated)
I'm (optimistically) guessing that this is not a cargo bug, but I'm not sure how you'd fix this.
Never mind, it looks like I'm not reading the output correctly 🤔. It looks like it's a lot more broken that I thought.
Hey, guys! I am messing around with a small level in my game, and I am seeing upping the substeps makes it make sensible. What's a good substepcount? I added it to 30 without it fixing my issues, but upping it to 130 seems to make it consistent. Does 130 substeps mean that it does 130 times as much processing?
What issues are you having? I'd imagine 30 to be enough, 130 feels very excessive
I can show you quickly, I already made a video.
Well, I am about to with the parameters I am talking about.
130 substeps means that it runs integration (moving bodies based on velocity and forces), narrow phase collision detection, and the solver (collision response etc.) 130 times per physics frame. Some things like broad phase collision detection (a "rough" collision check for optimization) are still run just once per frame
but yeah, having more substeps corresponds to more processing
This is without substeps at all. You can see the ball running over the things and leaving some of them behind.
at 12 seconds.
And it seems super odd, I am probably doing something wrong here, because the collision object is over it for multiple frames.
And again at 50 seconds-ish.
Yeah the one at 12 seconds is very odd, it should definitely be colliding with it based on the video
But I have something here I can reproduce, let me make a video with 30 substeps.
This thing.
There is something seriously wrong here, it must be in my code.
How are you despawning them on collision?
Well, this is my crystal despawner:
pub fn crystal_despawner(
mut commands: Commands,
asset_server: Res<AssetServer>,
players: Query<&Player>,
crystals: Query<&Crystal>,
last_crystals: Query<&LastCrystal>,
mut ev_collision: EventReader<CollisionStarted>,
mut ev_crystal_collected: EventWriter<CrystalCollected>,
score: ResMut<Score>,
audio_state: Res<AudioState>,
crystal_count: Res<CrystalCount>,
) {
for CollisionStarted(e1, e2) in ev_collision.read() {
let (_player, crystal) = if players.contains(*e1) && crystals.contains(*e2) {
(*e1, *e2)
} else if players.contains(*e2) && crystals.contains(*e1) {
(*e2, *e1)
} else {
return;
};
let mut play_audio = false;
if last_crystals.contains(crystal) {
if score.0 == crystal_count.0 - 1 {
info!("Last crystal collected!");
commands.entity(crystal).despawn();
play_audio = true;
ev_crystal_collected.send(CrystalCollected);
}
info!("Last crystal!");
} else {
info!("Normal crystal!");
commands.entity(crystal).despawn();
ev_crystal_collected.send(CrystalCollected);
play_audio = true;
}
if play_audio == true && audio_state.play_sound_effects == true {
info!("Playing crystal capture sound");
// Play sound
commands.spawn(AudioBundle {
source: audio_state.pickup.clone().into(),
..default()
});
}
}
}
Sorry, all the code is messy. I am a noob still and will be for another year or so.
The code looks fine based on a quick look, not sure what's going wrong here
I guess it could be some weird edge case where CollisionStarted doesn't always fire
Well, if it is helpful, it is simple to reproduce in this codebase.
yeah
Would you want to look at it? I think you already have access to the repo, I have been yapping here constantly for 5 months now. 🙂
And thanks for indulging me.
I have it running on web also, I can make it reproducible there also, I think.
But it feels like it should be working without substeps, right?
Also, having two crystal accidentally stacked in the same place should be OK?
I am not trying to remake pacman, it is a test level.
Sorry for not responding. I'll look into it more deeply later, spending Christmas with family atm
Get your priorities straight! 😄 No, just kidding, have a great and peaceful Christmas and thanks for all the help so far. 🙂 This is no rush at all.
I noticed CollisionStarted and CollisionEnded are separate events. This could be problematic if it's possible to get both events for the same entities in one frame.
I'm pretty sure it can only send one of them during a single (physics) frame
They're sent based on whether a collision is/was marked as active in the Collisions resource
I feel like the event logic is currently unnecessarily complex though so I'd like to clean it up eventually
It appears that when you add RigidBody or Collider to a component, it updates the Transform, even if the SyncPlugin is disabled..
Is there any way around this?
This is probably the PreparePlugin initializing positions when creating rigid bodies or colliders. It might currently overwrite the Transform even when it shouldn't change, we should fix that if that is the case
I'm on mobile rn so it's hard to read the code
How would it know when it should change it or not?
I guess it could check the SyncConfig values, but that's in a different plugin
This definitely seems like the place
I tried just disabling PreparePlugin but it panics 😅
fwiw I sort of worked around it by adding a Collider::cuboid(0.0, 0.0, 0.0) but of course I get a flood of warnings since they're all in the same position (I'm trying to integrate big_space so they're all technically at 0, 0, 0)
Since it only checks if Added<Collider> not on startup
If Position exists already, rewrite Transform based on it, otherwise initialize Position based on Transform (without reinserting the Transform, but I'd guess that it currently always reinserts the Transform)