Can't get contact events from `CollisionWorld`


Hey folks, sorry for posting so much on my first day. I’m trying to implement collision detection in my Amethyst game and it’s been a bit of a struggle. Turns out my first issue was an outdated example. Now I have something compiling, but not working.

I’m trying to implement a simple collision system for my Asteroids shooter. Everything is modeled as spheres. For the moment, my goal is just to get collision events, then generate game-specific events that I handle in other systems.

I have 3 collision groups. Players/ships are group 0, bullets group 1, asteroids/targets group 2. Players and bullets can only collide with asteroids. Asteroids can collide with everything. For the moment I’m disabling blacklists just to get things working.

Here’s my system with comments throughout. It’s Amethyst-based but hopefully understandable for folks not familiar with Amethyst.

struct CollisionSystem {
    world: CollisionWorld<f32, Entity>,
    ships: CollisionGroups,
    bullets: CollisionGroups,
    asteroids: CollisionGroups,
    // Make it easier to sync `CollisionWorld` with Amethyst
    handles: HashMap<Entity, CollisionObjectHandle>,
    reader_id: Option<ReaderId<ComponentEvent>>,

impl Default for CollisionSystem {
    fn default() -> Self {
        // Not implemented to above spec, I just want to see some collisions
        let mut ships = CollisionGroups::new();
        let mut bullets = CollisionGroups::new();
        let mut asteroids = CollisionGroups::new();
        asteroids.set_whitelist(&[0, 1, 2]); // Should make asteroids at least hit themselves
        // ships.set_blacklist(&[1]);
        ships.set_whitelist(&[2]); // Not sure if needed but asteroids should hit players
        bullets.set_blacklist(&[0, 1]); // Not firing bullets right now so left intact
        let handles = HashMap::new();
        Self {
            world: CollisionWorld::new(0.02),
            reader_id: None,

impl<'s> System<'s> for CollisionSystem {
    type SystemData = (
        WriteStorage<'s, Transform>,
        ReadStorage<'s, BoundingVolume>,
        ReadStorage<'s, Player>,
        ReadStorage<'s, Bullet>,
        ReadStorage<'s, Target>,

    fn run(&mut self, (entities, mut transforms, bounding_volumes, player, bullets, targets): Self::SystemData) {
        let events =;
        // Sync `CollisionWorld` with anything having a `BoundingVolume` component, just `BoundingVolume::Sphere` for now
        for event in events {
            match event {
                ComponentEvent::Inserted(id) => {
                    info!("Inserting {}", id);
                    let entity = entities.entity(*id);
                    if let (Some(bounds), Some(transform)) = (bounding_volumes.get(entity), transforms.get(entity)) {
                        let collision_groups = if let Some(_) = player.get(entity) {
                        } else if let Some(_) = bullets.get(entity) {
                        } else if let Some(_) = targets.get(entity) {
                        } else {
                            info!("Unknown, making target for now");
                        let shape = ShapeHandle::new(match bounds {
                            BoundingVolume::Sphere(radius) => {
                                info!("Making sphere of radius {}", radius);
                        // I just care about proximity for now.
                        let geometric_query_type = GeometricQueryType::Proximity(0.0);
                        let obj =*transform.isometry(), shape, collision_groups, geometric_query_type, entity);
                        self.handles.insert(entity, obj.handle());
                        info!("Inserted {:?}", obj.handle());
                // Other arms not hit, removed for brevity
        // Push entity isometries from Amethyst to `CollisionWorld`
        for (entity, transform, _) in (&entities, &transforms, &bounding_volumes).join() {
            if let Some(handle) = self.handles.get(&entity) {
                // info!("Setting position of {:?}: {:?}", handle, transform.isometry().translation);
      *handle, *transform.isometry());
        // World updated, pull isometries from `CollisionWorld` back to entities
        // Future optimizations might do this only on contact
        for (entity, transform, _) in (&entities, &mut transforms, &bounding_volumes).join() {
            if let Some(handle) = self.handles.get(&entity) {
                let obj =*handle).unwrap();
                let iso = obj.position();
                let mut new_iso = transform.isometry_mut();
                new_iso.translation = iso.translation;
                new_iso.rotation = iso.rotation;
                // info!("Syncing {:?}: {:?}", handle, new_iso.translation);
        for contact in {
            // . . . except, not

    fn setup(&mut self, res: &mut Resources) {
        self.reader_id = Some(WriteStorage::<BoundingVolume>::fetch(&res).register_reader());

If I uncomment the commented-out info! statements, I see transforms pushed/pulled. But I never get any collision events, and I have no clue why. Here’s a sample of my logs:

[INFO][onslaught] Inserting 2
[INFO][onslaught] Got bounds and transform
[INFO][onslaught] Target
[INFO][onslaught] Making sphere of radius 30
[INFO][onslaught] Inserted CollisionObjectHandle(1)
[INFO][onslaught] Inserting 3
[INFO][onslaught] Got bounds and transform
[INFO][onslaught] Target
[INFO][onslaught] Making sphere of radius 30
[INFO][onslaught] Inserted CollisionObjectHandle(2)
[INFO][onslaught] Inserting 4
[INFO][onslaught] Got bounds and transform
[INFO][onslaught] Target
[INFO][onslaught] Making sphere of radius 30
[INFO][onslaught] Inserted CollisionObjectHandle(3)

You’re seeing it insert 3 asteroids. But even letting the thing run for 10 minutes, and even with asteroids moving, nothing ever collides. I’m wondering if I’m setting up my CollisionGroups incorrectly? I actually tried passing CollisionGroups::new() instead of my collision_groups variable, but that doesn’t seem to work either, so collision groups don’t appear to be the problem.

The arena is 300X300. Objects are definitely constrained, and wrap when they move outside the bounds. I also just made asteroids have a radius of 90 rather than 30, and they’re still not colliding despite moving. Or, at least, the event handler is never triggered.

Thanks for any help.



Don’t worry about posting lots of question! They may be useful to others too :wink:

It seems you initialized your collision objects with GeometricQueryType::Proximity(0.0). This means you will get only proximity events instead of contact events. Thus you have to iterate through world.proximity_events() instead of world.contact_events() (see


Yup, that did it. Thanks! I wonder if there’s some way to warn if
someone tries querying an iterator without any objects of the correct
GeometricQueryType attached? I wouldn’t want to negatively impact
performance, but having some sort of debug/training wheels mode that
incurs a performance hit but warns about things like this might be useful.


Thanks for the suggestion. Perhaps some kind of debug print based on the log crate could prove useful, yes.