I'm very new to game dev and to Rust, so I might be way on the wrong track. I'm using bevy 0.10.1, bevy_ecs_tilemap, bevy_rapier2d, and tiled to make a tilemap with collisions specified in the .tsx file. Some of my tiles use polygons to define the collision objects. I'm able to get the rectangular collisions to work, but the polygon collisions are in the wrong location and sometimes the wrong shape.
if let Some(object_layer) = layer_tile.collision.clone() {
let shapes = object_layer
.object_data()
.iter()
.filter_map(|object_data| {
let pos = Vect::new(object_data.x / 2., -object_data.y / 2.);
let rot = object_data.rotation;
let shape = match &object_data.shape {
tiled::ObjectShape::Rect { width, height } => {
Collider::cuboid(width / 2., height / 2.)
}
tiled::ObjectShape::Ellipse { width, height } => {
Collider::capsule_x(width / 2., height / 2.)
}
tiled::ObjectShape::Polyline { points } => {
Collider::polyline(
points
.iter()
.map(|(x, y)| Vect::new(*x, *y))
.collect(),
None,
)
}
tiled::ObjectShape::Polygon { points } => {
Collider::convex_hull(
&points
.iter()
.map(|(x, y)| Vect::new(*x, *y))
.collect::<Vec<_>>(),
)?
}
_ => {
return None;
}
};
Some((pos, rot, shape))
})
.collect::<Vec<_>>();
let world_pos = tile_pos.center_in_world(&grid_size, &map_type);
let transform =
get_tilemap_center_transform(&map_size, &grid_size, &map_type, 0.)
* Transform::from_xyz(
world_pos.x + offset_x,
world_pos.y - offset_y,
0.,
);
if shapes.len() == 1 {
let (pos, rot, collider) = shapes[0].clone();
let transform = transform
* Transform {
translation: Vec3::new(pos.x, pos.y, 0.),
rotation: Quat::from_rotation_x(rot),
..default()
};
commands
.entity(tile_entity)
.insert((collider, TransformBundle::from_transform(transform)));
} else if shapes.len() > 1 {
commands.entity(tile_entity).insert((
Collider::compound(shapes),
TransformBundle::from_transform(transform),
));
}
}
How do I convert tiled polygon collision data into Rapier colliders? Or am I combining tools that don't belong together?
After much trial and error, I figured out how to do it. The polygons were upside-down and I needed to treat the rectangles different from the polygons. (My tilemap currently doesn't have ellipses or polylines, so I'm assuming they work the same as the other shapes.)
if let Some(object_layer) = layer_tile.collision.clone() {
let shapes = object_layer
.object_data()
.iter()
.filter_map(|object_data| {
let pos = Vect::new(object_data.x, -object_data.y);
let rot = object_data.rotation;
match &object_data.shape {
tiled::ObjectShape::Rect { width, height } => {
let shape = Collider::cuboid(width / 2., height / 2.);
Some((
pos + Vec2::new(
(-grid_size.x + width) / 2.,
(grid_size.y - height) / 2.,
),
rot,
shape,
))
}
tiled::ObjectShape::Ellipse { width, height } => {
let shape =
Collider::capsule_x(width / 2., height / 2.);
Some((
pos + Vec2::new(
(-grid_size.x + width) / 2.,
(grid_size.y - height) / 2.,
),
rot,
shape,
))
}
tiled::ObjectShape::Polyline { points } => {
let shape = Collider::polyline(
points
.iter()
.map(|(x, y)| Vect::new(*x, -*y))
.collect(),
None,
);
Some((
pos + Vec2::new(
-grid_size.x / 2.,
grid_size.y / 2.,
),
rot,
shape,
))
}
tiled::ObjectShape::Polygon { points } => {
let shape = Collider::convex_hull(
&points
.iter()
.map(|(x, y)| Vect::new(*x, -*y))
.collect::<Vec<_>>(),
)?;
Some((
pos + Vec2::new(
-grid_size.x / 2.,
grid_size.y / 2.,
),
rot,
shape,
))
}
_ => {
return None;
}
}
})
.collect::<Vec<_>>();
let world_pos = tile_pos.center_in_world(&grid_size, &map_type);
let transform =
get_tilemap_center_transform(&map_size, &grid_size, &map_type, 0.)
* Transform::from_xyz(
world_pos.x + offset_x,
world_pos.y - offset_y,
0.,
);
if shapes.len() == 1 {
let (pos, rot, collider) = shapes[0].clone();
let transform = transform
* Transform {
translation: Vec3::new(pos.x, pos.y, 0.),
rotation: Quat::from_rotation_x(rot),
..default()
};
commands
.entity(tile_entity)
.insert((collider, TransformBundle::from_transform(transform)));
} else if shapes.len() > 1 {
commands.entity(tile_entity).insert((
Collider::compound(shapes),
TransformBundle::from_transform(transform),
));
}
}