piqnt / planck.js

2D JavaScript Physics Engine
http://piqnt.com/planck.js/
MIT License
4.9k stars 236 forks source link

Collision events and ECS-styled games #4

Closed pkieltyka closed 7 years ago

pkieltyka commented 7 years ago

@shakiba congrats on this amazing work and project. I believe this is very needed in the community and it's exactly what I was looking for months ago. I ended up settling on p2.js, which also looks inspired by box2d, so it'll be easy for me to port my game to planck.js

p2.js was very good, but I'm glad to see someone crafting a clean and even refactored box2d port to JS - so thank you very much.

Of course there are many architectures when writing a game - in my case I used an ECS design to break everything into synchronous and discrete set of steps in time. However with listeners, it opens up to an asynchronous state which maybe or maybe not is compatible with ECS - what do you think?

also, what is the point of the events versus other state testing functions in a physics engine, and having an API that is friendly to an ECS or other game architectures?

shakiba commented 7 years ago

Thank you for your kind words, you are welcome! :)

Can you please elaborate on how you expect an ECS-friendly API work?

magwo commented 7 years ago

I think what may be wanted is to have a way to bunch all events of a certain type into a single "publish". So that, for example, all objects are accelerated and moved, collisions are resolved etc. And THEN you ask for the collision events or such. Instead of having events being fired inside loops directly when they "happen". I'm not sure how planck currently behaves, but in general having game code reacting to physics events while the simulation is being updated can result in buggy behaviour. My experience is that it's generally more stable and "ECS friendly" to do a full update before emitting events about the simulation.

pkieltyka commented 7 years ago

I guess im thinking about concurrency in the game, and ECS is synchronous and events are asynchronous - which mixes the two paradigms. In my limited experiencing in writing a game I came up against this with my ECS system (https://github.com/noffle/nano-ecs) and with p2.js which offers async events for whenever a collision contact exists between two shapes.

In a frame (1/60 time step) of a game loop, everything happens in discrete time - to maintain a single mode of concurrency (ie. synchronous / discrete), the physics world should set state for the various events and set them on the world object for the game to test their state, instead of sending them as an async event.

does that make sense? if not, I will follow up with psuedo code to show examples.

Point is, aside from async events which could stay in the physics engine, I think there should be effort to fully support a synchronous concurrency model if intended to be used to write games, and in this case a time-step in the world would have a state object and allow a game loop to peak into that to determine game-logic from occurences in the world.

magwo commented 7 years ago

There already is m_contactList in the world object.. Would that be enough to complement contact events?

Also, are we talking event-loop (JS engine) asynchronous or not? As far as I can see, the events emitted by the planck code are synchronous - the physics update, including events, is all done within one JS tick.

pkieltyka commented 7 years ago

@magwo cool, does m_contactList include every begin contact and end contact event? as well, is there something like m_contactList for every other kind of event type?

perhaps better is m_events list in a discrete form, but, the main consideration is how to retrieve a subset of that list based on some criteria, in a very efficient manner - ie. how to fast & efficiently find any objects in m_events (or m_contactList) that include bodies 1 or 2. The data organization and structure will be key for fast retrieval.

let me know if you guys have better ideas - do you know what is typically done in the best-of-breed games?

shakiba commented 7 years ago

Events are synchronously called when the world is being solved, however if you want to add/remove something in world you need to do it when world is not being solved (it can be asynchronously or synchronously after/before it). See post-solve and pre-solve event listeners in examples.

They are actually just synchronous callbacks, not asynchronous events.

magwo commented 7 years ago

A side note.. I noticed that the m_contactList appears to be a linked list. Is this a conscious choice or just a carry-over from Box2D? I believe the performance characteristics of linked list are really poor compared to arrays until you have huge lists.

shakiba commented 7 years ago

Is this a conscious choice or just a carry-over from Box2D?

Both, because adding/removing items can easily break array iteration, etc.

I believe the performance characteristics of linked list are really poor compared to arrays until you have huge lists.

Why? I'm not sure if it has an impact on overall performance either way?

magwo commented 7 years ago

Ok. Never mind, it's probably fine. :) Just general "modern programming" wisdom - linked list looks performant on paper but is terrible in practice - unless huge lists - due to causing cache miss on every element read.

pkieltyka commented 7 years ago

Performance and efficiency is critical for browser based games and GC pauses will cripple a game, so you have to use object pools everywhere to have no GC pressure anywhere

shakiba commented 7 years ago

@magwo I wasn't able to mention you in another thread I made. Few days ago I recalled that MouseJoint has a setTarget method which is similar to what you asked about.

shakiba commented 7 years ago

I'm going to close this, please feel free to open it again if required.