I want to write a simple JavaScript animation that makes some balls move inside a canvas. I want to detect collisions with custom events handled with Backbone.js instead of having a nested for loop that checks for collisions between each pair of balls.
var j;
for (j = 0; j < N_BALLS; j++) {
ball_center = new Point(..., ...);
ball_shape = new Circle(ball_center, ball_radius);
ball_velocity = ...;
ball_ID = j;
balls[j] = new Ball(ball_shape, ball_velocity, ball_ID);
_.extend(balls[j], Backbone.Events);
balls[j].on("imhere", balls[j].event_handler);
}
function animate() {
if (!paused) {
context.clearRect(0, 0, canvas.width, canvas.height);
var j;
for (j = 0; j < N_BALLS; j++){
balls[j].updatePosition();
balls[j].trigger("imhere", balls[j].shape, balls[j].ID);
}
for (j = 0; j < N_BALLS; j++)
balls[j].draw(context, '#0000ff');
window.requestNextAnimationFrame(animate);
}
}
The event_handler is a member method of each Ball object
Ball.prototype.event_handler = function(shape, ID) {
console.log("ball " + this.ID + " caught message from ball " + ID);
};
I would expect that sometimes a ball catches one message from another one, but this is NEVER the case.
I would like to arrange things in such a way where the event handler can:
- forward the event when
this.ID == ID - stop event propagation if
this.ID != ID
Backbone events
this.IDis probably undefined because Backbone.on()needs the context as the last param unless you're already binding the context to the function elsewhere manually.You're also registering the event on the Ball itself, and triggering on the ball as well.
Events aren't global within Backbone, you need to create a global event channel yourself or use a plugin. Look for event bus, or event aggregator, etc.
A quick example could be as simple as:
Backbone itself is a an object that extends the
Backbone.Eventsfunctionality, but it's best to create a local event channel for your use-case.Speaking of using the
Backbone.Eventsobject, you're extending each instances of aBallwhen you really should be extending theBallprototype once.You should favor
listenTooveron.When destroying a ball, you could call
ball.stopListening()and you would avoid memory leaks. It would also solve the context problem within theevent_handlercallback.So, with a global event aggregator:
Backbone events are not DOM events and they're not bubbling up and down, so there's no
stopPropagationorpreventDefault. In the background, it's just vanilla JS for Backbone.Collision detection
Now that we've covered the events part, let me just say that it's not the best solution for collision detection.
It's exactly the same as if you would call a
isCollindingWith(ball)function on every other ball when updating the position. That's what's going on with the events, but with the loops hidden in the events implementation.Tackling an optimized collision detection algorithm in this answer would be too broad, so I'll just link to this great article on 2D collision detection.
The interesting part is at the end, where they talk about spacial data structures like Quad Trees, R-Trees or Spacial Hashmaps.