liabru / matter-js

a 2D rigid body physics engine for the web ▲● ■
MIT License
16.72k stars 1.96k forks source link

Per-body events: addedToWorld, removedFromWorld, collision, collisionWith #65

Open Aralun opened 9 years ago

Aralun commented 9 years ago

Plop!

I think it would be cool to be able to listen to events fired by bodies themselves. For example, the following events could be fired:

'addedToWorld' 
  -> { body : someBody, world : /* World it has been added to */ }
'removedFromWorld' 
  -> { body : someBody, world : /* World it has been removed from */ }
'collision' 
  -> { body : someBody, pair : /* Pair of the collision */ }
'collisionWith{{Someone Else}}'
  -> { body : someBody, pair : /* Pair of the collision */ }

The motive behind this is that, when coding a game in which you control a square (a single body), I failed to find an easy way to observe what was happening to the player: Did he collide with something? Did he collide with [this thing]? I was able to do so with the following:

Matter.Events.on(engine, 'collisionActive', function(e) {
  var i, pair,
      length = e.pairs.length;
  for(i = 0; i < length; i++) {
    pair = event.pairs[i];
    if(!(pair.bodyA.label === 'Player' || pair.bodyB.label === 'Player')) {
      continue;
    }
    //Here body with label 'Player' is in the pair, do some stuff with it
  }
});

So to watch a single body I was forced to watch every single collision pair only to find the one I was interested in. Thus, maybe the following would be better:

Matter.Events.on(player.body, 'collision', function(e) {
  //player.body is in e.pair, do stuff with it
}

Or:

Matter.Events.on(player.body, 'collisionWith{{Exit}}', player.win);

The event collisionWith being particular because it would be the first event to my knowledge that can have a parameter.

All of this could be further used with Composites.

liabru commented 9 years ago

What you described is correct, as it stands :) and is somewhat a conscious decision.

There's certainly reason for adding a whole ton of useful events like this. But here's my dilemma and my current stance: I feel like many of these belong in the scope of a game engine rather than a physics engine.

Now I agree that these events are needed in some fashion. The question is should I add more events directly in Matter.js (and risk bloating it) or should they be some sort of plugin / extension? Or should there be a separate Matter.js game engine project?

This is something I'm still thinking about in terms of direction. For me, a plugin architecture seems like a good way to go for this (and in general).

As it stands you may want to create a small abstract layer that hooks the engine collision/tick events etc. and dispatches events on the bodies (use Events.trigger). This is essentially how my implementation would work anyway.

I'll keep this open and update you when I've figured it out!

Aralun commented 9 years ago

I like the "Keep it clean and nice and let people write their own piggy stuff around" way of doing things.

For now, here's where I am:

Matter.Events.on(engine, 'collisionActive', function(event) {
  var i, pair,
      length = event.pairs.length;

  for (i = 0; i < length; i++) {
    pair = event.pairs[i];
    if (!(pair.bodyA.label === 'Player' || pair.bodyB.label === 'Player')) {
      continue;
    }
    Matter.Events.trigger(player.body, 'collision', { pair : pair });
  }
});

With the associated listener of course.

I also tried to make Events fire from a closure-wrapped Matter.Pair, failed miserably because no one cares about Matter.Pair specifically.

corsa1r commented 9 years ago

Hi all,

if i can join to your issue about event triggering, i want to show my implementation of that.

My 'Scene' class, handle all Matter events about collisions (Start, End and Active collision).

1) I create own gameObject class with some abstract methods 1.1) update(delta, scene) - called every single physics update 1.2) draw(context, screen) - called every single render update 1.3) collisionStart(with) - called when Scene detect collisionStart and bodyA is your object, 'with' is the other gameObject 1.4) collisionEnd(with) 1.5) collisionActive(with)

Example:

    Player.prototype.collisionStart = function (with) {
        if (with.label == "ground") {
            this.isGrounded = true;//Now, i can jump again
        }
    };

