#gamedev #2d #collision #bevy


Plugins and helpful methods for using sepax2d with Bevy for 2d overlap detection and collision resolution

2 releases

Uses new Rust 2021

new 0.1.1 Jun 22, 2022
0.1.0 Jun 13, 2022

#154 in Game dev

49 downloads per month


190 lines

Bevy tracking Crates.io MIT/Apache 2.0


Plugins and helpful methods for using sepax2d with Bevy for 2d overlap detection and collision resolution.

Compatible Versions

bevy bevy_sepax2d
0.7 0.1


Add the following to the [dependencies] section of your Cargo.toml:

sepax2d = "0.3"
bevy_sepax2d = "0.1"

There is an additional debug feature which can be used to render collision shapes to the screen. This relies on Bevy's default features as well as bevy_prototype_lyon for rendering. This can be enabled in your Cargo.toml:

bevy_sepax2d = { version = "0.1", features = ["debug"] }

To add a shape to your world, simply insert a Sepax struct into any entity.

let polygon = Polygon::from_vertices((0.0, 0.0), vec![(0.0, -25.0), (15.0, 15.0), (-15.0, 15.0)]);
let convex = Convex::Polygon(polygon);

.insert(Sepax { convex });

Sepax has one field, convex: This is an instance of the Convex enum, which has possible values for each shape supported by sepax2d: Polygon, Circle, AABB, and Capsule. Each variant contains an instance of the corresponding shape. Check the sepax2d documentation for information about each one.

The underlying shape can be conveniently accessed through the shape and shape_mut methods, which provide easy access to references to the underlying shapes without need to match the enum.

fn bullet_system
    mut bullets: Query<(&Bullet, &Sepax)>,
    targets: Query<&Sepax, Without<Bullet>>
    for (_b, bullet) in bullets.iter()
        for target in targets.iter()
            if sat_overlap(wall.shape(), bullet.shape())
                //Bullet hit target, now react appropriately

If included, the SepaxPlugin provides the following basic features, which occur during Bevy's PostUpdate stage:

  • Resets the collision information from the previous frame
  • Updates the location of any Sepax component attached to a Transform
  • Provides inelastic collision between entities with a Sepax shape which are tagged Movable and those that are not movable.

These systems are public, so you may include them manually if you do not want all of them. This is likely to happen when you want to introduce finer control over which objects collide with which, but still want to reset collision data and update locations. Or, you may want to collide and update locations, but want to maintain old collision data until it has been processed.

The Movable component signifies that an entity is dynamic. By contrast, the absence of a Movable component denotes a static object which is treated like a "wall" that movable entities should collide with.

The Movable struct contains a list of normalized collision resolution vectors from the previous frame during the Update stage for you to react to in your code. These vectors represent the direction AWAY from the object that was collided with. For example, the following code zeroes out the y-component of an entity's velocity when it lands on or hits the bottom of a platform:

fn velocity_correction_system(mut query: Query<(&mut Velocity, &Movable)>)
    for (mut velocity, correction) in query.iter_mut()
        for (_x, y) in correction.axes.iter()
            if y.abs() > f32::EPSILON && velocity.y.is_sign_positive() != y.is_sign_positive()
                velocity.y = 0.0;

Debug Rendering

If you enable the debug feature, then you can render your shapes with the help of bevy_prototype_lyon. The following convenience methods help with rendering:

  • Sepax::as_shape_bundle will take in a reference to a Sepax struct and a DrawMode and return a ShapeBundle to be added to an entity.
  • Sepax::shape_geometry will take in a reference to a Sepax struct and return a Path representing the given shape. Use this in conjunction with ShapePath to change the rendered shape at runtime. Check out the platformer example to see this in action!
let circle = Circle::new((0.0, 0.0), 15.0);
let convex = Convex::Circle(circle);

let player = DrawMode::Fill(FillMode::color(Color::WHITE));

.insert_bundle(Sepax::as_shape_bundle(&convex, player))
.insert(Sepax { convex })
.insert(Movable { axes: Vec::new() });


The repository includes two example applications showcasing a basic platformer (which only uses the basic plugin), and a shmup which demonstrates some custom systems.

cargo run --features="debug" --example platformer

cargo run --features="debug" --example shmup


Please feel free to suggest additional features, bug fixes, or optimizations. Thanks!


~342K SLoC