#Avian Physics
1 messages · Page 27 of 1
I'm not too familiar with how boids systems are typically implemented, but based on a quick look, spatial hashing (grids) and quadtrees seem pretty common? A BVH might be fine too though, could always start with that if it's the easiest and see if you end up needing something more specialized
There's also bevy_spatial for nearest neighbor lookups
I see a lot of quadtree/octrees/kdtrees
I have no idea what the performance implications are for choosing one over another.
By state-based logic, do you mean components that are automatically added or removed depending on some state? I can't really think of anything other than Sleeping
There will likely be some more that are temporarily added and removed within the same frame though. For example, I'm adding a RecomputeMassProperties component that is added to queue entities for mass recomputation, and removed once it's done. Forces and impulses might also work similarly in the future, with components that are inserted and later removed after applying them, if not applied every frame.
If the question is in the context of "what components should I roll back for networking?" there's an issue that lists some
https://github.com/Jondolf/avian/issues/478
i've seen a really cool one using voronoi, but it didn't account for any other obstacles
After updating my FPS game to bevy 0.15 and avian3d main stuff generally works, but shortly after I start moving the camera with the mouse my camera starts rotating wildly and shortly after it crashes with this:
Error: The vector given to `Dir3::new_unchecked` is not normalized. The length is 1.011216.
stack backtrace:
2: bevy_math::direction::assert_is_normalized
at /home/kris/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_math-0.15.0/src/direction.rs:63:9
3: bevy_math::direction::Dir3::new_unchecked
at /home/kris/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_math-0.15.0/src/direction.rs:402:9
4: <avian3d::position::Rotation as core::ops::arith::Mul<bevy_math::direction::Dir3>>::mul
at /home/kris/.cargo/git/checkouts/avian-b0a258754c9e72b0/d5b9634/crates/avian3d/../../src/position.rs:728:9
5: avian3d::spatial_query::update_shape_caster_positions
at /home/kris/.cargo/git/checkouts/avian-b0a258754c9e72b0/d5b9634/crates/avian3d/../../src/spatial_query/mod.rs:319:36
Encountered a panic in system `avian3d::spatial_query::update_shape_caster_positions`!
Encountered a panic in system `avian3d::schedule::run_physics_schedule`!
Encountered a panic in system `bevy_app::main_schedule::FixedMain::run_fixed_main`!
Encountered a panic in system `bevy_time::fixed::run_fixed_main_schedule`!
Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!
My character controller is a bit janky but it worked with 0.14.2 and avian 0.1.2. It looks like the same issue @formal galleon had a couple of days ago.
A minimal reproduction would be nice
I haven't gotten the panic in months :p
makes sense thanks
I'll see if I can extract something.
Did you decide if you wanted to do a small release first or wait until 0.3 is ready? I have some fixes on top of main I could PR, I needed the examples to build in order to test stuff.
at this point I'll probably wait until the full release is ready since people seem to be fine with it / prefer it ^
main should compile though, I already merged a PR to make it work with the full 0.15 release
It's just small stuff like the bevy_mod_debugdump branch being merged, a few bevy_input fields being converted to methods etc
Fair, fixed since after I wrote my first message xD
I managed to find a reproducer, ShapeCasters don't like to spin apparently. If you add the following function to e.g. the kinematic_character_3d example then after some time the "Dir3::new_unchecked is not normalized" message will start appearing with slowly increasing length values. For faster repro you can do time.set_relative_speed with some high number in setup.
fn spin(mut query: Query<&mut Transform, With<ShapeCaster>>) {
for mut transform in query.iter_mut() {
transform.rotate_local_y(10.0);
}
}
There are no controller examples with any rotation, so if the problem is that rotation needs to be handled differently than just setting Transform then it would be good to add an example for it.
Would be cool to be able to build these kinda ship physics with avian3d
https://youtu.be/9wJRqcVTxpI?t=232&si=q30509slCwJcHhWU
would the approach be have several engines that one can apply physics force to which are attached to a larger rigid body that’s the ship hull?
Adding in realistic spaceship physics is super complicated but also very rewarding. Check out some of the details in today's devlog.
Join our discord https://discord.gg/MnkX2acDrY
Watch the rest of the Devlogs here! https://www.youtube.com/playlist?list=PLCEllhcsdc7Yt7MxD1YIO2lM7ZaRJirCg
Game Collaborators:
Checkout Mr. Meteor https://prebenr...
Or maybe that’s just overly complex what they’ve built. Could just apply angular velocity to the ship hull and let engines onboard just carry data for how much thruster force one is getting in forward and backward direction.
And then apply force to the sides, +x and -x
i've seen their system done in plenty of prototype games and iirc star citizen has the same thing
it's either just unnoticeable, or the parts can be destroyed and it's really not fun to play
@vestal minnow Is gravity some sort of persisten force? Or you just apply it directly to velocity?
All forces are applied to velocity, a force is just something that causes acceleration, taking into account masses. But the Gravity resource in particular represents gravitational acceleration, not the force itself, because all bodies should fall at the same rate regardless of mass.
It's basically just applied like this
lin_vel.0 += gravity.0 * delta_seconds;
so yes, to the velocity directly
Cool. So I’m trying to kinda figure out flight controls and ship physics that would actually be fun to play. Gotta figure out if movement is gonna happen by keyboard arrow keys + two keys for rotation or by mouse.
Can I apply gravity to big mass objects like planets in a zero gravity environment to make it attract smaller mass objects like ships and asteroids with avian?
you can make your own system that just applies forces based on Newton's law of gravitation or something similar
Or say, like a space base/station. Also need ships to slow down as they’re getting closer to a space station or another bigger object, so need a way to add friction to the ‘air’ around for the ship to slow down so it’s not too difficult for players to slow down upon arriving to the object. Could maybe call it like an artificial atmosphere. Or just like you now said, a system that takes over player control and helps apply the right forces to make things like say docking to the station easier.
Could I maybe use GravityScale to give objects higher gravity closer to say planets when they’re entering the atmosphere or a planet’s gravitational force?
I know multiplying anything by 0 does not much 🥴
You could, but of course if you have a custom gravity implementation, you'll need to handle that manually
Generally though GravityScale is intended for specific entities that you want a stronger gravity for, or things like flipping gravity when in some specific area
Newton's law of gravitation already applies stronger forces for objects that are close to each other
(specifically the force is inversely proportional to distance squared)
That’s true, so I should have just assumed it was a thing here
Alright makes sense. Wanna try to make as little changes to the physics as possible since I want most parts of the physics behavior to be realistic. Thanks for answering my questions.
@vestal minnow An interesting phenomenon in lightyear is ocurring if I turn on gravity rollbacks seen to occur massively. Might be something with lightyear itself, that is why I asked if there was persistence like in external impulse
I think I have everything working for the mass rework finally, I'm really happy with how it works now
Simplifies and cleans up internals a lot too, and lets us get rid of some unnecessary state like PreviousColliderTransform. I also got rid of the annoying zero mass warning, it should just be interpreted as infinite mass (almost every engine does this)
Just need to work on docs, and I'll make a PR
Cant seem to search here... What should I do to enable Avian colliders with new bevy picking?
Ah nevermind - found example
... I get this message when trying to use picking
2024-12-01T18:16:41.892769Z WARN bevy_ecs::system::function_system: avian3d::picking::update_hits did not run because it requested inaccessible system parameter ResMut<SpatialQueryPipeline>
Do you have the SpatialQueryPlugin?
Yes apparently I did not
Though if I add it without PhysicsPlugins::default() I get
thread 'main' panicked at /home/hexorg/Workspace/Public/avian/crates/avian3d/../../src/spatial_query/mod.rs:191:14:
add PhysicsSchedule first
You also need PhysicsSchedulePlugin
The plugins are kinda hard to use individually atm, a lot of them are dependent on each other
I've been trying to minimize unnecessary dependencies though, and intend to cut them down more
Now we just need required plugins 😂
I think we should clearly define the scope and requirements (plugins or resources required to function) for each plugin, document them, and have integration tests set up to verify that the minimal setup always works
And also maybe set up sub plugin groups, like DynamicsPlugins, CollisionDetectionPlugins, etc.
this PR is becoming chonky lol
I still have a lot more docs to write
@vestal minnow feel free to poke me when you are working on big_space support. We probably have different goals of how we want to support physics, but I'm more than happy to chat about the approach you want to take and help out.
Collisions (only if solver warm starting is enabled) @vestal minnow Is that enabled default, dont know where to look at docs?
It is enabled by default, it's pretty important for stability when objects are coming to rest
edited the issue to clarify the defaults
Thanks, how do I disable it?
I think the relevant thing here is disabling contact matching in NarrowPhaseConfig. It matches contacts from the previous frame to contacts during the current frame, which is used for warm starting contact impulses
app.insert_resource(NarrowPhaseConfig {
match_contacts: false,
..default()
});
We should probably have more obvious/discoverable docs for this stuff, although it's kinda low-level
You could probably just roll back the Collisions resource too, though it might be a bit expensive if there are lots of contacts
The issue weird right now it sometimes occurs sometimes it doesnt
I am wondering if it is something with system ordering
could be
In my game for example rollback only occurs after i spawn other predicted entities, with sole client
Dude
this solved it + a little bit of system reordering you are a godamm savant
haha nice!
Well he does still rollback when collision occurs but now he doesnt loop hahaa
When the contact impulses occur, they apply some sort of persistent force?
No, except sort of when warm starting enabled
Warm starting takes contact impulses from the previous frame into account to help things stabilize
But without it, contacts shouldn't persist across frames
Lovely well in this state I am just gonna say more than good enough
even more :P
plus an entire new crate (bevy_heavy)
PR today or tomorrow
might take a while to write a proper description since I'm honestly not sure how exactly the mass properties even used to work 😂
I believe the old behavior was very strange, like for example setting CenterOfMass at spawn did nothing unless you also specified an initial Mass iirc
now I at least have a bunch of tests set up
are you sure there's anyone at all who understands how it worked before? Dx
the proper description:
"I'm honestly not sure how exactly the mass properties even used to work. The old behavior was very strange."
is the branch on github already?
Yeah this is on mass-property-rework-2
thanks! task on the top of my todo list depends on some Mass stuff
TLDR of new user-facing behavior is
Mass,AngularInertia, andCenterOfMasscan be used to set the mass properties of an entity. These are never modified by Avian. If they're not present, the mass properties will instead be computed from the entity's collider based on itsColliderDensity.- Mass properties of descendants contribute to the total mass properties of rigid bodies, which are stored in
ComputedMass,ComputedAngularInertia, andComputedCenterOfMass(updated automatically). - Automatic computation of mass properties based on descendants or colliders can be disabled with
NoAutoMass,NoAutoAngularInertia, andNoAutoCenterOfMass, giving you full control to override them entirely. - There's a
MassPropertyHelpersystem parameter for computing and updating mass properties manually. This will probably get more functionality later.
And a ton of internal changes, and a ton of new methods and helpers for constructing and computing mass properties
One kinda cool random thing I did is that you can set mass properties using Bevy's primitive shapes
let torus = Torus::new(0.5, 1.5);
// Spawn a rigid body with mass properties computed from a torus
commands.spawn((RigidBody::Dynamic, MassPropertiesBundle::from_shape(&torus, 2.0)));
// Equivalent to
commands.spawn((RigidBody::Dynamic, torus.mass_properties(2.0).to_bundle()));
// Use the mass of a sphere but the angular inertia of a torus (because why not?)
commands.spawn((
RigidBody::Dynamic,
Mass::from_shape(&Sphere::new(2.0), 1.0),
AngularInertia::from_shape(&torus, 5.0),
));
if the scale of a collider is modified, is the ComputedMass updated to reflect the new size? i'm thinking in cases like the lightning bolt in mario kart - smaller karts get bounced around easier
Yes, unless you have set the Mass of that entity explicitly
awesome
(or at least, it should work; if not, let me know!)
RecomputeMassProperties is printed to the console..looks like once per collider
on this new branch
Oh wow looks like I left that in haha, I was debugging an issue earlier
should be gone now @rocky seal
@vestal minnow Hmm might I ask how is friction currently applied
Similarly to how the normal part of contacts is handled, with an impulse
(the actual friction constraint is in this file)
Hmm interesting
forgive me if this has been covered, I did search with not much luck, I cannot figure out how to orient my player in a my top down game towards the direction of input. If I use transform.rotate_y I get a crazy spinning character. I realise this might be because of Avian has control (or should have control) but I only care to use linear velocity for movement. How do I go about this?
are you using rotate_y only to rotate depending on the rotation change?
A day late, but here is the behemoth
https://github.com/Jondolf/avian/pull/574
Also, bevy_heavy 0.1! #crates message
so ready to levy bevy heavy to calculate the mass of my chevy
+3,024 −1,012
That's a lot of code for mass properties
That can be extremely usefull to be honest
+3,024 -1,012 and a whole new damn crate... you'd think something like mass properties isn't that much work
the price you pay for decent APIs ig
to be fair probably over half of it is docs and tests
having to do this to have nice doc examples for 2D and 3D is fun
omg retained gizmos got merged, I think they'll be sooo nice for physics debug rendering
thank you @pulsar bone 🙏
Does "static lines" mean just that the actual gizmo asset remains unchanged? I.e. I can change Transform without losing the perf benefits
That should be the case
okay nice
Hi @hidden girder yep using rotate_y after direction from input is received.
I just have to say that this is far and away the most pleasing, well documented crate I've ever worked with. It's been a pleasure reading the docs. I'm in love.
Thank you for your efforts
So you are not calculating the rotation angle from input each frame and then passing that to rotate_y?
Because if you do this it is to be expected that it just keeps spinning.
Unless you have 0 rotation input.
I was looking into this now, and I'm kind of torn on whether we should fix this on the Avian side. The problem here is just that you're performing tons of successive rotations, which naturally results in denormalization over time. That is not necessarily an Avian issue, it's just what happens if you're rotating entities like this, regardless of whether you are using a physics engine.
If you had any AngularVelocity instead, this shouldn't be an issue, since it renormalizes rotations after position integration. By modifying Transform directly, the entity has no velocity from Avian's perspective, and no renormalization is performed / it's left to the user.
I guess we could just perform the renormalization on Avian's side every time rotation is changed though, even if there isn't any AngularVelocity. It might ultimately be nice to provide that guarantee of rotations of all dynamic and kinematic rigid bodies staying normalized.
dir has a warning, so i think the same thing for quat would make sense
Thanks for looking into it. I'll try to use AngularVelocity instead. I looked at various example trying to build a character controller for a counter-strike esque game where you rotate the camera with pitch and the pawn with yaw, and I ended up doing it using the transform for the pawn. If AngularVelocity works for that use case then I don't think there's a problem. If there is a comment about Transform denormalizing somewhere and maybe an example that would also be good.
Another option is to add a debug assert that checks normalization and links the correct place in the docs?
what to do when the wall normal and velocity dot is exactly -1 in a collide_and_slide?
velocity = project_on_plane * remaining * f32::sqrt(direction.dot(hit_data.normal1) + 1.);
I did it like this, and it scales the remaining velocity. However how would someone cleverer do it?
This might be an incredibly dumb question, but when spawning a triangle collider (avian2d), are we supposed to enter the vectors in CCW order? I was having issues with non-circle colliders clipping through triangles until I reordered the triangles to CCW. Maybe that's obvious to others, but it took me a min to realize that was my problem. I don't see any mention of CW/CCW order on the docs (tho I might have missed it somewhere)
i had a similar issue, i'm currently using Collider::convex_hull
I believe it needs to be CCW, yeah. This requirement comes from Parry (the collision detection lib). I can change the Collider::triangle constructor in 2D to check the winding order and flip it to CCW automatically though
Fixed here
https://github.com/Jondolf/avian/pull/579
@severe urchin @vestal minnow appreciate the help and answers
Added MaxLinearSpeed and MaxAngularSpeed
https://github.com/Jondolf/avian/pull/580
These are actually pretty good for preventing explosiveness for things like chains
are trimesh unable to perform event CollisionStarted ? or I misunderstood how to use it ?
They should get the event when things start colliding with them, like other shapes
Hum... I must be doing something wrong then
Does anyone have an example of a vehicle controller using Avian3d?
Finally added a check for cargo doc to CI (to detect dead links etc.)
Oh and also I merged the mass property rework and interpolation stuff
merged RigidBodyDisabled too
I think I'll merge this too unless someone is opposed
https://github.com/Jondolf/avian/pull/510
Moves most settings for ray casts and shape casts into RayCastConfig/ShapeCastConfig, renames "time of impact" stuff to "distance", adds a few more configuration options for shape casts, and fixes a whole bunch of docs
It's unfortunate how Bevy 0.15 has RayCastSettings for mesh ray casts now, which overlaps with our names a bit. I made a PR to rename it on the Bevy side to MeshRayCastSettings though
https://github.com/bevyengine/bevy/pull/16703
My understanding is that Bevy tends to prefer FooSettings over FooConfig, so I might rename a lot of our FooConfig types to FooSettings for Avian 0.3
(source: #1264881140007702558 message)
i still have the same concerns from the last time, you're mixing parts that might be constant with parts that might need to change often
and it's hard to predict which parts would change, so you could merge pos+rot into it to and use a builder pattern instead
Could you give an example? I'm not sure if I understand what you mean
mainly concerned about the filter, maybe layers would remain the same across all entities but excluded entities are most likely going to change for each one
for a more generic system max range might change often
and i guess it'd be possible to store config in component, in which case pos/rot wouldn't change either
you could keep cast_x functions as shorthand for xcast(config with all the old params), and let people test new approach without any concerns
and i just remembered that we concluded on making our own helper function 🤔
i completely forgot about it, but it seems like i arrived at exact same conclusion as before lmao
I would be fine moving the filter out of the config, but otherwise I think the PR's current approach is fine
Hmm I might move the filter out, yeah
i'm still not sure what it solves though
i'd understand keeping everything in 1 struct and using builder pattern for the ergonomics though
let me try to find the part about making custom helper...
oh, that was it
I'm mainly thinking conceptually about the bigger picture.
If we consider a single shape cast against a single shape, it should be:
let hit = circle.cast_shape(rect, rect_pos, rect_rot, dir, config);
where config controls how the cast behaves.
If we're instead querying the world for a hit, we have introduced another layer, and we want to filter which entities to perform the shape cast on. This is the same as before, but with an additional filter:
let hit = spatial_query.cast_shape(rect, rect_pos, rect_rot, dir, config, &filter);
I feel like these two should use the same type for config. The filter is for filtering which entities are included, while the config is for controlling how the actual shape cast behaves for those entities.
It's also more consistent in the sense that other spatial queries like project_point and shape_intersections have the filter as an argument
i think layer filter would be fine in config, the entity filter is more of a custom thing
and i think that this would make the most sense either way
you could actually make things like .forward(&Transform) for this too, getting position and rotation in 1 step
This is fine as a helper on the user side but in terms of first-party APIs, I definitely don't want to just put everything in a single struct. The ray origin and direction are what define the actual ray cast, and must be specified, so they should be arguments. The rest (max distance, "solidness", query filter) are extra options, not something that defines the core ray cast.
And the query filter is semantically different from the other configuration options
We could honestly even do spatial_query.filter(...).cast_ray(ray, &config)
The filter applies to all spatial queries
wait a minute
you could do into ray
and it'd be able to take both (pos, rot) and Ray{pos, rot}
and ray from transform could be a helper for ray itself
that'd be (origin, direction) but yeah
you could use a rotation too I guess but the direction at Quat::IDENTITY is ambiguous
(position, direction) would make more sense
i'm not even sure if i like my own idea, it seems like it makes sense but wouldn't actually be nice in practice
getting ori+dir for ray from transform happens pretty often though
but something like physics.raycast(template_config.from_transform_z(&t).exclude([entities])); would be neat
mmm I'm kinda tempted to do something like this, you could do
let hit = spatial_query.with_mask(...).exclude(...).cast_ray(ray, &config);
not for this release though
mask in config though
Why? It's not configuring how the ray cast behaves, it's adding a filter to the SpatialQuery. It's like adding more filters to Query
i think for this release just do toi -> distance without the rest of it
i consider layers to be a part of config that is probably one of the easiest things to share
like if you have a damaging ray, you always know that it'll hit enemies and environment, but e.g. max range could change
Sharing config is not really the point, and semantically mask makes no sense in config in terms of how I think about it, and from an internal perspective
And with this the layers could be "shared" anyway
in that case it's just breaking apart params for semantic reasons, and only making it harder to read
You could even do e.g. this
fn foo(enemies: Query<&Transform, With<Enemy>>, spatial_query: SpatialQuery) {
let filtered_query = spatial_query.with_mask(GameLayer::Player);
let config = RayCastConfig::from_max_distance(5.0);
for transform in &enemies {
let ray = Ray3d::new(transform.translation, transform.forward());
filtered_query.cast_ray(ray, &config);
}
}
does that actually improve performance?
in that case it actually makes sense
but it's definitely harder to read
It doesn't
But I don't understand why it'd be in config
from a semantic perspective
this actually looks fine
fn foo(enemies: Query<&Transform, With<Enemy>>, spatial_query: SpatialQuery) {
let filtered_query = spatial_query.with_mask(GameLayer::Player);
for transform in &enemies {
filtered_query.cast_ray((pos, dir), &config);
}
}
but if it doesn't improve performance...
yeah but you just removed the part where config and ray are created :P
fn foo(enemies: Query<&Transform, With<Enemy>>, spatial_query: SpatialQuery) {
for transform in &enemies {
spatial_query.cast_ray((pos, dir), config.without([self_entity]));
}
}
i've been assuming config is just a struct that can be a const or static
it can, yeah
actually wasn't there already a discussion about moving it to just cast
cast(ray, config)
cast(shape, config)
or was it ray.cast(config) and shape.cast(config)
i feel like i'm recovering after amnesia or something
you're working on too much stuff at the same time without merging, it's been 5 months now D;
but yeah i don't see how it'd actually be possible to make in any comfortable way
I'll at least move the query filter out of the config (like it was before the config struct was even a thing). This is the same as what Rapier does, is more consistent with the other queries, and makes more sense both semantically and in terms of exposed types. Otherwise we will have duplicate config types with the only difference being that one has the filter and one doesn't.
I can revert RayCastConfig and have the options as separate arguments like they were previously / are currently on the main branch, but we definitely need ShapeCastConfig to exist.
Because this is miserable.
let hits = spatial.shape_hits(
&Collider::sphere(0.5),
Vec3::ZERO,
Quat::default(),
Dir3::ZERO,
100.0,
10,
0.0,
true,
false,
&default(),
);
it definitely is, but the solution isn't really ideal either
would adding a spherecast make performance worse? i don't get why sphere needs a quat
oh i just realized you made it worse by adding target_distance and compute_impact_on_penetration, and i don't even know what they do
That's exactly why we need ShapeCastConfig
Most people generally don't need to configure them and shouldn't need to specify them manually
Without the config types we also need to duplicate docs for all ray/shape cast methods
this doesn't look that much different though
let hits = spatial.shape_hits(
&Collider::sphere(0.5),
Vec3::ZERO,
Quat::default(),
Dir3::ZERO,
&config,
layerTemplate.exclude([one_entity]),
);
Every type is distinct there, unlike the earlier example
let hits = spatial.bikeshedding_sphere(0.5, pos, dir, &config, [excluded_e]);
or
idk if it'd be possible
let hits = spatial.bikeshedding_sphere(0.5, pos, dir, &config).without([excluded_e]);
excluded entities just really feels out of place and shouldn't warrant a default anywhere
That isn't, but
spatial.without([excluded_e]).bikeshedding_sphere(0.5, pos, dir, &config);
is, which is just my earlier idea
same thing to me
everything non-essential except excluded entities stays in config
while keeping excluded entities easily accessible but omitted when unnecessary
Then the different types of filters, i.e. "filter by layer" and "filter by excluding entities" are separated to completely different APIs
I would remove excluded_entities and have a predicate instead
is there an internal reason why it had to be merged into 1?
I guess Godot does have exclude too
https://docs.godotengine.org/en/stable/classes/class_physicsrayqueryparameters2d.html#class-physicsrayqueryparameters2d
not separating it might eventually cause problems with the editor
yup
layers feel as much of a property as max range, but excluded entities don't
godot specifically has both the layer mask and excluded entities in the same object
ahhh i hate godot's java-brained get/set so much
doesn't seem like unity has excluded colliders at all
looking at unity docs i realized
config makes sense if treated as a replacement for unity's overloading
it'd actually be less confusing than 9 raycast variants where you don't know how parameters move around
I would probably remove it too, except maybe keep it for RayCaster/ShapeCaster for convenience. There's no point paying for the (small-ish) memory and performance overhead of the hash set for every spatial query, when you can easily do the same thing manually with a predicate anyway
looking at other engines, they don't tend to have it either, except Godot
i'm using it to exclude self for now but i guess with that fixed it wouldn't be as important
it wouldn't make sense to create a whole new layer just to exclude 1 entity though, so maybe it's better to keep it
again, you'd just use your own predicate for that
same overhead (or might even be smaller)
just |entity| entity != my_entity
how do you continue ray/shapecast after that though
it's a predicate you give to the ray cast, it just ignores entities that don't match and keeps going
it's a filter, not an early-exit
is that already here?
or the existing API doesn't allow this ig
or it kinda does
You can use ray_hits_callback and manually respond to each hit, which is essentially what e.g. Box2D has
you'd need figure out a way to explain it in docs because i wouldn't know what to do if excluded entities were removed and i needed them D;
Hmm random thought, I don't love the naming inconsistency and ambiguity between cast_ray and ray_hits, or cast_shape and shape_hits
We could do cast_ray (many hits) and cast_ray_closest (only the closest hit), which also makes the behavior clearer
Box2D has this naming
unity has raycastAll
i don't really like any of those names though
cast_xray maybe? xD
ew
wouldn't work with shapes though
actually, couldn't it just be done as a predicate too?
I like that cast_ray_closest makes it explicit that it's specifically the closest hit (with the version that gives many hits, they're not ordered)
the traversal algorithm is different
what are they both called
The traversal? Afaik the closest hit version uses a best-first traversal, the other one uses depth-first
getting a single arbtrary hit with depth-first is more efficient than getting the closest hit iirc
but it could e.g. pass through walls, right?
which might be surprising with the names cast_ray and ray_hits, some people might assume that using a method that can get many hits is always more expensive than getting one hit
yes
from what i've seen in unity people either used -all and sorted by distance, or used raycast only vs environment first, then did second ray or sphere cast with that distance
i think some consistent naming with bikeshedding_sphere and shapes would make the most sense for it
i keep forgetting how it's called in avian
in unity it's overlapsphere/box
shape_intersections
Unity got the OverlapFoo naming from Box2D
ray_intersections doesn't sound right at all
overlap_ray could work
ray intersections makes me assume it'd give me points where it intersects with other shape
ray_intersections gets entities intersecting the ray, rays overlapping sounds weird to me
i guess shape intersections is confusing for the same reason
maybe something like test_shape and test_ray?
It's also ray_intersections because it's called e.g. a ray-capsule intersection test, and the method for a single shape would be shape.intersects_ray(ray)
that one does make sense
cast_ray / ray_cast
overlap_ray / ray_overlap
intersect_ray / ray_intersections
test_ray / ray_test
cast_sphere / sphere_cast
overlap_sphere / sphere_overlap
intersect_sphere / sphere_intersections
test_sphere / sphere_test
cast_shape / shape_cast
overlap_shape / shape_overlap
intersect_shape / shape_intersections
test_shape / shape_test
i think overlap is more intuitive (but it might be a unity bias), but as long as they follow a common pattern it's all good
i'd also prefer removing the underscore, but that makes overlap and intersections really bad
Rapier also uses intersections_with_ray
This should be consistent with whatever point intersection/containment tests use too
raycast/spherecast/shapecast
raytest/spheretest/shapetest
both ending with -st makes them confusing so it has to be something else =/
Hmm random thought, we could technically replace solid with an enum (name TBD) that has an Ignore variant
#[derive(Default)]
enum InteriorHit {
/// If the query starts inside the collider, stop immediately, and return the result.
Solid,
/// If the query starts inside the collider, return a hit at the boundary.
Hollow,
/// If the query starts inside the collider, ignore the hit entirely.
#[default]
Ignore,
}
IIRC that's the default behavior in Unity and Godot
It's the same as solid: true and checking if the distance is zero though
Okay I reverted RayCastConfig for now, so the ray casting methods are the same as before. I kept ShapeCastConfig though since there's way too many niche parameters otherwise (and it matches Rapier and Parry)
I want to get this PR merged since the renames, documentation fixes etc. are valuable on their own
We can continue bikeshedding the other things later
Merging once CI passes
@vestal minnow I'm getting this warning from avian, I wonder if this would cause FPS to drop. I'm seeing my game have sudden FPS drop randomly for a few seconds once a while. I wonder if the warning is related to the issue
2024-12-08T06:45:39.531002Z WARN avian3d::collision::narrow_phase: Entity { index: 1119, generation: 5 } (rock) and Entity { index: 1896, generation: 66 } (ammo) are overlapping at spawn, which can result in explosive behavior.
All my rigid bodies are set to RigidBody::Kinematic BTW. So I suppose I won't see anything explosive visually
i really like the idea but needs bikeshedding
yup, fixedupdate can be executed 0 or multiple times per frame
Could be, it's hard to tell. Make sure you're not spawning a ton of overlapping colliders at the same spot at once, and that they have the correct initial positions
and check if the collisions are expected, like in this case, are the rock and ammo expected to be overlapping?
That actual warning doesn't exist on the main branch anymore though, since it was mostly causing unwanted spam for people
what, it's not just opt-in, you completely yeeted it?
yeah we yeeted it
https://github.com/Jondolf/avian/pull/547
I'm not sure if the fix schedule explains the issue fully. Here is a frame where the physics schedule ran 15 times per frame. Around that frame, there were total 10+ frames where physics schedule executed multiple times per frame. Seems like a cascading effects causing physics schedule to run many times
I'm recycling entities in my game for reuse later. If a rock get destroyed, I will remove RigidBody (as well as Position/Rotation) from the entity (keeping the collider on it still), then move the entity to a Transform::IDENTIY and make it Visible::Hidden. When I reuse the rock entity, I will move the object's transform to the specified spawn position and re-add the RigidBody back to the entity to activate the collision check. Could this cause the issue above?
this seems inadvisable. Why are you doing that?
The ecs is already very very good at recycling memory.
i didn't want to do that. my game is running fine on desktop, but suffer from spawning objects containing materials on mobile. based on tracy profiling, i found that every time bevy spawn entities containing materials, or update the uniforms in the materials, the FPS might drop. i'm optimizing my game by
- reduce spawn frequency
- reduce material uniform update frequency
That should really only happen on first spawn with shader compilation afaik.
Let me verify if that is the case. I can turn off recycling and do some profiling on mobile
If you are reusing them, you might want to hold on to a strong handle to the materials in a resource, e.g. rock: Handle<StandardMaterial>, and it should stay loaded as you despawn/spawn entities.
Yeah, I have preloaded all my materials into a resouce that contains all the strong handles
In that case, I don't see why this would be an issue after it is spawned at least once.
If it is, would be good to report that over in rendering-dev so more knowledgable people can look into it
Yeah, I hope that is the case as you described.
Entity pooling is an antipattern though, if you have to do that, something has probably gone wrong. Pooling large allocations is still useful sometimes though.
It's almost inevitably going to lead to issues like you are seeing here. Entities should be truly unique, and spawning new ones and handles to things should be cheap.
Some update after test/profiling: Yes, pooling is unnecessary, only the first time spawn is slower. Also after disabling pooling, the FPS sudden drop issue seems gone, although I haven't tested it for extended time.
Wanted to complete the trifecta, so I added ColliderDisabled in addition to the existing RigidBodyDisabled and JointDisabled
https://github.com/Jondolf/avian/pull/584
I think this'll be the last thing for 0.2, aside from maybe some doc tweaks
Hey, i'm trying to have a ShapeCaster pointing directly down at all times to detect when the player is on the ground. Currently my player collider is a sphere so it is always rotating. Is there a way to decouple the shapecaster's rotation from the collider's rotation?
here's my player code for reference:
fn setup_player(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
let sphere_radius = 0.5;
let mut caster_shape = Collider::sphere(sphere_radius);
caster_shape.set_scale(Vector::ONE * 0.99, 10);
commands.spawn((
Mesh3d(meshes.add(Sphere::new(sphere_radius))),
MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))),
Transform::from_xyz(0.0, 1.5, 0.0),
RigidBody::Dynamic,
Collider::sphere(sphere_radius),
LinearDamping(1.5),
Player,
Name::new("Player"),
CharacterController,
ShapeCaster::new(
caster_shape,
Vector::ZERO,
Quaternion::IDENTITY,
Dir3::NEG_Y,
)
.with_max_time_of_impact(1.0),
));
}
If you don't want the player sphere to roll on the ground, you could lock its rotation with LockedAxes. If you do want it to roll but want the shape cast to always point down, I probably wouldn't use ShapeCaster, and would instead do shape casts manually in a system with SpatialQuery::cast_shape
Awesome, thanks @vestal minnow ! I was considering manually setting the position of the shapecaster every frame, but SpacialQuery looks like a nicer approach 🙂
do you think adding an option to ShapeCaster to decouple it's rotation from the collider would be a useful feature? I wouldn't mind trying to make a contribution if you think it would be desirable
Why does this warning start getting spammed after I walk around for a bit with my character controller? I don't use Dir3::new_unchecked without explicitly normalizing, so I don't think it's on my end. The more I move around, the bigger the length number in the warning becomes
Using main branch on 0.15 btw
Do you rotate the character somewhere?
Yes, mouse movement in a system
If you remove that, do you get the warning anymore?
Nope
So I guess it's cause of the rotation
Oh wait it just gave me that error again and panicked
Hmm weird
In general if you're rotating something constantly, it's accumulating error over time and you should normalize or renormalize the rotation occasionally
physics does this automatically if there's some AngularVelocity, but not for manual rotation changes
So just rotation = rotation.normalize()?
Yeah
Is the panic from avian3d::spatial_query::update_shape_caster_positions like this issue?
I just spent like 5 minutes trying to get it to panic and realized I didn't set backtrace 1 🤦
@tulip prism we've been multiple people further up with the same issue, I think you simply can't reliably rotate the transform on a physics-enabled object atm. My plan is to try to use AngularVelocity for my character controller instead, but I haven't ported to it yet.
It would be nice to have rotation in one of the character examples, I think it'll be a common stumbling block
Am I supposed to build with opt level 0 to get that info? Cause for me it doesn't have stuff like line number
Nvm I think it's cause I have debug=0
Hmm, I think I would say no for now. Ignoring an entity's transform (or parts of it) feels like something that could technically be applicable to almost anything, like even colliders and meshes. I wouldn't special case RayCaster and ShapeCaster unless there's a large usability gain, and currently I think SpatialQuery fits the need fine
RayCaster and ShapeCaster are also kind of intended to be equivalent to Godot's RayCast2D/3D and ShapeCast2D/3D nodes, which are also relative to the transform
Ok finally got it, yes it's from that
I'm not getting the warning or panic even if I do add rotation :/
this is the kinematic character example, modifying Transform rotation directly with a basic slerp
(in debug mode)
It takes a lot of rotations, it only happened to me after I had some other bugs that made my camera go crazy. In order to reproduce it I just made it spin continuously pretty fast and sped up the time
But even though it's hard to trigger you can't really rely on a player not rotating the character enough, especially not in a mouse look game
This reproduced for me reliably. I can make a small self-contained repo if that helps
This should only be a problem with debug_assertions enabled, so the warning and panic shouldn't happen for people actually playing the game. Regardless you should generally renormalize if you're changing rotation frequently, that's what fast_renormalize is intended for (I just realized it actually wasn't added for Quat since the perf difference is small, but normalize does the same)
Yeah I managed to reproduce with this too, and renormalization fixed it
Nice, is it easy to do from outside avian? Because if I can just opt in to that for my player controller shapecaster that would be ideal
I mean it's just
fn spin(mut query: Query<&mut Transform, With<ShapeCaster>>) {
for mut transform in query.iter_mut() {
transform.rotate_local_y(10.0);
transform.rotation = transform.rotation.normalize();
}
}
Oh, it's just on the affected transform, I somehow thought it was an internal quaternion or something. Awesome, thanks for looking into it
Now make him climb a stairs 🔥
Is anyone working on avian in VSCode?
I can't figure out a way to switch Rust Analyzer highlight between 2d and 3d.
I tried some combinations of feature settings in RA, but it felt bugged.
You can add it to your .vscode/settings.json:
{
"rust-analyzer.cargo.features": ["3d"]
}
Thats the problem, I already have that in my settings.json 😅
Tried restarting RA server and VSCode
Same result
Also tried setting rust-analyzer.check.features
A cursed approach that works for me is to only have one of the crates as a workspace member in Cargo.toml
[workspace]
// Remove one of these
members = ["crates/avian2d", "crates/avian3d"]
resolver = "2"
I rarely do that though
Oh yeah, I remember doing that in the past.
Thanks.
Has syncing transforms been improved?
I remember it doing some "redundant" propagations in the past.
do you use vs code typically? do you just live in 2d mode?
In Avian, it only does extra propagation for hierarchies that have physics entities, so there should be basically no overhead for non-physics entities anymore. Previously it also needed to do propagation at every substep, which isn't necessary anymore.
It does still do several propagation passes per physics tick though, and I think that's (currently) necessary to let people use Transform directly
(Btw there is building interest in IK constraint solving infrastructure in #engine-dev . Since that would also need its own propagation loop, maybe we should think about a common mechanism for use by IK and physics.)
I use VSCode, yeah. I don't feel like I need to use a specific dimension often, since a lot of things aren't dimension-specific, and I usually still get errors and linting even for the inactive dimension. It's mainly stuff like autocomplete, go to definition, and other context-aware things that don't work for the inactive dimension
I currently have a weird setup where I mark every ancestor of physics entities with marker components like RigidBodyAncestor or ColliderAncestor (I have a generic plugin for this). It lets me skip propagation for trees that aren't related to physics, since that was previously causing severe perf issues for applications with lots of non-physics entities
and then this custom propagation is done in specific parts of the physics schedules
What would you do if we didn’t have to propagate from roots?
I'm not sure what that would look like / what implications it'd have
For a lot of the propagation stuff I think it'd be enough to propagate just from the rigid body entities
The hierarchy hooks changes is going to make caching ancestors information possible, which will let us determine m disjoint subtree roots given n changed transforms.
So that we can do within-tree parallelism.
This is something I def want to try, but I have no idea how your prop stuff works. Don’t want to break it.
I mainly need to do propagation right before physics (to make sure that body and collider positions are up-to-date before simulation) and right after physics (to make sure that the movement of rigid bodies is applied to their descendants).
Currently I also have an extra propagation pass right after physics to consider Transform changes that users made during the physics step, but I might remove that and just disallow changing Transform in the middle of physics, not sure yet
Also I currently have two types of propagation, one is basically a clone of Bevy's transform propagation, and updates GlobalTransform for trees with physics entities, and the other computes ColliderTransforms for colliders, which is like GlobalTransform but relative to the rigid body, not the world origin
fair enough. i also couldn't figure out a satisfactory solution to switching between the two (sounds like it bothers me more than you) so i just gave up. so anecdotally, the ergonomics of the repo organization might be deterring potential contributors
Yeah, it's not great
Could symlinking the primary crate into 3d and 2d crates work?
And then separate the 2
Maybe, I vaguely remember someone trying that in the very early days of bevy_xpbd
Again the least scuffed approach in terms of crate structure would be to just have a unified avian with 2D and 3D in the same crate (behind feature flags) and separate types for each dimension where relevant
It's just a lot of work, and somewhat challenging to do without tons of duplication
A lot of things are almost the same between 2D and 3D, with small differences here and there
needs a lot of traits at least
in the absence of that a documented way to switch between the two that plays nice with r-a would help. maybe just a comment in the toml file mentioning the cursed method
are forces (e.g. ExternalForce) given in units of force or units of acceleration?
nvm just looked into the code, forces are multiplied by inv_mass (so they are given in units of force)
in that case I am doing something quite wrong in my code lol
i am guessing gravity is just in units of acceleration tho
yes it's gravitational acceleration, not a gravitational force
any tips for doing terrain with avian3d? I'm using heightfields and a 1024x1024 field slowed my computer to a crawl, but 256x256 works fine. I haven't added more yet but I'm wondering if I should break it down even further
in debug or release?
debug
i wouldn't make performance decisions based on debug builds
well I'm just looking for general pointers on how to break down the colliders
roger. can't help you there i'm afraid - woefully ignorant myself 😛
just feels like it might be too big but idk what the physics engine is doing in the background
Posting an issue that is very similar to what @tulip prism was dealing with above, but with a notable difference. I had a panic due to a spinning physics body, however, this body was not spun by player input or user code. It was a dynamic collider, with locked translation, and no angular damping. Another collider would bump into it, and it would spin endlessly. For me, this issue disapeared once I added an AngularDamping component.
@vestal minnow I see your comment above that this is from a debug_assert (in this case, in Rot2::from_sin_cos), so it wouldn't panic in a release build anyways, and also that this can be corrected by renormalizing rotation (I'll remember fast_renormalization for future use). But that doesn't apply in this case, right - since it's not being spun by any user code?
Obviously I'll avoid endlessly spinning bodies in the future - I was just testing a bunch of random things. Should we include a sentence in the docs warning about this - maybe something saying if you're going to use LockedAxes::TRANSLATION_LOCKED, that you should add AngularDamping? Dummy-proof it for users like me lol
Also possible that I'm completely misunderstanding this error.
Can I update to bevy 0.15 with avian? The main branch is not compatible right?
it is
Yeah this looks like a slightly different issue than the earlier one. Do you have a minimal reproduction of this? I've had a 2D object spinning at zero gravity for 20 minutes now, and haven't gotten any warning or panic
(also with translation locked)
Ok here is a gif of it occuring. I can consistently get this panic everytime. When I add a AngularDamping component to the pentagon, I don't get the panic (pentagon goes to sleep). I could go more minimal if you'd like, make a new project and drop a random collider on it (as opposed to the capsule which has character controller code).
Also of note - there is global gravity in this demo, .insert_resource(Gravity(Vector::NEG_Y * 250.0))
Update: I went ahead and got rid of the player character controller. I instead am now just dropping a same-size capsule on top of pentagon, no player input. Still same panic.
Now trying to go as minimal as possible - will see if I can still reproduce
@vestal minnow Super minimal reproduction here: https://github.com/dylanafterall/panic_test
Will usually get the panic in first minute or two.
I tried to keep the collider dimensions and gravity magnitude the same, but now using default camera settings.
Running on a M1 macbook.
I usually assume I'm doing something stupid - still the most likely cause.
I updated my game to bevy 0.15 and am using main avian (e23d070)
I noticed that... while my truck movement is smooth, the smaller objects in the truck bed aren't.. but they have the TransformInterpolation component added. This wasn't happening to me on 0.14 with the
bevy_transform_interpolation crate.
I'm going to keep digging into it, just wanted to see if anyone knows something obvious I might be missing
it does seem less noticeable at 60fps vs 120
i have anecdotally also noticed choppier movement using the interpolation built-in to avian (choppier than when i was using the crate separately before its integration)
i have added some other systems that run every fixedmain too tho, so it's not a perfect comparison case. but i didn't add that much and i have a fairly powerful machine
this is in debug - it's smooth in release
is there a way to get the effective forces that have been applied to a rigidbody during the solving constraints step? Or should I just go ahead and calculate themselves by measuring the change of velocity?
hmm, still happens to me in release.. for you, is it all objects or just specific ones, like smaller ones?
are there any other known causes of jitter using bevy_panorbit_camera and camera following code?
assuming I've got this
.configure_sets(PostUpdate, PanOrbitCameraSystemSet.after(PhysicsSet::Sync))
.add_systems(
PostUpdate,
Self::camera
.after(PhysicsSet::Sync)
.before(TransformSystem::TransformPropagate),
)
(note the jitter seems to be non-deterministic depending on the run of the executable)
I've run into a couple different reasons for camera jitter. How are you using the camera? Is it following a moving object?
the biggest cause of jitter for me was that my camera smoothly follows an object and it jitters when the camera updates multiple times before the physics updates. My current solution for that is to not update the camera if its target hasn't moved
yes, it's following a moving object, lemme try that out
what's really weird is that some runs it's smooth, some runs it's jittery
I just have a component that is like "FollowTarget(Vec3)" and check/update it every time the camera updates
it's not like the jittery turns on and off, it's just either always on, or always off
fair, thanks, I have a feeling I just need to write my own camera to make it work lmao
if you really want to dig into it, you could do a profile and see how the physics updates aligns with the rest of your game like I did here
#1124043933886976171 message
once I saw it visualized like that, it kinda clicked what my problem was
that's a very good idea, time to set up profiling, thanks :)
https://github.com/bevyengine/bevy/blob/main/docs/profiling.md
the trace grows pretty quickly; I'd recommend setting up your game so when you run it's like right at the point where you want to measure
that's not gonna be a problem, I literally just started :P
now I just need to get tracy to work on nixos with X11
Thanks! #590 should fix it
Hmm, yeah it's weird that the truck works perfectly but the smaller objects don't 🤔 Any relevant differences between their components or otherwise?
Also do you happen to manually modify their transforms anywhere?
(currently bevy_transform_interpolation treats that as teleporting if done outside the fixed timestep schedules, which could potentially cause some jitter or other weirdness if done repeatedly)
the built-in version doesn't do anything differently from bevy_transform_interpolation, it just sets up the relevant plugins and components
Is Self::camera ordered relative to PanOrbitCameraSystemSet?
both run after physics and before transform propagation, but it looks like their relative order is left ambiguous atm
.add_systems(
PostUpdate,
Self::camera.after(PhysicsSet::Sync).before(PanOrbitCameraSystemSet),
)
this is the current code, but not sure if it should before or after
Not sure, I haven't implemented camera following with bevy_panorbit_camera
whatever doesn't jitter :P
yeah I think I'm gonna end up just writing my own orbiting camera, I was just being lazy and putting things together that I'll need
last time I was making a game (a decade ago) I started from the game engine with glium and shiet, and I ended up not making the game at all stuck writing dependencies, so I'm trying to force myself to do the entire opposite :P
Not really, it's not tracked or stored anywhere
impulses are only stored for each individual constraint and contact
I see, thanks. Guess it wouldn't be too hard to just get them from the velocity change
yeah, that's probably a fine estimation
yeah that's what i figured just mentioned it in case there was some other reason to believe the behavior has changed
I'm not changing the transform.. but it's possible I have the component in the wrong place. Gonna look at that again when I have time
thanks, I double checked and I had added the interpolation component at the wrong point in the hierarchy. Now they work well 👍
only thing is that they are sticking to each other a bit... seems like a separate issue though
might just solve this by making all of these push away from each other.. it does seem like when the colliders get pushed into each other they kinda get stuck together
try adding a CollisionMargin maybe
commands.spawn((
RigidBody::Dynamic,
Collider::trimesh_from_mesh(&mesh),
CollisionMargin(0.05),
));
alternatively there's some trimesh flags that might help https://idanarye.github.io/bevy-tnua/avian3d/collision/collider/struct.TrimeshFlags.html
this helped a lot, thanks for the info 🙏
Am I good to update to 0.15? I see that avian supports the RC
main branch uses 0.15 yeah
Ah the README still says 0.15 RC, it's supposed to be the actual 0.15 release
I'll fix that
But yeah I'm not adding any more significant breaking changes or features for Avian 0.2, mainly working on release notes and the migration guide
since updating bevy to 0.15 and avian3d to main branch, my logs are endlessly filled with
Warning: The vector given to `Dir3::new_unchecked` is not normalized. The length is 0.9997351.
Warning: The vector given to `Dir3::new_unchecked` is not normalized. The length is 0.9997351.
Warning: The vector given to `Dir3::new_unchecked` is not normalized. The length is 0.99948585.
Warning: The vector given to `Dir3::new_unchecked` is not normalized. The length is 0.99948585.
Warning: The vector given to `Dir3::new_unchecked` is not normalized. The length is 0.9995516.
Warning: The vector given to `Dir3::new_unchecked` is not normalized. The length is 0.9995516.
Warning: The vector given to `Dir3::new_unchecked` is not normalized. The length is 0.99973494.
Warning: The vector given to `Dir3::new_unchecked` is not normalized. The length is 0.99973494.
I use transform.look_to() to rotate my entities and if I remove the transform.look_to() the warning goes away.
The system runs during FixedUpdate. Tried using both RigidBody::Kinematic and RigidBody::Static as I currently move the entities via transform.translation.move_towards().
Normalizing the rotation after transform.look_to() also does not help:
transform.look_to(direction_norm, Vec3::Y);
transform.rotation = transform.rotation.normalize();
assert!(transform.rotation.is_normalized(), "Failed to normalize rotation");
(The assert never triggers, so I'm fairly certain the rotation is normalized 😄)
Any ideas?
is the character controller making it into 0.2?
will a body's angular velocity make its ballistic trajectory curve like it would in air?
and if not, any idea how i could impl that?
Do you mean this PR?
https://github.com/Jondolf/avian/pull/487
It won't be in 0.2. I want to properly explore this area and try various different APIs and approaches first, I just haven't had time for it yet.
There's a ton of different ways to approach character controllers, from both an API and implementation perspective, especially in an ECS context like ours. If we ship a built-in character controller, or even just tools for creating one, I want it to be polished and robust
darn I might just roll my own then
tnua is a bit jumpy and I switched from rapier to avian because the rapier controller wasn't detecting "grounded" state like it should have
I sort of like rapier's API but didn't play around with it that much. I like having full control and having to implement my own gravity and such because it makes it easy to switch off gravity for swimming, climbing etc
You mean like how a spinning ball curves in the air? No, that's very shape-dependent and not something we could realistically implement in a general way. I think this would be the Magnus effect
The Magnus effect is a phenomenon that occurs when a spinning object is moving through a fluid. A lift force acts on the spinning object and its path may be deflected in a manner not present when it is not spinning. The strength and direction of the Magnus effect is dependent on the speed and direction of the rotation of the object.
The Magnus e...
For balls specifically, intuitively I think you could get a rough approximate version of the effect by simply applying a force perpendicular to the movement direction and the axis of rotation
// Something like this maybe?? Scale the magnitude until it feels right
let f = lin_vel.cross(ang_vel);
(I have not tested this)
awesome. yeah i just need it for a ball and it doesn't have to be perfect. i'll test it out, thanks!
is there any way for me to temporarily turn off all of the physics for an object (including collisions) and move it myself by mutating the Transform, and then turn it back on to have it start interacting again from wherever i've placed it?
i know there's Sleeping but the docs say that gets cancelled whenever there is something that would affect it physically
Not conveniently in 0.1, but the main branch has both RigidBodyDisabled (disables simulation and contact responses) and ColliderDisabled (disables collision detection and spatial queries)
ahh, great. i'm on main but rarely remember to not look on docs.rs
RigidBodyDisabled got rid of my Dir3::new_unchecked is not normalized warnings 😄
Is it possible to have multiple systems running in the same app, but running at different time frequencies? From what I see in the examples it all runs off of the app's global FixedUpdate interval. I'm trying to optimize some very taxing simulations involving tens of thousands of entities with a lot of collisions. I want to break up the simulation into coarser resolution parts that can run update much less frequently. So basically several unconnected collision systems running at different frequencies that I can connect manually myself where necessary.
Does avian3d v0.1.2 support bevy 0.15? https://crates.io/crates/avian3d/0.1.2/dependencies the website says it's depending on 0.14 of bevy
I believe only main brainch supports 0.15.
Are there any planned release for 0.15?
you could use Res<Time<Fixed>>> and early return except at some cadence
For sure, but what I'm wondering is how to stop Avian from recalculating ALL collisions on every fixed update. I just thought of a potential solution, actually, using collision groups, if I duplicate my entitie's translation components into different groups and then only update their positions at my desired lower frequency, then even though Avian is still running at its normal frequency I imagine that if none of the entities in a collision group have changed position then there must be an optimization in Avian that prevents it from recalculating collisions for that group.
I'm just writing release notes and the migration guide now, I'd expect the release to be ready within 2-6 days. The main branch won't have any large changes before the release, apart from maybe documentation improvements or small bug fixes, so you can use that in the meantime
Currently, not really, since the engine uses the ECS directly, so the systems operate on all active physics entities and use the same schedules. I can potentially see us making things more flexible in the future though, perhaps making the core simulation systems take generic query filters and providing some system parameters or other tools for stepping physics manually for entities with specific marker components. We could also add something like Rapier's AdditionalSolverIterations once we have simulation islands, allowing you to roughly control simulation fidelity for specific entities. Not sure what kinds of things we can reasonably support, but these things are definitely worth exploring
Thanks for the info!
Whats your timeline on simulation islands? That's the thing we need to get sleeping that doesn't suck right? 🤔
Cause the current sleeping implementation is the archnemesis of my rollback library 😂
I made an issue for it last week, I'd really want it for 0.3
https://github.com/Jondolf/avian/issues/578
that, and the joint rework ideally
(i.e. impulse-based joints with motors, servos, nicer limits, nicer stiffness configuration, local frames, and other improvements)
I wonder how well simulation islands as entities would perform lol
Only one way to find out 🧠
Technically it would kinda make sense, assuming we're going for persistent islands; islands need identifiers (Entity), you want to be able to mark them as sleeping or as candidates for splitting (marker components), and you want parallel iteration over them
could even use relations if we had them :P
but it'd still involve a lot of spawning and despawning
and I would assume that it'd probably have more overhead than just having islands chucked in a resource
I guess I'll have to try both lol
okay, I rewrote the camera stuff myself, and even when it's not following anything I get jittery movement on some runs, and not jittery movement on others, any other ideas of what might be going on?
that sounds like system ordering
there is some system you depend on that you need to explicitly order against
my guess is it needs to be before transform prop if you are doing this in PostUpdate, or after some physics system. I don't know the full context though, just a drive by.
could this be happening from movement stuff? just changing velocities or dampening without proper ordering?
the example didn't seem to use any ordering stuff for that kinda thing
Possibly.
I think it's something else, if I disable the damping, and touch the move once, the thing keeps moving but also keeps jittering without input coming from anywhere 🤔
found it, it was the TransformInterpolation thingy
hello
im using avian2d and creating a world with vec2::zero gravity, when i create a rigidbody dynamic entity with a collider, and update linear velocity briefly (like a tap the button and increase velocity for a few updates) it goes back down to 0 velocity. but if i increase it enough it stays moving with constant velocity. what happening?
getting this panic:
thread 'main' panicked at /home/ty/.cargo/git/checkouts/avian-b0a258754c9e72b0/b47c30c/crates/avian3d/../../src/collision/collider/mod.rs:369:9:
assertion failed: b.min.cmple(b.max).all()
it happens in collision::collider::backend::update_aabb
not sure if it's my messy hacking or not
possibly relevant details: i'm adding RigidBodyDisabled and ColliderDisabled to a ball and then manually updating its transform.translation to be equal to the ball carrier's transform.translation each tick until the ball carrier throws it somewhere
at which point i'm setting the ball's translation to somewhere well outside of the collider for the ball carrier and then removing those components
oh i wonder if this happens when i set its translation to somewhere inside a different collider 🤣
This is likely due to Sleeping, which stops simulating rigid bodies when they're moving slow enough for some time, until woken up by some interaction. How slowly are the bodies moving with those small taps?
Indicates that a rigid body is not simulated by the physics engine until woken up again. This is done to improve performance and to help prevent small jitter that is typically present in collisions.
(also see the docs of the component if you want to disable or tune the sleeping behavior)
Yeah this often means that the extents of the AABB somehow became NaN or otherwise invalid. I don't have time now but I can try to reproduce and debug tomorrow
time for me to learn about AABBs
an axis-aligned bounding box is just the minimum and maximum extents of the collider along X, Y, and Z
like a sphere with a radius of 1 positioned at [2.0, 1.0, 2.0] would have an AABB like
ColliderAabb::from_min_max(Vec3::new(1.0, 0.0, 1.0), Vec3::new(3.0, 2.0, 3.0))
gotcha. i don't think i am manipulating any collider extents
It's in global space so it changes as your colliders move
so the position or rotation could be NaN somehow
which would cause the AABB to be invalid
for debugging it could be useful to log the AABB, position, and rotation of the entity that seems to be causing the crash
(right before the crash if possible)
there aren't that many entities and this happens fairly quickly so i'll just log all of them. brb
ok the position of the ball is Vec3(NaN, NaN, NaN)
ok i think i know what the problem is:
commands.spawn((
Ball::default(),
SceneRoot(assets.load(GltfAssetLabel::Scene(0).from_asset("ball.glb"))),
ColliderConstructorHierarchy::new(ColliderConstructor::TrimshFromMesh),
));
using ColliderConstructorHierarchy to attach the Collider components to a child of the entity rather than the entity itself makes using ColliderDisabled slightly not obvious
i suspect applying ColliderDisabled to the parent entity doesn't do what we want?
yeah ColliderDisabled currently only affects the entity it is on, so it won't affect the children in this case
If it's a rigid body, you could use RigidBodyDisabled instead?
i'm using both
moving the collider onto the parent entity seems to work at least to prevent the Position from becoming NaN
but it still panicked on the same assert, later on
still investigating
hmm
is it possible to tick the physics simulation manually?
I want to do server-authoritative multiplayer with prediction/reconciliation, so when I get an authoritative state from the server I need to rewind the state of things and re-run physics to catch up to the current frame
ok i've resolved the panic. definitely my error. but i do think it was fruitful to identify the ColliderDisabled gotcha
should i be creating rigid bodies with a transform component or should i use the position and rotation already included?
they definitely need a transform to be rendered at least (not sure if required), and I think the library is intended to be used so position and rotation could be ignored by the user, since they are automatically synced to the transform
@keen ridge
ok
so theres no difference if im querying for transform::translation or rotation and rigidbodyqueryonly::position and rotation? it feels kind of redundant
Position and Rotation are global physics positions, and have different representations for 2D and 3D. Transform is local and relative to the parent.
In general, I consider using Transform to be more idiomatic, and Position and Rotation are more for physics internals
But you can use them directly too if you want, some people prefer that
Yep, see the part about stepping physics here
https://docs.rs/avian3d/latest/avian3d/schedule/struct.Physics.html#pausing-resuming-and-stepping-physics
ah that seems easy enough. Would I need to rewind Time<Physics> resource or will it be fine just to advance it?
and thanks !'
I think just advance it
you can also pause the clock if you only want to advance it manually
that makes sense, keep the clock paused and advance it manually like in the example
I'm guessing I should advance that during FixedPostUpdate if my game logic is all in FixedUpdate
hm I might have to rewind the time if that's even possible - run_physics_schedule seems to override the default Time resource which I'm guessing would mess up my other systems that use the Time resource
my project compiles with bevy 0.15 👍 now onto the speedrun challenge of figuring out why the physics are broken before the migration guide is out 😛
the docs now say that the ShapeHitData.point2 and point1 are given in world space, but I'm pretty sure they are given relative to the respective shape positions. So, to get the world position, you would need hit_data.point2 + shape2.position
in practice, you would probably do something like
hit_data.point2 + cast_origin + cast_dir * hit_data.distance
to get the contact point in world space
ok I now tested with a rotated shape, and it actually affects the results (despite the shape being a sphere), so maybe the point2 is given in local space after all
but I'm pretty sure at least normal1 is given in world space
probably swapping from postupdate to fixedupdate
Well, this is a new behavior
... forgot to filter out my entity from the spatial query rookie mistake
Anyways, I'm experimenting with moving the solver logic into avian's collision detection and keeping the actual sliding plane solving logic separate
https://docs.unity3d.com/6000.0/Documentation/ScriptReference/RaycastHit-textureCoord.html Is it possible to get the UV Coordinates of a mesh with a raycast like in Unity?
ah, major thing was that I was relying on Mass to be well defined, without me ever setting it. Switching to ComputedMass fixed most issues
is there a way i can access a rigid bodies "acceleration"? like isntead of accumulating linear velocity, i just set an acceleration value, like on button press, i give it an external force, and remove it on release? so that i can query for it and implement behaviour depending on its acceleration ts
Is it possible to get the current angle of a revolute joint?
Alright, I had to take a couple of days off to de-stress from stuff, but I have the release notes and migration guide pretty much ready now
I intend to release 0.2 tomorrow 🪶
There isn't really a joint API for this currently, but for 2D you'd get the Rotation of each body, and use rotation1.angle_between(rotation2). In 3D it's slightly more complicated
We'll probably have an API to get this from the joint directly after the planned joint rework
ah got it, thank you. I saw the get_rotation_difference() method in 3d and thought I could derive the angle from that.
quick question regarding timings: if I change the Transform (or Position?) of an Entitiy with a Collider could a SpatialQuery detect the new collision before the next PhysicsTimeStep?
If you update the Position and then call SpatialQuery::update_pipeline, then yes
the update is currently relatively expensive though
good to know, thank you! 🙂
can someone sanity check the lightyear avian setup plugin for me please – shouldn't there be a .chain() on the tuple with the three physics sets? https://github.com/cBournhonesque/lightyear/blob/main/lightyear/src/utils/avian2d.rs#L15
Logically yes, but the ordering is defined by Avian's PhysicsSchedulePlugin already, so it shouldn't be necessary I think
https://github.com/Jondolf/avian/blob/b47c30c5ef231e3ce27435b56604b638ba5f9f4e/src/schedule/mod.rs#L66-L75
if lightyear configures them in a different schedule will that ordering still be preserved i wonder
Avian uses whatever schedule is given to it (FixedPostUpdate by default on main). If that matches lightyear, it should have the correct ordering
If lightyear and avian use different schedules then yeah it'd be wrong, but at that point the system configuration in general would be wrong since it'd be for the wrong schedule
ok yeah i think it's probably ok. there's some older code still using FixedUpdate and doing app.add_plugins( PhysicsPlugins::new(FixedUpdate)... but in that case it sets up the ordering correctly anyway i think.
thanks 🙂
This is quite likely to be user error, but I'm seeing
error: cannot find macro `round_op` in this scope
--> /Users/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wide-0.7.30/src/f64x2_.rs:652:36
|
652 | Self { sse: round_m128d::<{round_op!(Nearest)}>(self.sse) }
| ^^^^^^^^
error: cannot find macro `round_op` in this scope
--> /Users/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wide-0.7.30/src/f32x4_.rs:683:35
|
683 | Self { sse: round_m128::<{round_op!(Nearest)}>(self.sse) }
| ^^^^^^^^
error: could not compile `wide` (lib) due to 2 previous errors
warning: build failed, waiting for other jobs to finish...
on MacOS builds in CI. The wide dependency seems to be from avian via bevy_heavy. Just an FYI, I'm not really sure if Mac support is high on the list at the moment! (I don't particularly need it, was just setting up a new project).
I don't have wide in bevy_heavy though 🤔
I think it can only come from simba through Nalgebra
since that does use wide

holy skibidi
looking good! the bit about debugging to find a flipped sign made me wince 😬 I wonder if physics engines have the highest ratio of time spent debugging to patch fix size
here is my spaceship. 2024-12-21T11:57:28.570342Z INFO space_game::spaceship: Applying force: Vec2(-7071.0684, -7071.067) from: Vec3(7.5, 12.5, 0.0) 2024-12-21T11:57:28.570356Z INFO space_game::spaceship: Applying force: Vec2(7071.068, 7071.068) from: Vec3(-7.5, -12.5, 0.0)
i apply forces from the top right and bottom left of the rectangle, both inwards at 45 degrees. shouldnt this make the spaceship rotate? because it isnt
Arent your force vectors facing eachother? if you drew them it would be easier to see that
does this not make it spin around
I believe they are canceling eachother out. if they were parallel it would spin
they are parallel
He means "if the lines were going at 180 degrees both (but in different directions)"
Congratz on the 0.2 release!
Just noticed two warnings on my derive(PhysicsLayer) after updating:
unexpected `cfg` condition value: `2d`
no expected values for `feature`
using a cfg inside a derive macro will use the cfgs from the destination crate and not the ones from the defining crate
try referring to `PhysicsLayer` crate for guidance on how handle this unexpected cfg
the derive macro `PhysicsLayer` may come from an old version of the `avian_derive` crate, try updating your dependency with `cargo update -p avian_derive`
see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
`#[warn(unexpected_cfgs)]` on by default
the second warning is the same but with "3d" instead of "2d"
did not find anything in the 0.2 release notes or in the docs regarding this, any clue?
Have you run cargo update?
yep, and cargo clean. did also try cargo update -p avian_derive to no avail
Hmm I'm not getting it with a fresh setup
I just tried compiling with rust stable v1.83 and there are no warnings, so this seems related to the current rust nightly (I used nightly v1.85 before)
Yeah, I'm getting this warning too and I'm on nightly.
It looks like that the macro expands to:
#[cfg(feature = "2d")]
use avian2d::prelude::PhysicsLayer;
#[cfg(feature = "3d")]
use avian3d::prelude::PhysicsLayer;
impl PhysicsLayer for FooBarLayer {
...
}
It seems like a reasonable warning to me, so I'm surprised that it doesn't warn on stable
The macro currently works because the user is likely to have PhysicsLayer in scope from the prelude. But if you do:
#[derive(avian2d::prelude::PhysicsLayer, Default)]
pub enum FooBarLayer {
#[default]
Foo,
Bar,
}
It'll fail: cannot find trait `PhysicsLayer` in this scope. It'll probably also fail if there's a 2d/3d feature actually present.
👋 Just updated my game to use 0.2.0, and noticed that now occasionally the player character seems to noclip through the floor of specific portions of the level. This seems to:
- Happen consistently for the same floor pieces every time it happens
- Only happen for floor pieces with relatively thin colliders (I'm using convex hull colliders)
- Only happen for some of the app runs
- Happen far more often on my laptop which has a 120hz display than on my desktop which has a 60hz monitor
- When the debug gizmos are enabled, the colliders are displayed normally, even when the noclip happens
Is it possible that this is related to the schedule changes and my character controller? I originally adapted it from the kinematic character controller example, while still under 0.1, and this wasn't happening
Tweaked my spectator camera setup to allow teleporting the player entity back to inside the map, so I can try falling through the same collider multiple times. Looks like for whatever reason, after upgrading to 0.2, for some of the entities the Collider component is registering but the RigidBody::Static is not having any effect, the same "broken" floor piece is also intangible for the concrete crumbs that I spawn whenever the player shoots the walls/floor, but is a valid target for shapecasts
I know this is gonna sound like an ifei fix, but just increasing the density of the collider will probably avois the clipping issue
As it gives more time, for the get out of me systems to act
maybe change the systems from update to FixedUpdate
Okay I think I've figured it out, though I don't fully understand it. It isn't the collider size or the velocity of the character, as I was able to find larger colliders in the stage with the issue, and tried very slow velocities. I have a system in place to remove/add colliders and rigid bodies to entities on the map based on proximity to the player/other character entities, to reduce the overall number of rigid bodies in the scene. When I disabled that system, the issue went away. (Even if the app ran much slower)
I did some further experimentation, and turns out if I remove an entity's Collider but not it's ColliderAabb, and then add the collider back, it seems to occasionally corrupt/desync the acceleration structure used by the broad phase, so even though the entities still have all the components, the collisions fail for them. When I modified my system's logic to remove the ColliderAabb and re-add it along with the Collider, the issue went away.
(This system runs in Update schedule, so I thought maybe there was potentially some weird timing/sequencing interaction with the system that updates the acceleration structure. I changed it to FixedUpdate and the bug didn't go away)
Something I don't understand is that we now have ColliderDisabled, and I tried to modify the system to use that instead of removing the Collider, as perhaps that would be a less error prone method of getting this to work, and the noclip bug persisted. However, the system that adds entities back to the acceleration structure specifically seems to handle this case so it should work?
is it really necessary to remove the colliders? I thought avian did a lot of that work for you
It does a lot internally, but the map has a lot of static rigid bodies (50,000+) and that was taking its toll even with all the optimizations. IIRC I tried to just remove the rigid bodies but not the colliders, but had issues with that as well
I did a very naive "buckets" setup, tuned to my specific map size/scale where each rigid body can be in one or more buckets on a grid and the buckets can be flagged as active or not, to stash and unstash the rigid bodies and colliders, and that brought it up from 20-30 FPS to 120+ FPS
But there's probably a better way of doing this, it's very hacky
ColliderDisabled and RigidBodyDisabled are probably parts of a more proper solution now that they're available (until simulation islands at least? at which point maybe the 50k+ rigid bodies will be totally fine without any extra work) but I tried using them and still hit the same spurious noclip bug
I think having 50k rigid bodies will always end up having some cost over having say 500 relevant ones. Especially since afaik Jondolf didn't actually finish that rework to a BVH broadphase, nor swap out parry for something with more reliable performance characteristics. I'd imagine anything doing change detection also gets expensive at 50k, and there are some optimizations bevy could do for that
I'd imagine simulation islands don't actually affect how static bodies affect scaling however, since if they did the whole world would just have to become one big island
Hmm, I imagined maybe static bodies outside of any islands would simply be skipped entirely and never touched
but overall adding and removing the rigid bodies has been very straightforward, even if it occasionally requires tweaks, like in this case I had work around this ColliderAabb issue
Even skipping them would require some lookup, which I guess optimally would be a BVH, so 500 vs 50k ends up maybe doubling the depth instead of requiring 100x as many iterations
Ah yea the broadphase rework is in the "whats next"
Also can't find any details on switching to the "driver component" approach (StaticBody, DynamicBody, etc deciding the requires instead of RigidBody) to avoid adding components to static/kinematic bodies that they don't need, but doing so could reduce the number of entities that match a query and also speed up any change detection that happens 🤔
Yea I want something like this, though they'd probably require some shared RigidBody or RigidBodyBase component to make queries acting on all types of bodies nicer, and I'm not 100% sure what the semantics for that should be. A user adding RigidBody would be a footgun if you were required to specify a body type for it to do anything
Could make it so that users can't ever construct a RigidBody 🤔
I guess it could also be called RigidBodyMarker or similar to make it more explicit that it's just a marker component and not a "driver component" but it's still not entirely clear
Calling it RigidBodyMarker could definitely be clearer (and simplify migrations since the errors would make sense)
Another option would be to make RigidBody default to a dynamic body, and make KinematicBody and StaticBody require it, but then everything requires the components of a dynamic body again
Also wouldn't get the cool benefit of finally being able to query only the types you want
You could technically, just a bit more annoying for dynamic bodies
- Dynamic:
Query<Entity, (With<RigidBody>, Without<KinematicBody>, Without<StaticBody>)>(could have a type alias for the filter) - Kinematic:
Query<Entity, With<KinematicBody>> - Static:
Query<Entity, With<StaticBody>>
but yeah I think I'm leaning towards DynamicBody, KinematicBody, StaticBody, and a shared RigidBodyMarker
what's the difference between static rb and a collider without rb?
a Collider currently doesn't have collision response
i.e. other things can't bump into it
is that behavior required for kinematic or something else?
not really
then wouldn't it make sense to just assume collider is static, unless dynamic/kinematic/sensor is added?
I think that is an option, yeah
on the other hand static marker might be really useful for other stuff
In general, all your physics objects that participate in the simulation are rigid bodies, and you attach collision shapes to them; if we get rid of explicitly marking static bodies, you kinda lose that "thing that shapes are attached to"
but I think that may be fine for static geometry
immutable by default 😤
what do I need to make this plugin work? I've implemented it like so:
app.add_plugins((
DefaultPlugins,
PhysicsPlugins::default(),
PhysicsDebugPlugin::default(),
));
Added the player:
#[derive(Component)]
pub struct AvianRigidBody(pub RigidBody);
#[derive(Component)]
pub struct AvianCollider(pub Collider);
#[derive(Bundle)]
pub struct PlayerBundle {
pub _marker: Player,
pub rigid_body: AvianRigidBody,
pub avian_collider: AvianCollider,
pub sprite: SpriteBundle,
pub linear_velocity: LinearVelocity,
}
pub fn setup_player(
mut commands: Commands,
asset_server: Res<AssetServer>,
texture_info_cache: Res<TextureInfoCache>,
) {
commands
.spawn(PlayerBundle {
_marker: Player,
rigid_body: AvianRigidBody(RigidBody::Dynamic),
avian_collider: AvianCollider(Collider::rectangle(0.5, 0.5)),
sprite: SpriteBundle {
sprite: Sprite::default(),
texture: /* ... */,
transform: Transform::from_xyz(0.0, 0.0, 300.0),
..default()
},
linear_velocity: LinearVelocity(Vec2 { x: 10.0, y: 0.0 }),
});
}
Then ran a simple system to display its position:
for (velocity, transform) in players_query.iter() {
println!("linear velocity: {}", velocity.0);
println!("pos: {}", transform.translation);
}
result:
linear velocity: (10, 0)
pos: (0, 0)
linear velocity: (10, 0)
pos: (0, 0)
It's not affecting the Transform at all. What did I do wrong? It's not displaying any debug info either, no errors. I am on bevy 0.14 and avian 0.1.2.
Your entities don't have RigidBody or Collider components, only your own wrappers
@marble cradle are meshe colliders simplified and have a convex hull? is terrain and everything else large in chunks? those are the only knobs i know, maybe someone here knows more
yes and yes. it runs pretty well on my pc now that i upgraded it but struggles on my laptop stiill which is weird
at least i can work w it now for development
Got an initial implementation of a contact graph working 👀
It's a modified version of petgraph's UnGraph (undirected graph) along with an entity index to map Entity IDs to node indices in the graph. Largely similar to what Rapier has with its Graph, Coarena, and InteractionGraph.
Box2D seems to handle the contact graph by maintaining doubly linked lists on each body directly, but I feel like it'd be trickier to make that work nicely API-wise, and it seems less flexible
Still need to make a bunch of changes to handle edge addition and removal properly and emit collision started/ended events as part of the narrow phase, instead of the current scuffed way it's handled
Also need to try changing the narrow phase to maintain bit arrays for edge addition/removal, like Box2D
After that stuff is done, simulation islands should hopefully be easier to implement. I already have the core logic and wiring for island merging and splitting, but got blocked on needing this contact graph
It's just a graph where nodes are bodies/colliders (entities) and edges are contact pairs. For a lot of things, like island splitting, you need efficient look-ups for all contacts associated with a body, and with a graph that is trivial. The old/current storage for Collisions on the other hand is just an IndexMap, which requires you to iterate over all contact pairs just to find contacts associated with a specific entity
The current Collisions::collisions_with_entity is also very slow because of this, since it's O(n), where n is the number of collisions in the world. With a graph you can just directly look up the edges associated with the entity
We'll also want a constraint graph for joints, it's needed for at least simulation islands and it will let us efficiently handle disabling collisions between bodies attached via joints
pub fn m4_shooting(
mut m4_props: Single<&mut M4>,
mouse_input: Res<ButtonInput<MouseButton>>,
mut shooting_event_writer: EventWriter<ShootingEvent>,
) {
if mouse_input.just_pressed(MouseButton::Left) && m4_props.okay_to_shoot {
shooting_event_writer.send(ShootingEvent);
}
}
pub fn ball_contact_checker(
ball: Query<Entity, With<Ball>>,
crosshair: Query<Entity, With<CursorCrosshair>>,
mut hit_event_writer: EventWriter<HitEvent>,
mut shooting_event_reader: EventReader<ShootingEvent>,
mut collision_event_reader: EventReader<Collision>,
) {
let ball_entity = ball.single();
let cross_entity = crosshair.single();
for _event in shooting_event_reader.read() {
for Collision(contacts) in collision_event_reader.read() {
if contacts.entity1 == ball_entity && contacts.entity2 == cross_entity {
info!("test");
hit_event_writer.send(HitEvent);
}
}
}
}
i have this basic system when i click left mouse button, it sends shooting event. if two collisions are touching eachother when shooting event happens, a hit event send. for some reason sometimes it sends two hit events and sometimes it send one hit event. i tested this with info!("test") is it something with avian or am i doing something wrong?
You can break the loop, to send just one event.
I guess i can but i wanna learn why its not consistent
A rough guess would be that it's because of the fixed timestep causing physics to run twice on some frames to catch up to real time, causing your system to get events from two different physics ticks within a single frame if that system is not running in a schedule like FixedUpdate
not sure though
well i guess its a good guess but i cant put those systems in fixedupdate
Working on updating the WIP kinematic character controller draft
Using the latest iteration of vidar's version
Example doesn't seem to want to build though
... Not related to avian though, wgpu-hal being wgpu-hal 
Hey, I've noticed an issue with change detection, wanted to check if it's already on your radar here before opening a PR or anything: because the functions in semi_implicit_euler take their mutable inputs as lin_vel: &mut Vector etc., the systems in integrator call them using &mut body.lin_vel.0 etc., which immediately triggers change detection and the "has this actually changed" checks inside the integration functions don't have the desired effect (the Mut from the query has already been deref_mut()-ed). Changing those functions to accept the whole body: *IntegrationQueryItem structs would fix change detection, but break being able to call them easily in tests
I've done some messing around and found that changing the parameter types from e.g. lin_vel: &mut Vector to mut lin_vel: impl DerefMut<Target=Vector> does the trick - passing a &mut ref in tests still works and using body.lin_vel.as_deref_mut() in the systems works to project out the inner type from newtype components (more generally, mut_component.reborrow().map_unchanged(|c| &mut c.field) can project out arbitrary fields as well)
I've got a branch where this should be fixed (at least in the cases I've found, I might be missing some) that I can send in a PR from if that'd be helpful
@vestal minnow https://github.com/Jondolf/avian/pull/606
Merry christmas, this was a ton of work.
Major notes from the original implementation attempt: Split pass system, (gravity and movement are done in separate passes, meaning gravity can be constantly applied. This does come with the sideeffect that jumping needs to set gravity instead of the velocity), it has a backout during a gravity pass to only prevent sliding behavior. Basic depenetration.
I need to rename all the component names still.
Also, didn't end up using linearvelocity though I probably should. It was mainly not done for the take of simplicity, there's probably several components that could be rewritten to use avians instead
i had to do manual gravity even in dynamic controller, i don't see why you wouldn't want it in kinematic controller D;
Thanks, feels a lot better than the original! IIRC that one got stuck very easily when I tested.
Some initial things I noticed based on a quick test (I haven't reviewed the actual code properly yet)
- If I jump at a ceiling, the character hovers in the air rather than stopping and falling down
- Sometimes, walking down slopes is jittery
- When the character is in this "state", walking off of a ledge will make the character fall at an abnormally high speed, implying that there's some built up gravitational acceleration
- There's some jitter and other weirdness when walking into a crevice with two unclimbable surfaces
- Landing on the floor feels "softened", it's not an immediate stop
Walking down slopes being jittery might be a grounds detection issue, will need to adjust
Landing on the floor is softened to avoid doing a floor snap, but I can maybe come up with a better way to do that
Maybe only start applying the gravity reduction if you're grounded for two ticks
The ceiling issue might be fixed by adjusting gravity velocity after the collide and slide pass if gravity is going backwards from its application direction
The intent of the soft landing was to prevent situations where you just downwarp cause you're building up speed as you're on the ground without having the character controller float at a variable height due to the ground detection
If you have a better solution let me know
Right, makes sense
Btw lemme know if you want this demo scene, I just whipped it up in Blender a few days ago. Dunno if you already have a better one
it should also have a checkerboard ground shader but idk how to make it show up in Bevy :P
Would make a much better test scene yeah
You can add it to the pr if you want
I also need to figure out why the macos compile breaks due to expecting dvec3, is the test runner running with both the single and double precision features enabled?
Yeah, CI checks that both f32 and f64 compile. Maybe for examples it could be better to somehow only use f32 though
@vestal minnow why does avians SaP check for layers after the x axis check? Wouldn't it be simpler/faster to have separate intervals for each layer?
https://github.com/Jondolf/avian/blob/main/src/collision/broad_phase.rs#L232
The AABBs are just sorted along the x axis with the current impl. Checking for disjoint x intervals is the primary pruning mechanism and needs to be done first. The order was originally different, but checking x first is a 10x improvement
https://github.com/Jondolf/avian/pull/219
Oh i guess its so that the user can define custom interactions between multiple layers
Hm alright, i suppose its faster cuz it would need to deref the values first while the aabb is available already and it can benefit of the discarding of all disjoint x axis pairs
To be clear the current broad phase is super basic and just always sorting along x isn't good :P the plan is to switch SaP to separate BVHs for dynamic, kinematic, and static bodies
with support for incremental updates
Do you plan to support other broadphase backends someday (hshg, quad/oct trees, ...?) or just SaP?
I suppose SaP is prob the most used in other game engines so it makes sense to have that as a default
It's just a plugin so it should be replaceable either way, dunno if there'd be first-party alternatives though
No, Box2D, Bepu, Jolt, Godot physics etc. all use a dynamic BVH or other similar tree structure
Rapier is the only one I know uses SaP, though it's not traditional SaP but its own unique "Hierarchical SaP" with grids and stuff
ah PhysX also used to use SaP, it's different now though
oh okay hm, im pretty sure physx was using sap
When did physx move off sap?
Hmm, or I might be partially wrong 🤔 PhysX does have SAP as an option, but also has several "box pruning" algorithms that are bounded to "regions of interest", I just haven't found anything about what it does inside those regions
- sweep-and-prune (SAP)
- multi box pruning (MBP)
- automatic box pruning (ABP)
- parallel automatic box pruning (PABP)
- GPU broadphase (GPU)
okay yeah the box pruning algos do seem to be just SAP applied to bounded regions, where the automatic variants compute those bounds automatically
makes sense, iirc the reason why they used sap in the first place was that they can easily offload it to the gpu
Ooh nice, my new narrow phase seems to be a decent amount faster than the old one already, based on some quick tests. Very WIP though, and not even parallelized yet
Hmm I do need to figure out contact callbacks/hooks, I'm not sure if I can keep a whole schedule like PostProcessCollisions in between contact computation and contact event stuff
I want to make my terrain into a collider; it's currently a patchwork of triangle meshes [since I intend to have some holes and overhangs, a heightfield unfortunately won't work for my purposes]. My question is: to what extend does the resolution of the trimesh collider impact performance? For large, static meshes, are they automatically treated with some kind of spatial lookup, or is there something I can do to help avian accelerate collisions with the terrain mesh(es)?
or, for example, is it better to have one larger trimesh collider, or to split it into, say, 16 smaller patches which are each their own trimesh collider?
Well I think I got contact hooks with (almost) full ECS access working 👀
The main caveats are:
- You can only have one of each type of hook in the world (a contact filter hook, a pre-solve hook, and maybe some others)
- There are a few specific components and resources you cannot query for, at least not mutably. You get a runtime panic if you do.
It's very similar to bevy_rapier's physics hooks. It's a shame you can only have one of each hook, but I think the limitations are probably acceptable, they're similar in most engines I know (Rapier, Box2D, Jolt...)
Parry stores bounding volume hierarchies for the triangles in trimeshes, so it should be decently optimized for lots of geometry. For very large terrain colliders it might still be worth splitting into smaller chunks, but I haven't tested that so I'm not entirely sure
Ahah, I see - it turns out that this isn't actual Avian-specific, but it's consistently happened to me when I've tried to integrate avian into my Bevy project, and now I know the cause/fix: I was having a weird issue where for the first ~5 to ~10 seconds after starting the app, the game was running at like 2-5FPS. And then, over that time period, it gradually improved until it ran completely smooth.
The problem is that my setup function is really slow. On its own this would be fine, but it creates a big time debt in the Time<Fixed> resource, which means that FixedUpdate needs to run many times in order to catch up. But when all of these physics objects spawn, that takes a while, so it's barely able to catch up, so there's still a big time debt. My desktop is a bit faster, so it's eventually able to make up for the lost time, but it takes several seconds (and many iterations) to do this.
The fix is to just cap the amount of FixedUpdate time delay (at least initially) - I added app.insert_resource(Time::<Virtual>::from_max_delta(std::time::Duration::from_millis(60))) and now it catches up basically instantly, so it runs smoothly.
Is avian keeps track of collider's shapes? For example capsule, cylinder etc.
@vestal minnow Early morning question, should I have been using shape_hits instead of cast_shape in the kinematic controller?
Seems to be a no, rewriting it to use shape_hits seems to introduce ghost collisions, hm
Jondolf can you send me that better physics testing ground you have
I think I might have made a noteworthy improvement here, using shape_hits and computing a weighted average normal I think might have made it more reliable
Its pretty expensive to do though
Really depends on what you want. shape_hits gives you all the hits without any meaningful ordering, cast_shape tries to give you hits in order (but iirc it's not perfect)
The underlying representation still contains that data (it's needed to calculate collisions after all), but it is in a different format from how you likely constructed it and I don't think there is a nice way to get it back in the exact same format 🤔
In short, what I ended up doing is this:
let hits = spatial_query.shape_hits(
&controller.collider,
transform.translation,
transform.rotation,
velocity_dir,
4,
&config,
filter,
);
if !hits.is_empty() {
// Use the closest hit for safe movement distance
let closest_hit = &hits[0];
let safe_distance = closest_hit.distance;
let safe_movement = current_velocity * safe_distance;
transform.translation += safe_movement;
total_movement += safe_movement;
current_velocity -= safe_movement;
if let Some(aggregate_normal) = compute_weighted_normal(&hits) {
last_hit_normal = Some(aggregate_normal);
if is_gravity_pass && should_stop_on_slope(slope, floor_detection, aggregate_normal) {
continue;
}
current_velocity = calculate_sliding_velocity(
&mut collision_planes,
aggregate_normal,
current_velocity,
);
}
} else {
transform.translation += current_velocity;
total_movement += current_velocity;
current_velocity = Vector::ZERO;
break;
}
Its fairly expensive
Don't have an exact benchmark, but it seems more reliable than all my approaches so far
I think the way shape_hits works does make it more expensive yea
Sadly with character controllers it's rare that getting "the first hit" (which often isn't even accurate) is correct
With ground detection for example getting 1 hit makes you slide when you're standing on the wedges in Jondolf's demo scene 🤔
I'm hoping using shape_hits (even if more expensive) resolves the pingponging you see here
I'll need to think some more on the design here though still
Though also need to rework the entire gravity system in general
Hmmm ... That might not even be enough if it's one collider with multiple pieces of geometry that clash like that 🤔
Hmmm
There is a chain of methods "caster_shape.shape().as_capsule().unwrap()"
I am also making a kinematic controller. Does yours handle a constant negative velocity on the Y axis? I am trying to fight it.
Yea, it's just that you'll get back the format parry uses while your inputs were likely either a bevy_math primitive of one of the function constructors that matches those primitives. Not a big deal, just a bit clunky if you need to get back the data you put in
Just an FYI in case anyone else strikes this: the MacOS-only issue with Avian builds was down to the use of a feature-gated macro in wide (a dependency of simba). They've fixed the issue, so probably doing the likes of cargo update -p wide or generating a new Cargo.toml will sort things out. https://github.com/Lokathor/wide/issues/186
My child collider sensor is one step behind its parent rigid body position. Is this expected or am I maybe missing something?
probably not expected
I remember there generally being an issue with some gizmos being behind a step, tho I can't remember why
The rigid body axes seem to be at the right spot and only the collider is behind, so it seems like an actual bug
Hmm, for collision hooks, I don't think I can allow mutable ECS access, just read-only access
because it's iterating over the contacts in parallel
Okay I can provide access to Commands with ParallelCommands so that you can at least queue commands inside the hooks and handle deferred changes that way, though the execution order will be arbitrary
Yeah I think these limitations are acceptable
- Only read-only ECS access, but can queue commands, and modify contact data directly
- Only one set of hooks per narrow phase and broad phase
It's about the same caveats as in bevy_rapier, Box2D, etc.
I'll probably make PRs for these things I'm working on roughly in the order of
- Collision hooks
- Contact graph
- Rework contact pair logic (more persistence, bit sets, reworked event handling, contact flags, etc.)
- Simulation islands?
Before merging 2-4, I want diagnostics and better stress tests though, to both see how things improve and to see how it compares against Box2D, so I'll have to work on that
You forgot this one:
- Driver components
Got some more cool stuff cooking up: contact tangent velocities (for e.g. conveyor belts), per-manifold friction and restitution (for e.g. non-uniform material properties for terrain), and some renames and refactors for contact data types
Rapier has configurable friction, restitution, and tangent velocities for each point in a given manifold, but that seems somewhat overkill imo, and engines like Jolt also just seem to have per-manifold material properties
I feel like it makes sense to think of it like contact manifold == contact surface, and each surface has a configurable material, not the points
I suppose that would depend on the craziest manifold one can come up with.
And not what it "feels" like it should be.
It "feels" that the manifold should just be a bunch of points on a plane.
But idk how it actually works out with weird objects colliding.
It is basically just a bunch of points on a plane, often limited to 2 points in 2D and 4 points in 3D (though we don't / Parry doesn't limit 3D atm)
Surely you need that for uh ... a collision between a sphere and a torus where the torus has a gradient of friction properties going one way and a gradient of restitution properties along the perpendicular axis 
Every point you sample has different properties 
(my questioning may or may not be related to me trying to implement soft bodies into avian and not wanting to do extra work 😢 )
(well, not strictly on a plane, but they share the same normal)
Those would be two different manifolds
A manifold describes a contact surface, I would consider those to be two different surfaces
I mean usually things that aren't convex are also separated until they become convex for performance too, which would probably guarantee they will always yield separate manifolds 🤔
For example for a trimesh, each triangle has its own manifold, even if it's just a plane
Each collision/contact with a different triangle, not each triangle, you mean?
If a cuboid is resting on a plane formed by two triangles, the contacts for each triangle belong to a different manifold, even though they have the same normal
Yeah, that makes sense. Thanks for clarifying.
Manifolds were this weird thing I saw in parry that stores contacts that I wasn't really sure about what it is exactly.
@vestal minnow I realised that a simpler way to resolve this (at least in that it avoids generic inputs) would be to remove the &mut inputs to those functions, have them return new values instead and do the
if new_value != *value {
*value = new_value
}
checks in the system itself... I think the solution I suggested above is still the smallest in terms of code changes needed though
I noticed something strange when upgrading my project to avian 0.2.0, simple cube colliders started randomly rotating.
I managed to reproduce it in the example repo:
Its hopefully possible to see, some of the cubes just continously rotate.
here is the slightly modified cubes sample where I could reproduce it.
(yes the colliders are smaller than the mesh)
In my project I can remove the random rotations if I increase the mass of the cubes by 10x.
Hmm, seems to be an issue somehow caused by angular inertia. I'll look into it. Thanks for the repro
That would probably be fine. I feel like the deref workaround might be a bit too magicky, though cool
I'm wondering though if we even want the current checks 🤔
If a body is sleeping, it shouldn't run integration for it at all, and otherwise it's being simulated and I would probably expect it's velocity and position to be updated every frame
The only case where that might not be true is if all translational and/or rotational axes are locked (but it would start sleeping), or for position integration when a kinematic body has zero velocity. Or an edge case where a dynamic body has no velocity at zero gravity, but then it should also get marked as sleeping. I'm not sure if these special cases are worth handling, except maybe the kinematic case
Another friction bug I think :/
with zero friction it's fine
Ah, fixed I think! I was including angular inertia terms twice in one part of the effective mass computation for friction, it's pretty subtle
// Broken
part.effective_inverse_mass[2] = 2.0 * (i1_rt11.dot(i1_rt21) + i2_rt12.dot(i2_rt22));
// Fixed
part.effective_inverse_mass[2] = 2.0 * (rt11.dot(i1_rt21) + rt12.dot(i2_rt22));
Thanks again for the repro, this was an important thing to fix
I'll make a PR
I'll probably also make a patch release in a few days
I like how all of these critical fixes that massively improve stability are one line changes
Friction seems like a common source of bugs in avian 😂
The 3D friction is partially based on Rapier's implementation, but the math is handled slightly differently, since Rapier stores angular inertia as the inverse square root and we just use the inverse, and I didn't consider that correctly in this case :P
In Rapier the inertia term should be twice here to get rid of the square root, but we just want it once
Hopefully now the friction should be correct 😂
though we still just straight up don't handle static friction separately from dynamic friction (same as most engines) even though Friction has a static coefficient (unlike most engines)
I should probably try incorporating it again, last time I tried it caused it to be an unstable mess but that might've been because of the other issues, which are now fixed
Hm, that reminds me, I should implement friction into the character controller
Would (probably) remove the need for separate gravity passes and such if the materials you slide upon have friction
Collision hooks!
https://github.com/Jondolf/avian/pull/610
@grizzled depot There may be some things there that could be useful for Rapier/bevy_rapier too. E.g. I figured out a (mildly scuffed) workaround to specify hooks without requiring unnecessary generics like PhysicsPlugins::<()>::default() for the cases where you don't need hooks (you might remember my issue on this). I think it's nicer especially for new users.
I also feel like bevy_rapier's API for hooks overall isn't exactly great, it's pretty low-level and uses Rapier and Nalgebra types. Not sure if there's too much you can do there without significant changes or extra overhead though
Something that may be nice is to expose Commands like I do, since mutable ECS access isn't otherwise allowed in hooks
Oh and small detail, BevyPhysicsHooks in bevy_rapier should probably have a bound on ReadOnlySystemParam, not SystemParam, since it doesn't allow mutable access anyway. It might be more explicit and give errors earlier that way (didn't test in bevy_rapier, just in Avian)
anyone have example code of spawning a gltf and creating a trimesh from it?
I got something working but it feels janky
I'm loading the mesh and material in a Startup system like this
let tree_mesh = server.load(
GltfAssetLabel::Primitive {
mesh: 0,
primitive: 0,
}
.from_asset("tree.glb"),
);
let tree_material = server.load(
GltfAssetLabel::Material {
index: 0,
is_scale_inverted: false,
}
.from_asset("tree.glb"),
);
then storing those handles, and in an Update system I check if any of the handles are loaded then spawn the mesh and collider etc
this may be a dumb question, but is there a way i can visualize the collider heightfield? my mesh is displaying properly but i keep falling through the floor
That looks neat. Does that mean that we should do all collision processing/side-effects inside the CollisionHooks trait now, instead of with systems? What is the -> bool used for?
You can use ColliderConstructorHierarchy on the scene root entity. See the collider_constructors example
A component that will automatically generate Colliders on its descendants at runtime. The type of the generated collider can be specified using ColliderConstructor. This supports computing the shape dynamically from the mesh, in which case only the descendants with a Mesh will have colliders generated.
Yup, add the PhysicsDebugPlugin
A plugin that renders physics objects and properties for debugging purposes. It is not enabled by default and must be added manually.
Yup, ideally all custom contact filtering and modification should be done with CollisionHooks. It avoids unnecessary iteration over contacts that systems would often need to do, and the current PostProcessCollisions schedule might need to get removed as part of some narrow phase optimizations and improvements I'm working on. That's what really prompted me to implement this now
Of course you can access the Collisions resource in any system (almost) anywhere, but filtering and modifying contacts only really works in hooks
The returned bool just determines if the contact pair should be kept. If you return false, that contact is removed. Similar to doing .iter().filter(...)
This should hopefully be fixed now if you run cargo update again 🤞
awesome! thanks!
also noticed in your newest PR the mention of improved "one-way platforms" - I think I asked a few months back for one-way colliders, if I understand this right this would enable this as well?
They're already possible with custom systems, the PR just adds hooks which will be the new recommended place to handle it. But yeah you can implement colliders that only allow collision from one side, like what many 2D platformers have
The one_way_platform_2d example demonstrates one way to do it (this is the old version, it's slightly different in the hook PR)
yeah I looked at the old and new example, was just looking for confirmation I understand it correctly 🙂 thank you!
Does this replace the PostProcessCollisions stuff I currently use for my one way doors? 🤔
Most likely yes
Oh nice so im not the only one needing it. Thanks 🙂
I have a rigidbody dynamic with a ExternalForce with a non zero force and persistent set to true but it's not moving, any ideas ?
I also added the plugin ofc
hmmm well now after looking, first off, i thought the params for the heightfield were the spacing between points, so my collider was tiny lol, but now it seems like my heightfield is rotated o.O
if you're using the plane builder to make your mesh it's probably 90 degrees off, the columns/rows are backwards IMO
I figured it out. My mesh is made with data[z][x] and height field expects the opposite
I have no idea how to make games btw
lol
https://discord.com/channels/691052431525675048/1323964755685150832 if anyone can help me
Excuse me, for the EventReader<CollisionStarted> , when it listens for a collision event between two entities, there is no specific order,right? For example, when a player collides with the ground, sometimes the player is entity1 and sometimes the ground is entity1🤔
Right now I think the entities in events are ordered such that entity1 < entity2, but I wouldn't rely on this
IIRC at least in contact data like Contacts, the order is practically arbitrary
Yes, I found while debugging that it appears to be in a fixed order.
Ok, Tanks a lot!🥳
Huh, TIL that Jolt has enhanced internal edge removal for arbitrary compound shapes, not just trimeshes and heightfields like Parry/Rapier 🤔
This would make tilemaps and such without ghost collisions possible without specialized collider shape implementations
Jolt Physics now has an improved algorithm to avoid ghost collisions. Previously this worked by statically analyzing meshes, but now it also works at run-time on e.g. a grid of boxes like this.
The dream would of course be that it wouldn't be limited to compound shapes, and it'd also work if each collider was on a different body, but I'm not sure if that's possible
Yeah, that's fair enough - maybe the checks are more trouble than they're worth. The thing I had in mind was that it means the world-space angular inertia is recalculated every frame (because Changed<Rotation> always passes) for all bodies that are moving, even if they have zero angular velocity. With lots of rotation-locked objects moving around I could see that being a problem, but it probably wouldn't be an issue in most cases
Yeah, I suppose rotation-locked dynamic objects moving around isn't too rare, given that a lot of dynamic character controllers might be like that 🤔
Still trying to wrap my head around how I'd implement this, but it doesn't seem too bad in theory if I'm understanding it right; get supporting features for contacts, create face contacts in an initial pass, and process delayed contacts (vertices and edges) in sorted order, voiding triangle features in the process
Opened an issue for this at least, I think it'd be interesting to try and implement as an opt-in feature
https://github.com/Jondolf/avian/issues/612
Huh, I realized we can/should make ContactManifold store just a world-space normal instead of a separate normal1 and normal2 in local space
In XPBD it was necessary to recompute transformed normals everywhere, but now we just use the world-space normal computed before the substepping loop for contact constraints, so there's no point storing local normals in the manifold
It'll also make contact modification nicer since you can just modify a single world-space normal
The contact points need to remain in local space though, unless we store a DeltaRotation/AccumulatedRotation for applying rotation changes like Box2D does 🤔
I'm trying to get the width and height values of a rectangular collider. I thought I could do that by querying for the size of the ColliderAabb, but it returned the Vec2: [-inf, -inf]. Any ideas on an alternative to get these values? Should I be using the entities transform?
When are you querying for this? That should just be the initial value on the first frame before the engine has the correct positions and has computed the actual AABB.
You can get the actual collider's size by accessing the raw Parry shape though, it's just a bit annoying
// This is a Nalgebra vector type!
let half_size = collider.shape_scaled().as_cuboid().unwrap().half_extents;
// Convert to Glam and scale to full size
let size = 2.0 * Vec2::from(half_size);
Made an issue for hit events, not something I have bandwidth for right now but they might be nice to add for usability
https://github.com/Jondolf/avian/issues/613
purely an abstraction thing, I think it technically doesn't need any internal changes
Thanks this explains it, was trying to immediately use the aabb size in startup directly after creating the collider in a chained system
Hey, quick question: Are there any debug-tools available for avian? I'm thinking in the direction of gizmos outlining a collider.
A plugin that renders physics objects and properties for debugging purposes. It is not enabled by default and must be added manually.
@errant dome
Thank you! I clearly didn't rtfm 
Anyone know of an example of observers used for collision detections with Avian ?
Does anyone know what schedule and set ordering is required for a system to have access to snap an entity to a given target position when the current physics step would result in arriving or overshooting the target position? But without disrupting any of the other systems. For example, using avian to interpolate grid based movements and maintain proper collision detection? Like I don't want to create a false collision if it traveled the full overshoot distance. And I don't want to snap it if there's something in the way that would have otherwise prevented it.
I'm guessing it would be PhysicsStepSet::First and just see if the remaining distances is less than current velocity step, if so do speculative collisions for collisions and if none, teleport to target position. But I don't think you get access to any of the ccd stuff
can confirm, no more warnings on rust nightly with avian 0.2 👍
I'm looking at ways of implementing collision with an uneven, non-tilemapped, non-rectangular surface in 2D. So for example a sprite walking across an undulating platform. It seems that I might be able to make the platform a bitmask, but I'm not sure if Avian already provides a method to do this or if I'd need to look at a custom collider perhaps?
I'm watching the original creator of Lemmings, Mike Dailly do this and they were basically translating each individual pixel in the mask into a 2D array and checking each lemming to see if they were in contact with the mask! Not sure how sustainable that would be, I guess Noita probably did it to some extent also 😆
I know we have LayerMask in Avian but I'm thinking that's not quite the same thing...
I think the difficulty with heightfield would be complex shapes with overhangs, etc.
y'all, how do you use avian with bevy_ecs_tilemap? I tried adding static rigid body here https://github.com/StarArawn/bevy_ecs_tilemap/blob/main/examples/chunking.rs#L24, but my frame time exploded
A tilemap rendering crate for bevy which is more ECS friendly. - StarArawn/bevy_ecs_tilemap
I'm looking for an efficient way to do physics for procedural tiled worlds like this one https://cdn.discordapp.com/attachments/369169964956909580/1324713511993086013/2025-01-03_12-18-00.mp4?ex=67792719&is=6777d599&hm=85e160ac9a05a35288dc7504e1f09d7b9a152707d63f9c9b54538b04e5bcbf1c&
oh what the hell wait... https://github.com/shnewto/bevy_collider_gen this looks very very close to something I could use
a library for generating 2D colliders for bevy apps from images - shnewto/bevy_collider_gen
mind you that also appears to use heightmaps. Have to experiment with a more complex image 🤔
Hey, I want to do stuff only if a laser and an asteroid collide, right now it looks like this but this feels a bit innefficient
pub fn collisions(
mut collision_event_reader: EventReader<CollisionStarted>,
laser_q: Query<Entity, With<Laser>>,
asteroid_q: Query<Entity, With<Asteroid>>,
) {
let mut maybe_laser = None;
let mut maybe_asteroid = None;
for CollisionStarted(e1, e2) in collision_event_reader.read() {
if laser_q.get(*e1).is_ok() && asteroid_q.get(*e2).is_ok() {
maybe_laser = Some(e1);
maybe_asteroid = Some(e2);
}
if laser_q.get(*e2).is_ok() && asteroid_q.get(*e1).is_ok() {
maybe_laser = Some(e2);
maybe_asteroid = Some(e1);
}
let Some(laser) = maybe_laser else {continue;};
let Some(asteroid) = maybe_asteroid else {continue;};
//do stuff with laser and asteroid
}
}
Is there a better way to do this ?
Hey, is there a way for bodies to stop jumping/hitching when crossing over seams in ground/wall colliders?
Is there a way to have a second larger collider on an object. I still want the one that matches the size for updating the mesh but would like a seperate one for raycasting that is larger so objects are easier to point at
I have a query_collision function which determines which query item is involved in a collision.
pub fn query_collision<'a,D,F>(query: &'a mut Query<D,F>, collision: &CollisionStarted)
-> Option<QueryItem<'a, D>>
where
D: QueryData,
F: QueryFilter,
{
let CollisionStarted(entity1, entity2) = collision;
if query.contains(*entity1) { return query.get_mut(*entity1).ok(); }
if query.contains(*entity2) { return query.get_mut(*entity2).ok(); }
None
}
Use it like this.
It'll work with any query data, not just the Entity.
fn collisions(
mut collision_events: EventReader<CollisionStarted>,
laser_query: Query<Entity, With<Laser>>,
asteroid_query: Query<Entity, With<Asteroid>>,
) {
for collision in collision_events.read()
{
let Some(laser_entity) = query_collision(&mut laser_query, collision) else {
continue;
};
let Some(asteroid_entity) = query_collision(&mut asteroid_query, collision) else {
continue;
};
// do stuff with the collision entities
}
}
Add the raycast collider on a different collision layer which only interacts with your raycasts
Ohhh I forgot about collision layers thank you
Do u happen to also have a solution for shaking objects when they are small
No worries if not ofc but figured I'd ask
can you elaborate on that? are they spinning or motion (translation) is jittery. does it happen after collisions.
thanks
if it's choppy movement, you might want to add transform interpolation/extrapolation.
https://docs.rs/avian3d/latest/avian3d/index.html#why-does-movement-look-choppy
Both and it seems to happen whenever they are colliding with a static ground. I'll give the transform interpolation a shot
your question sounds like what I ran into here
#1124043933886976171 message
back then I did some digging and I found there wasn't a good solution at the time and... I read enough stuff referencing other engines that made me think this is a common, inherent problem. I followed a raycast vehicle tutorial to solve my problem and it works really well (and also reduces collisions which is great)
but there might be other ways to tackle the problem. I'm not an expert on it, just dug into it over time since asking that question
Is it possible to exclude some CollisionLayers from the PhysicsDebugPlugin? I use different CollisionLayers for different RenderLayers, because I have multiple scenes, but I always see all Colliders, which is a bit disturbing.
Or any other way to handle this?
I have a lot of situations where I want to delete objects on collision (bullets for example) but for every one of these I often get a warning that I tried to delete an already deleted entity:
pub fn asteroid_collisions(
mut commands: Commands,
mut collision_event_reader: EventReader<CollisionStarted>,
laser_q: Query<Entity, With<Laser>>,
asteroid_q: Query<Entity, With<Asteroid>>,
mut health_manager: HealthManager
) {
let mut maybe_laser = None;
let mut maybe_asteroid = None;
for CollisionStarted(e1, e2) in collision_event_reader.read() {
if laser_q.get(*e1).is_ok() && asteroid_q.get(*e2).is_ok() {
maybe_laser = Some(e1);
maybe_asteroid = Some(e2);
}
if laser_q.get(*e2).is_ok() && asteroid_q.get(*e1).is_ok() {
maybe_laser = Some(e2);
maybe_asteroid = Some(e1);
}
let Some(laser) = maybe_laser else {continue;};
let Some(asteroid) = maybe_asteroid else {continue;};
commands.entity(*laser).despawn();
health_manager.damage(*asteroid, 10.);
}
}
Could not despawn entity 94v3#12884901982 because it doesn't exist in this World. See: https://bevyengine.org/learn/errors/b0003
Anyone know of a good workaround ?
I'm guessing this happens because the object might start colliding with more than one entity at the same time, so there's more than one CollisionStarted event with the same laser/asteroid, and you end up queuing duplicate despawning commands. You can use try_despawn to make it not emit warnings at least.
thanks
Hmm, I don't think there's really a good way to do this currently, but a somewhat manual way would be to use the DebugRender component directly. You could even set up some hook or observer to add it automatically when CollisionLayers is added, depending on the layers used.
you can disable the global gizmos with e.g. this, and use DebugRender to override it for the entities you want
app.insert_gizmo_config(PhysicsGizmos::none(), GizmoConfig::default());
I might split the component at some point like ColliderDebug, RigidBodyDebug, JointDebug, etc.
This sounds like #609, which should be fixed on main
I'll probably do a patch release soon
Yay! Thanks I don't mind switching to main
You can add colliders as child entities
Yeah I was planning on trying that just havent yet
https://imgur.com/a/iAkw1iX is super nice being able to have a bevy native psychics engine did this little vr gravity grab
Whoa, cool!
Is anyone using Contacts::total_tangent_impulse or Contacts::total_tangent_force()? I realized they technically might not be accurate since the tangent directions can change between substeps, and the contact data also doesn't store the tangent directions anyway, so it's unclear which way the impulses are pointing, especially in 3D where there's more of a friction cone
so I'm thinking I might remove them
you can still manually compute them by going through the contact data and getting tangent_impulses
PR to make a lot of the contact types more minimal/efficient, clearer, and better documented
https://github.com/Jondolf/avian/pull/616
I can't decide if ContactData should be ContactPoint or ManifoldPoint (like Box2D), or something else
In Rapier it's TrackedContact, I guess because it can store feature IDs? I don't love that name though
I'm also tempted to rename Contacts to ContactPair like in Rapier, since it specifically represents contacts between two entities, not all contacts in the world
I know this is a really "it depends" question, but like.. within an order of magnitude, how many dynamic colliders hitting each other should I be able to sustain before stuff gets choppy? like, are people typically doing single digits, 10s, 100s? 1000s?
Does the crate calculate velocity for objects transforms that are set else where? Like if i were to move a cube with a function does linearvelocity get updated even if i directly set the transforms?
nah I don't think so
I have a problem with fast moving objects (avian3d), where there seams to be a border around the colliders proportional to speed that objects bounce off. Anyone know what's going on with this?
Anyone know why my wall collider extends beyond where it should for fast moving objects?
https://hastebin.skyra.pw/vohiceberu.rust
Never mind - I've found the culprit. It's speculative CCD (enabled by default).
is there a way to have a collider ignore another collider? I'm having a issue similar to https://github.com/Jondolf/avian/issues/615 where im guessing from the profiling is the contact reporting is having a field day reporting all the static entities that are touching. If there is a way I wanted to test first before commenting.
nvm im blind there is a way - https://docs.rs/avian2d/latest/avian2d/collision/struct.CollisionLayers.html
Defines the collision layers of a collider using memberships and filters.
Having trouble finding a clear answer in the docs, but can a parent entity have different CollisionLayers from their child entities?
The benefit of using separate entities for the colliders is that each collider can have its own friction, restitution, collision layers,
so that doesn't exactly answer your question as it follows this paragraph:
Colliders can be arbitrarily nested and transformed relative to the parent. The rigid body that a collider is attached to can be accessed using the ColliderParent component.
which doesn't clarify if collision layers will matter for nested. but id assume the collision layer would apply for that specific collider. and id also assume the parent ignores child colliders?
probably worth just testing clean room
Yeah I'm gonna give it a go haha, this is exactly what tripped me up a bit, Initially I was just going to attach a Sensor to the parent but that turned out to be inherited
in broad_phase this makes me suspect they can have their own collision layers
struct AabbIntervals(
Vec<(
Entity,
ColliderParent,
ColliderAabb,
CollisionLayers,
StoreAabbIntersections,
IsBodyInactive,
)>,
);
Yeah it's looking like that is the case in my testing so far, perfect!
as long as they arent part of the same ColliderParent they will interact fine i think parent1 == parent2
// No collisions between bodies that haven't moved or colliders with incompatible layers or colliders with the same parent
if (*inactive1 && *inactive2) || !layers1.interacts_with(*layers2) || parent1 == parent2
{
continue;
}
so a child collider wont interact with anything in the hierarchy of that specific entity unless you can get them to have different ColliderParents somehow
I can't think of one off the top of my head, but I wonder if there is a usecase where you would want to enable collision between siblings in the same hierarchy
good question. the only thing i could think of would be like a turret bounded dynamically by the mesh it's sticking out of but that would probably just be 2 separate colliders sharing a common non ColliderParent parent.
Yeah, or you could maybe use a joint for something like that
Having a similar issue. It's also the first time I've had it
Seems like it's coming out of the walls, but I'm not entirely sure how
are you using collision layers? that solved it for me
Unless I'm misunderstanding, I've got it set up like this:
Entity => filters: [ Wall, Entity ], memberships: [Entity]
Floor => filters [None], memberships: [Floor],
Wall => filters [Entity], memberships: [Wall]
They shouldn't interact with each other at all, even tiled, right?
It's an inefficient way of setting up a collision system, to be fair, but I'm only doing it unoptimised to play around with avian's backend for bevy picking
you probably want floor filtering Entity too right?
It's a top down RTS set up. The floor's just tiled until I replace it with a big plane lump
gotcha, then wouldnt the floor have 0 colliders?
but yeah your setup would in theory not have walls interacting with each other
The only thing the floor would do would be to interact with the picker in this case
Yeah, which has me concerned
whats your concern or symptom you are trying to diagnose?
Walls are switched back on now. It's the floor
but whats your issue? or did you fix it?
Sorry, the problem was:
- my setup's a really primitive replacement for bevy_ecs_tilemap which just has a bunch of meshes representing floor and wall tiles
- those floor and wall tiles had colliders
- the floor tiles were set to run as Sensors to avoid interactions with anything
- this generated a ton of collisions
It shouldn't have interacted with anything. Turning off the collision stuff for the floor got me my fps mostly back, but I'm still not entirely sure why it slowed everything down so much
its becuase each floor was colliding with its neighbor
The filter should have prevented it though right?
It was set to not interact with itself. unless memberships always interact with each other
in theory, maybe there's a bug with sensors not honoring collision layers (i dont know if it shares the same code paths as normal collisions)? try just using RigidBody::Static ?
I did. Had the same issue
Also, oddly, I had the same issue even if the colliders for the floor were too small to interact with each other
i assume you have the egui world inspector to verify your hierarchy? if not I would double check your entity hierarchy to make sure something unexpected isnt happening
I'd planned on building one myself as a learning experience. But given the circumstances it might be prudent to take a look, yeah. Tomorrow though 🙂
Thanks for the help in either case
ah yeah. first plugin i add to any project is bevy-inspector-egui xD. without an editor its easy to "misplace" entities. for example you could be spawning something to trigger your floors but they are rendering behind it forwhatever reason or dont have a mesh.
Tbh given what I'm doing, having a mesh for every part of the floor is overkill. But I started with a basic tilemap written in a csv because I wanted to do as much of this as I could in rust alone as a learning experience, and stretched to the physics engine to have some fun 🙂
I can probably solve the problem by just planar meshing the floor
im lazy and using a mesh for every part of the floor right now, as long as you are reusing mesh and material handles it can scale a bit before you have issues and need optimizations like ecs_tilemap has
Yeah I've had no problems for now at least. The collider issue's the first one I've come across. Even bevy_picking was razor sharp with a faux tile map

