dimforge / dimforge/bevy_rapier

Make it easier to get static snapshots of collider data

Open
#85 1 comment 0 reactions 0 assignees View on GitHub
A-Integration C-Enhancement D-Medium P-Medium S-not-started
Dominant language
Rust
Stars
1.6k
Forks
282
PR merge metrics
No merged PRs in 30d

Description

I've been having a very hard time with this and wonder if there's any way bevy_rapier might make this easier. I'm trying to integrate physics into my pathfinding, which can take more than a single frame. Previously, my AStar calls generated paths that routed entities through areas in which they couldn't physically fit. I'm trying to add shapes to this algorithm such that valid successors to a given tile are only those in which the shape of the source entity can fit. Here's an example of what I have right now:

```
fn find_path_for_shape(
pool: &AsyncComputeTaskPool,
initiator: Entity,
start: &dyn PointLike,
destination: &dyn PointLike,
map: &Map,
query_pipeline: &QueryPipeline,
collider_query: &QueryPipelineColliderComponentsQuery,
shape: &dyn Shape,
channel: &Sender>,
) {
let collider_set = QueryPipelineColliderComponentsSet(&collider_query);
let shapes = Shapes(
collider_query
.iter()
.map(|(a, b, c, d)| (a, b.clone(), c.clone(), d.clone()))
.collect::>(),
);
let start = start.i32();
let destination = destination.i32();
let map_clone = map.clone();
let shape_clone = shape.clone();
let channel_clone = channel.clone();
pool.spawn(async {
let path = astar(
&start,
|p| {
let mut successors: Vec<((i32, i32), u32)> = vec![];
for tile in map_clone.get_available_exits(p.0 as usize, p.1 as usize) {
let mut should_push = true;
let shape_pos = Isometry::new(vector![tile.0 as f32, tile.1 as f32], 0.);
query_pipeline.intersections_with_shape(
&shapes,
&shape_pos,
&*shape_clone,
InteractionGroups::all(),
Some(&|v| v.entity() != initiator),
|handle| {
should_push = false;
false
},
);
if should_push {
successors.push(((tile.0 as i32, tile.1 as i32), (tile.2 * 100.) as u32));
}
}
successors
},
|p| (p.distance_squared(&destination) * 100.) as u32,
|p| *p == destination.into(),
);
channel_clone
.send(if let Some(path) = path {
Some(Path(path.0))
} else {
None
})
.expect("Channel should exist");
})
.detach();
}
```

It was suggested that I make a wrapper type that creates a static snapshots of colliders to pass into the pool. I'm fine with this, as I can probably correct for changes in the system that actually negotiates calculated paths for cases where a path gets blocked. Here is a partial attempt at that:

```
struct Shapes(Vec<(Entity, ColliderPosition, ColliderShape, ColliderFlags)>);

impl ComponentSet for Shapes {
fn size_hint(&self) -> usize {
self.0.len()
}

fn for_each(&self, f: impl FnMut(bevy_rapier2d::rapier::data::Index, &SharedShape)) {
unimplemented!()
}
}

impl ComponentSetOption for Shapes {
fn get(&self, index: Index) -> std::option::Option<&SharedShape> {
self.0
.get(index.into())
.map(|v: &(Entity, ColliderPosition, ColliderShape, ColliderFlags)| &v.2)
}
}

impl ComponentSet for Shapes {
fn size_hint(&self) -> usize {
self.0.len()
}

fn for_each(&self, f: impl FnMut(bevy_rapier2d::rapier::data::Index, &ColliderFlags)) {
unimplemented!()
}
}

impl ComponentSetOption for Shapes {
fn get(
&self,
index: bevy_rapier2d::rapier2d::data::Index,
) -> std::option::Option<&ColliderFlags> {
self.0.get(index)
}
}

impl ComponentSet for Shapes {
fn size_hint(&self) -> usize {
self.0.len()
}

fn for_each(&self, f: impl FnMut(bevy_rapier2d::rapier::data::Index, &ColliderPosition)) {
unimplemented!()
}
}

impl ComponentSetOption for Shapes {
fn get(
&self,
index: bevy_rapier2d::rapier2d::data::Index,
) -> std::option::Option<&ColliderPosition> {
self.0.get(index)
}
}
```

Obviously that's incomplete, but here are some of the errors I'm getting:

error[E0284]: type annotations needed: cannot satisfy `<_ as SliceIndex<[(bevy::prelude::Entity, bevy_rapier2d::prelude::ColliderPosition, bevy_rapier2d::prelude::SharedShape, bevy_rapier2d::prelude::ColliderFlags)]>>::Output == (bevy::prelude::Entity, bevy_rapier2d::prelude::ColliderPosition, bevy_rapier2d::prelude::SharedShape, bevy_rapier2d::prelude::ColliderFlags)`
--> src\pathfinding.rs:63:14
|
63 | .get(index.into())
| ^^^ cannot satisfy `<_ as SliceIndex<[(bevy::prelude::Entity, bevy_rapier2d::prelude::ColliderPosition, bevy_rapier2d::prelude::SharedShape, bevy_rapier2d::prelude::ColliderFlags)]>>::Output == (bevy::prelude::Entity, bevy_rapier2d::prelude::ColliderPosition, bevy_rapier2d::prelude::SharedShape, bevy_rapier2d::prelude::ColliderFlags)`

error[E0621]: explicit lifetime required in the type of `shape`
--> src\pathfinding.rs:129:10
|
114 | shape: &dyn Shape,
| ---------- help: add explicit lifetime `'static` to the type of `shape`: `&'static (dyn bevy_rapier2d::prelude::Shape + 'static)`
...
129 | pool.spawn(async {
| ^^^^^ lifetime `'static` required

error[E0621]: explicit lifetime required in the type of `query_pipeline`
--> src\pathfinding.rs:129:10
|
112 | query_pipeline: &QueryPipeline,
| -------------- help: add explicit lifetime `'static` to the type of `query_pipeline`: `&'static bevy_rapier2d::prelude::QueryPipeline`
...
129 | pool.spawn(async {
| ^^^^^ lifetime `'static` required

This is probably a more advanced use case, and I'm not sure what the plugin might do to make this easier. Maybe even an example of snapshotting collision data and accessing it in a pool would be useful? There are some obvious mistakes in my `Shapes` code--Rust Analyzer's automatic import added a few broken paths, for instance--but I feel like I'm adding mud to an existing mess rather than working toward a solution. Alternately, I'm not sure if there's a better solution to my pathfinding issue than taking physical shapes into account, but I had enough special cases for handling bad data that I thought it might help to prevent that bad data from being generated in the first place.

Thanks.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.