schteppe / p2.js

JavaScript 2D physics library
Other
2.64k stars 330 forks source link

[QUESTION] Mouse-dragging bodies #309

Open dtognazzini opened 7 years ago

dtognazzini commented 7 years ago

I'm using p2 physics to implement a game in Phaser whereby the user stacks objects by dragging and dropping them with the mouse.

All draggable bodies are dynamic. There is also a static, "ground" body. All the bodies use polygon shape data.

My initial implementation:

  1. onMouseDown on a subject object/body, create a new body to track the mouse, the mouse body.
  2. create a lock constraint to keep the subject body and mouse body in-sync.
  3. onMouseMove update the mouse body position from the mouse position.

This keeps the subject body tracking the mouse and colliding with other bodies in the world.

As noted in #268, there is some lag between the mouse position and the subject body. The lag is understandable since the mouse body is moved instantaneously and the subject body moves via the constraint, catching up to the mouse body. Moving the mouse really quickly back and forth causes the subject body to really lag and it looks silly - it's like flinging around an object on a rope. πŸ˜„

The suggestion in this comment is to "implement your own "constraint", which handles all movement and collisions when dragging the mouse".

My current attempt at not using a constraint is to instead of creating a new body to track the mouse, I move the original body to track the mouse. Of course, when doing this, I can move the mouse faster than the frame rate and forego collision detection thereby moving the subject body through other bodies. With the constraint, this wasn't a problem, since the subject body would react to other bodies in the world as it was being forced to the mouse body position by the constraint.

Ideally, when the mouse moves really quick, the subject body collides with objects on the way to traveling to the mouse position.

I'm curious how I could implement that. Is creating a custom p2 constraint the way to go here or would it be better to implement collision detection in a mouse-down event before moving the subject body? It seems like any implementation dependent on a constraint is necessarily constrained to the frame rate and would result in a "flinging around on a rope" effect.

Assuming the non-constraint strategy of handling all movement and collisions when dragging via the mouse, it seems that the next piece of my solution would look something like this:

  1. onMouseMove - assume the subject body moves to the mouse position.
  2. calculate all overlaps in this position.
  3. If there are no overlaps, move the subject body to this position.
  4. If there are overlaps, move the subject body on the vector back to the previous position.
  5. Loop through steps 2 & 3 until there are no more overlaps.

At this point, I think I have the following options:

  1. Release the subject body from dragging and have it drop wherever it is.
  2. Release the subject body from dragging, add velocities to the body in the direction of the mouse position.
  3. Add some "infinite" velocities to the body in the direction of the mouse position thereby moving any dynamic objects along the way to the mouse position. If/when a non-dynamic body is detected, release the subject body from dragging.

The last option would be equivalent to my initial constraint-based implementation.

That was long. If you read through it all, thank you. I'd appreciate any thoughts on these ideas. Mouse-dragging bodies seems like a pretty common interaction and I'm sure somebody has solved this before - I'd prefer to avoid reinventing the wheel.

Thanks!

schteppe commented 7 years ago

This was a long read, lol. I'll just throw out some ideas and maybe you could use something from them.

Maybe you want something like this demo? It uses SAT.js to resolve collisions instantly. You'll still need to toggle physics on the scene somehow.

Your solution sounds like the Continuous Collision Detection (CCD), available in the master branch... Maybe you could reuse parts of this code?

Another maybe simpler solution would be to use a smaller timestep while the user is dragging the mouse. Open any demo (for example the Ragdoll demo) and increase the FPS parameter to see how the constraints will resolve faster. Other parameters, such as solver tolerance, relaxation etc., can and will change the behavior too.

dtognazzini commented 6 years ago

@schteppe Thanks for your thoughts! I'll check out some of these options.

One question on using SAT.js: wouldn't I have to replicate the entire p2 world in SAT.js objects to make this work?

schteppe commented 6 years ago

No problem. I hope you can get the result you want. For SAT.js you would need to add all the objects you want to collide with, yes.