this method automaticly called when scene handle event from matter engine and route it to Player gameObject.

Sorry about bad english!

On 23 February 2015 at 02:28, Aralun notifications@github.com wrote:

I like the "Keep it clean and nice and let people write their own piggy stuff around" way of doing things.

For now, here's where I am:

Matter.Events.on(engine, 'collisionActive', function(event) { var i, pair, length = event.pairs.length;

for (i = 0; i < length; i++) { pair = event.pairs[i]; if (!(pair.bodyA.label === 'Player' || pair.bodyB.label === 'Player')) { continue; } Matter.Events.trigger(player.body, 'collision', { pair : pair }); } });

I also tried to make Events fire from a closure-wrapped Matter.Pair, failed miserably because no one cares about Matter.Pair specifically.

— Reply to this email directly or view it on GitHub https://github.com/liabru/matter-js/issues/65#issuecomment-75475124.

corsa1r commented 9 years ago

Live example,

inspect all scrips in this simple demo, to see my implementation in Scene.js

http://sourcehint.com/Physics/

On 23 February 2015 at 09:07, vladimir enchev vladimir.corsair@gmail.com wrote:

Hi all,

if i can join to your issue about event triggering, i want to show my implementation of that.

My 'Scene' class, handle all Matter events about collisions (Start, End and Active collision).

1) I create own gameObject class with some abstract methods 1.1) update(delta, scene) - called every single physics update 1.2) draw(context, screen) - called every single render update 1.3) collisionStart(with) - called when Scene detect collisionStart and bodyA is your object, 'with' is the other gameObject 1.4) collisionEnd(with) 1.5) collisionActive(with)

Example:

    Player.prototype.collisionStart = function (with) {
        if (with.label == "ground") {
            this.isGrounded = true;//Now, i can jump again
        }
    };

this method automaticly called when scene handle event from matter engine and route it to Player gameObject.

Sorry about bad english!

On 23 February 2015 at 02:28, Aralun notifications@github.com wrote:

I like the "Keep it clean and nice and let people write their own piggy stuff around" way of doing things.

For now, here's where I am:

Matter.Events.on(engine, 'collisionActive', function(event) { var i, pair, length = event.pairs.length;

for (i = 0; i < length; i++) { pair = event.pairs[i]; if (!(pair.bodyA.label === 'Player' || pair.bodyB.label === 'Player')) { continue; } Matter.Events.trigger(player.body, 'collision', { pair : pair }); } });

I also tried to make Events fire from a closure-wrapped Matter.Pair, failed miserably because no one cares about Matter.Pair specifically.

— Reply to this email directly or view it on GitHub https://github.com/liabru/matter-js/issues/65#issuecomment-75475124.

photonstorm commented 9 years ago

Just to add my 2c - I think any event which is under the control of the game engine (a body added to the world, removed from, etc) should be dispatched by the game engine.

But any event that arises as a direct result of an internal physics function (collision especially) should absolutely be dispatched by matter.js itself. If not events then callbacks should certainly be provided to be hooked in to.

corsa1r commented 9 years ago

I handle all matter.js events and 'translate' them, using my event system to follow single interface of events.

The physics engine must be modular (if you want Tetris, you dont need the matter.js). Physics world/gameLoop must be parallel.

