impacted
impacted copied to clipboard
Support for ray casting?
Is there any support planned for ray casting in this crate?
Yes, definitely.
It is not yet exactly clear how it will look like though. Right now, I would lean toward simply adding a "ray" shape that could be created like this: CollisionShape::new_ray(Vec2::X)
.
I wonder if it should be on CollisionShape
, or a separate component entirelly 🤔
I wonder if it should be on CollisionShape, or a separate component entirely
I wonder too. Though, being a collision shape makes it very simple. I am not really sure what would be the benefits of having a dedicated abstraction. And how it would look like.
If you have an idea of a nice possible usage, feel free to post it here.
Maybe something like:
let shape = CollisionShape::new_circle(1.0);
let ray_origin = Vec2::new(-2.0, 0.0);
let ray = Vec2::X * 2.0;
assert!(shape.collides_with_ray(Ray::new(ray_origin, ray));
Or using a ray shape:
let shape = CollisionShape::new_circle(1.0);
let ray_origin = Vec2::new(-2.0, 0.0);
let ray = CollisionShape::new_ray(Vec2::new(-2.0, 0.0))
.with_transform(Transfrom::from_translation(ray_origin));
assert!(shape.collides_with(ray);
I don't know. I kind of like avoiding the proliferation of abstractions if we can reuse the existing ones. But feel free to share your opinions and suggestions.
Hm this is interesting. In my case I don't really benefit by having the ray actually be a component... I just need to check if I cast a ray, does it collide with any collision shapes. But usually you would want to check with all collision shape entities available, so maybe it could be a trait?
struct Ray {
origin: Vec2,
direction: Vec2,
distance: Option<Vec2>, // None means infinity
// maybe layer filters in the future
}
trait RayCast {
fn ray_cast(&self, ray: Ray) -> Option<Vec2>;
}
impl RayCast for CollisionShape { ... }
impl<F> RayCast for Query<&CollisionShape, F> { ... }
impl RayCast for Vec<&CollisionShape> { ... }
That would make the api quite simple for bevy systems:
fn ray_casting(colliders: Query<&CollisionShape>) {
if let Some(point) = colliders.ray_cast(Ray::new(origin, direction).with_distance(10.0)) {
println!("hit point {:?}", point);
}
}
The alternative would be to have Ray as a component (or a shape on the CollisionShape component) and have ray cast bevy events, but this makes it more specific to bevy than a general library.
Well, because it is a CollisionShape
doesn't mean it needs to be used as a component when using bevy. It can still be created on the fly the same way.
But I agree, it may be more ergonomic to have a Ray::new(origin, direction)
than having to create and transform a collision shape.
As of using a trait so that it might be implemented by collections (like vec or query), that's interesting. But still I feel like the other shapes would deserve the same treatment. Like:
fn collision(colliders: Query<&CollisionShape>) {
let shape_cast = CollisioinShape::new_circle(1.0);
if let Some(contact) = colliders.contact_with(shape_cast) {
println!("contact: {:?}", contact);
}
}
I made a simple ray casting implementation in my repo https://github.com/tqwewe/bevy_controller_2d/blob/main/src/ray_cast.rs#L42.
It's not the most generic approach with the API, but the logic is there for simple ray casting (thanks to the coding train's tutorial on it).
Thanks for sharing :-)
I'll probably go a different route though, I'll try to follow up on the GJK/EPA algorithms already in place and using properties of the Minkowski difference. That would notably make it work with shapes that contains an infinite number of edge (like circle or capsule)
By the way, can you tell me more about your use case? What is your use of ray cast currently?
I'm not very experienced with physics engines and collision detection math, so I'm sure your method would be much better.
My use for ray casting is to create a 2D player controller (without a physics engine) by casting rays from the player on all sides and checking it with all CollisionShape
's top, right, bottom & left edges.
It would've been much simpler if I could have just done a ray cast without querying all CollisionShapes
and calculating their edge points... I'm not sure on the best API for this though.
For example, Unity's 2D ray cast function https://docs.unity3d.com/ScriptReference/Physics2D.Raycast.html, it doesn't need any references to colliders... but I think with Bevy you would need to query them all.. unless impacted kept an internal reference to all the components created and provided a Res<Raycast>
resource that lets you just do raycast.ray_cast(Ray::new(...));
My use for ray casting is to create a 2D player controller (without a physics engine) by casting rays from the player on all sides and checking it with all CollisionShape's top, right, bottom & left edges.
Okay, then I guess you don't need to have the hit point, do you? Only need to know if each ray hits?
unless impacted kept an internal reference to all the components created and provided a Res<Raycast> resource that lets you just do raycast.ray_cast(Ray::new(...));
I would consider that out of scope for impacted. Which is not a bevy plugin, but rather the low-level collision logic.
In practice, a higher lever "engine" would combine impacted
with a broad-phase that would keep track of the bounds of each objects. When ray-casting it'd ask the broad-phase the list of entities that may collide with the ray, then for each entities returned by the broad-phase it would ask impacted
if the ray actually hits the collision shape.
I may (or may not) write such higher-level collision detection plugin for bevy one day, using bhv-arena
for the broad-phase and impacted
for the narrow-phase. But no promise ;-)