Skip to content

Commit

Permalink
Use polyline for tiled polygons to account for convex shapes (#66)
Browse files Browse the repository at this point in the history
  • Loading branch information
alanbaumgartner authored Dec 15, 2024
1 parent b2e3b36 commit 415bd6b
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 93 deletions.
87 changes: 47 additions & 40 deletions src/physics/avian.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
use avian2d::{math::Vector, prelude::*};
use bevy::prelude::*;
use tiled::Map;
use tiled::{Map, ObjectShape};

use crate::prelude::*;

/// The actual Avian physics backend to use when instanciating the physics plugin.
/// The actual Avian physics backend to use when instantiating the physics plugin.
///
/// Example:
/// ```rust,no_run
Expand All @@ -21,7 +21,7 @@ use crate::prelude::*;
#[derive(Default)]
pub struct TiledPhysicsAvianBackend;

impl super::TiledPhysicsBackend for TiledPhysicsAvianBackend {
impl TiledPhysicsBackend for TiledPhysicsAvianBackend {
fn spawn_collider(
&self,
commands: &mut Commands,
Expand Down Expand Up @@ -51,44 +51,8 @@ impl super::TiledPhysicsBackend for TiledPhysicsAvianBackend {
} => object.as_deref(),
})?;

let (pos, collider) = match &object_data.shape {
tiled::ObjectShape::Rect { width, height } => {
// The origin is the top-left corner of the rectangle when not rotated.
let shape = Collider::rectangle(*width, *height);
let pos = Vector::new(width / 2., -height / 2.);
(pos, shape)
}
tiled::ObjectShape::Ellipse { width, height } => {
let shape = Collider::ellipse(width / 2., height / 2.);
let pos = Vector::new(width / 2., -height / 2.);
(pos, shape)
}
tiled::ObjectShape::Polyline { points } => {
let shape = Collider::polyline(
points.iter().map(|(x, y)| Vector::new(*x, -*y)).collect(),
None,
);
(Vector::ZERO, shape)
}
tiled::ObjectShape::Polygon { points } => {
let shape = match Collider::convex_hull(
points
.iter()
.map(|(x, y)| Vector::new(*x, -*y))
.collect::<Vec<Vector>>(),
) {
Some(x) => x,
None => {
return None;
}
};
let (pos, collider) = get_position_and_collider(&object_data.shape)?;

(Vector::ZERO, shape)
}
_ => {
return None;
}
};
Some(TiledColliderSpawnInfos {
name: format!("Avian[{}]", object_data.name),
entity: commands.spawn(collider).id(),
Expand All @@ -97,3 +61,46 @@ impl super::TiledPhysicsBackend for TiledPhysicsAvianBackend {
})
}
}

fn get_position_and_collider(shape: &ObjectShape) -> Option<(Vector, Collider)> {
match shape {
ObjectShape::Rect { width, height } => {
// The origin is the top-left corner of the rectangle when not rotated.
let shape = Collider::rectangle(*width, *height);
let pos = Vector::new(width / 2., -height / 2.);
Some((pos, shape))
}
ObjectShape::Ellipse { width, height } => {
let shape = Collider::ellipse(width / 2., height / 2.);
let pos = Vector::new(width / 2., -height / 2.);
Some((pos, shape))
}
ObjectShape::Polyline { points } => {
let shape = Collider::polyline(
points.iter().map(|(x, y)| Vector::new(*x, -*y)).collect(),
None,
);
Some((Vector::ZERO, shape))
}
ObjectShape::Polygon { points } => {
if points.len() < 3 {
return None;
}

let points = points
.iter()
.map(|(x, y)| Vector::new(*x, -*y))
.collect::<Vec<_>>();

let indices = (0..points.len() as u32 - 1)
.map(|i| [i, i + 1])
.chain([[points.len() as u32 - 1, 0]])
.collect();

let shape = Collider::polyline(points, Some(indices));

Some((Vector::ZERO, shape))
}
_ => None,
}
}
113 changes: 60 additions & 53 deletions src/physics/rapier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
use bevy::prelude::*;
use bevy_rapier2d::prelude::*;
use tiled::Map;
use tiled::{Map, ObjectShape};

use crate::prelude::*;

/// The actual Rapier physics backend to use when instanciating the physics plugin.
/// The actual Rapier physics backend to use when instantiating the physics plugin.
///
/// Example:
/// ```rust,no_run
Expand All @@ -21,7 +21,7 @@ use crate::prelude::*;
#[derive(Default)]
pub struct TiledPhysicsRapierBackend;

impl super::TiledPhysicsBackend for TiledPhysicsRapierBackend {
impl TiledPhysicsBackend for TiledPhysicsRapierBackend {
fn spawn_collider(
&self,
commands: &mut Commands,
Expand Down Expand Up @@ -51,57 +51,8 @@ impl super::TiledPhysicsBackend for TiledPhysicsRapierBackend {
} => object.as_deref(),
})?;

let (pos, collider) = match &object_data.shape {
tiled::ObjectShape::Rect { width, height } => {
// The origin is the top-left corner of the rectangle when not rotated.
let shape = Collider::cuboid(width / 2., height / 2.);
let pos = Vect::new(width / 2., -height / 2.);
(pos, shape)
}
tiled::ObjectShape::Ellipse { width, height } => {
let shape = if width > height {
Collider::capsule(
Vec2::new((-width + height) / 2., 0.),
Vec2::new((width - height) / 2., 0.),
height / 2.,
)
} else {
Collider::capsule(
Vec2::new(0., (-height + width) / 2.),
Vec2::new(0., (height - width) / 2.),
width / 2.,
)
};
let (pos, collider) = get_position_and_collider(&object_data.shape)?;

let pos = Vect::new(width / 2., -height / 2.);
(pos, shape)
}
tiled::ObjectShape::Polyline { points } => {
let shape = Collider::polyline(
points.iter().map(|(x, y)| Vect::new(*x, -*y)).collect(),
None,
);
(Vect::ZERO, shape)
}
tiled::ObjectShape::Polygon { points } => {
let shape = match Collider::convex_hull(
&points
.iter()
.map(|(x, y)| Vect::new(*x, -*y))
.collect::<Vec<_>>(),
) {
Some(x) => x,
None => {
return None;
}
};

(Vect::ZERO, shape)
}
_ => {
return None;
}
};
Some(TiledColliderSpawnInfos {
name: format!("Rapier[{}]", object_data.name),
entity: commands.spawn(collider).id(),
Expand All @@ -110,3 +61,59 @@ impl super::TiledPhysicsBackend for TiledPhysicsRapierBackend {
})
}
}

fn get_position_and_collider(shape: &ObjectShape) -> Option<(Vect, Collider)> {
match shape {
ObjectShape::Rect { width, height } => {
// The origin is the top-left corner of the rectangle when not rotated.
let shape = Collider::cuboid(width / 2., height / 2.);
let pos = Vect::new(width / 2., -height / 2.);
Some((pos, shape))
}
ObjectShape::Ellipse { width, height } => {
let shape = if width > height {
Collider::capsule(
Vec2::new((-width + height) / 2., 0.),
Vec2::new((width - height) / 2., 0.),
height / 2.,
)
} else {
Collider::capsule(
Vec2::new(0., (-height + width) / 2.),
Vec2::new(0., (height - width) / 2.),
width / 2.,
)
};

let pos = Vect::new(width / 2., -height / 2.);
Some((pos, shape))
}
ObjectShape::Polyline { points } => {
let shape = Collider::polyline(
points.iter().map(|(x, y)| Vect::new(*x, -*y)).collect(),
None,
);
Some((Vect::ZERO, shape))
}
ObjectShape::Polygon { points } => {
if points.len() < 3 {
return None;
}

let points = points
.iter()
.map(|(x, y)| Vect::new(*x, -*y))
.collect::<Vec<_>>();

let indices = (0..points.len() as u32 - 1)
.map(|i| [i, i + 1])
.chain([[points.len() as u32 - 1, 0]])
.collect();

let shape = Collider::polyline(points, Some(indices));

Some((Vect::ZERO, shape))
}
_ => None,
}
}

0 comments on commit 415bd6b

Please sign in to comment.