For that reason, i implement my own event system which handled and dispatches all events in the game engine to translate all events as one interface (physics engine is a part/module of the game

examples:

Player.on('jump', callback); Player.on('collide', callback); Ground.on('shaked', callback); Keyboard.on('output', callback); Mouse.on('output', callback); GameLoop.on('update', callback); Touch.on('tap', callback); Network.on('message', callback);

etc..

On 23 February 2015 at 21:11, Richard Davey notifications@github.com wrote:

Just to add my 2c - I think any event which is under the control of the game engine (a body added to the world, removed from, etc) should be dispatched by the game engine, absolutely.

But any event that arises as a direct result of an internal physics function (collision especially) should absolutely be dispatched by matter.js itself. If not events then callbacks should certainly be provided to be hooked in to.

— Reply to this email directly or view it on GitHub https://github.com/liabru/matter-js/issues/65#issuecomment-75610011.

corsa1r commented 9 years ago

https://github.com/corsa1r/TransformiumJS/blob/master/src/engine/events/EventSet.js

On 24 February 2015 at 11:48, vladimir enchev vladimir.corsair@gmail.com wrote:

I handle all matter.js events and 'translate' them, using my event system to follow single interface of events.

The physics engine must be modular (if you want Tetris, you dont need the matter.js). Physics world/gameLoop must be parallel.

For that reason, i implement my own event system which handled and dispatches all events in the game engine to translate all events as one interface (physics engine is a part/module of the game

examples:

Player.on('jump', callback); Player.on('collide', callback); Ground.on('shaked', callback); Keyboard.on('output', callback); Mouse.on('output', callback); GameLoop.on('update', callback); Touch.on('tap', callback); Network.on('message', callback);

etc..

On 23 February 2015 at 21:11, Richard Davey notifications@github.com wrote:

Just to add my 2c - I think any event which is under the control of the game engine (a body added to the world, removed from, etc) should be dispatched by the game engine, absolutely.

But any event that arises as a direct result of an internal physics function (collision especially) should absolutely be dispatched by matter.js itself. If not events then callbacks should certainly be provided to be hooked in to.

— Reply to this email directly or view it on GitHub https://github.com/liabru/matter-js/issues/65#issuecomment-75610011.

liabru commented 8 years ago

Just opened a thread on a possible plugins approach that may be useful here, if anybody is interested.

liabru commented 8 years ago

Progress has been made on the plugins system, which should be merged into master soon. More information in my latest comment on the issue. These events could quite easily be implemented as a plugin without bloating the core, I'll try it out soon!

InitialCrow commented 8 years ago

hi ! thx for your lib is very helpfull but i Have a problem.

I try to catch a collide event per body e.g if i shoot a bullet i want destroy this when bullet is colliding on other body

    initBullet: function(){

          $M.Events.on(App.GameEngine.World.engine, "collisionStart", function(evt){
            //kill bullet when is collide
                for(var i =0 ; i< App.GameEngine.World.engine.world.bodies.length; i++)
                             if(App.GameEngine.World.engine.world.bodies[i].label === 'bullet'){
                               App.GameEngine.World.engine.world.bodies.splice(App.GameEngine.World.engine.world.bodies.indexOf(App.GameEngine.World.engine.world.bodies[i]),1);
                                   break;
                             }

               }

          });
    },

this working but destroy bullet if my player collide other body before the bullet collide body I can fix that if i can detect just the bullet's collide do you have a method for detect collide by body ?

KernelDeimos commented 7 years ago

Is this still a plugin that needs to be developed? I have a small (~60 lines of Coffeescript) plugin I wrote that adds per-body events in a similar way to what Aralun did (but more general).

I also started adding more functionality to it - like detecting when an entity should be able to jump, etc.

I'm considering two ways of proceeding:

liabru commented 7 years ago

@KernelDeimos looks like @dxu has started a collisions events plugin that may cover some of what you need? If not maybe you could give him some pull requests?

If you're looking to create a plugin though, check out matter-plugin-boilerplate.

vkoves commented 7 years ago

@liabru - I'm jumping onto this a bit late (since I am working on something using MatterJS), but from my experience in game development I would think collision events would make perfect sense within the scope of a physics engine. It is a core component of the simulation of the physics simulation, rather than graphics or AI type things that would be more typical of a game engine. For a game engine to be built on top of a physics engine, the physics engine has to communicate all events to the game engine, and that definitely includes collisions.

I'd also cite this wonderful gamedev StackExchange post on this as a reference, specifically:

The game engine may respond to events from the physics engine. The physics engine might raise an event when two objects collide, or when two objects that were touching separate...