liabru / matter-js

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

Velocities do not get updated with manual initialization? #258

Closed mbchang closed 8 years ago

mbchang commented 8 years ago

I am trying to generate some object trajectories given nonzero initialized velocities. In the first time step, the objects move fine according to these velocities, but immediately in the next timestep the velocities are reset to 0. I had expected those objects to follow their inertial path and continue moving at their original velocities until a collision. Is there something I need to do to have the objects maintain inertial movement?

Below is a minimal example based on the Demo:

var Matter = require('matter-js')

// module aliases
var Engine = Matter.Engine,
    World = Matter.World,
    Bodies = Matter.Bodies,
    Composites = Matter.Composites;

// create an engine
var engine = Engine.create();
engine.world.gravity.y = 0;
engine.world.gravity.x = 0;

var offset = 5;
World.add(engine.world, [
    Bodies.rectangle(400, -offset, 800.5 + 2 * offset, 50.5, { isStatic: true, restitution: 1 }),
    Bodies.rectangle(400, 600 + offset, 800.5 + 2 * offset, 50.5, { isStatic: true, restitution: 1 }),
    Bodies.rectangle(800 + offset, 300, 50.5, 600.5 + 2 * offset, { isStatic: true, restitution: 1 }),
    Bodies.rectangle(-offset, 300, 50.5, 600.5 + 2 * offset, { isStatic: true, restitution: 1 })
]);

var stack = Composites.stack(0, 100, 3, 1, 20, 0, function(x, y) {
    return Bodies.circle(x, y, 50, { restitution: 1,
                                     friction: 0,
                                     frictionAir: 0,
                                     frictionStatic: 0,
                                     inertia: Infinity,
                                     inverseInertia: 0,
                                     velocity: {x: 5, y: 5}});
});
World.add(engine.world, stack);

// initial velocity
console.log('Initial velocities')
console.log(engine.world.composites[0].bodies.map(function(elem) {
    return {position: elem.position, velocity: elem.velocity}
}))

// run the engine
console.log('After updating')
for (i = 0; i < 5; i++) {
    Engine.update(engine)
    console.log('Step',i)
    console.log(engine.world.composites[0].bodies.map(function(elem) {
        return {position: elem.position, velocity: elem.velocity}
    }))
}

The output of which is:

Initial velocities
[ { position: { x: 52.135, y: 152.5 }, velocity: { x: 5, y: 5 } },
  { position: { x: 178.905, y: 152.5 }, velocity: { x: 5, y: 5 } },
  { position: { x: 305.675, y: 152.5 }, velocity: { x: 5, y: 5 } } ]
After updating
Step 0
[ { position: { x: 69.8349823, y: 152.5 },
    velocity: { x: 0, y: 0 } },
  { position: { x: 178.905, y: 152.5 }, velocity: { x: 0, y: 0 } },
  { position: { x: 305.675, y: 152.5 }, velocity: { x: 0, y: 0 } } ]
Step 1
[ { position: { x: 83.99496814, y: 152.5 },
    velocity: { x: 0, y: 0 } },
  { position: { x: 178.905, y: 152.5 }, velocity: { x: 0, y: 0 } },
  { position: { x: 305.675, y: 152.5 }, velocity: { x: 0, y: 0 } } ]
Step 2
[ { position: { x: 87.5039862249784, y: 152.5 },
    velocity: { x: 0, y: 0 } },
  { position: { x: 186.7239705870216, y: 152.5 },
    velocity: { x: 0, y: 0 } },
  { position: { x: 305.675, y: 152.5 }, velocity: { x: 0, y: 0 } } ]
Step 3
[ { position: { x: 90.31120069296114, y: 152.5 },
    velocity: { x: 0, y: 0 } },
  { position: { x: 192.97914705663888, y: 152.5 },
    velocity: { x: 0, y: 0 } },
  { position: { x: 305.675, y: 152.5 }, velocity: { x: 0, y: 0 } } ]
Step 4
[ { position: { x: 92.55697226734732, y: 152.5 },
    velocity: { x: 0, y: 0 } },
  { position: { x: 197.9832882323327, y: 152.5 },
    velocity: { x: 0, y: 0 } },
  { position: { x: 305.675, y: 152.5 }, velocity: { x: 0, y: 0 } } ]
liabru commented 8 years ago

This is because body.velocity is read-only. Instead if you want to control velocity you should specify a body.positionPrev since velocity is derived using body.position - body.positionPrev during Verlet integration. So an example to give a body a velocity of (5, 5):

 Bodies.rectangle(400, 90, 10, 10, { 
     positionPrev: { x: 400 + 5, y: 90 + 5 } 
 });

You could also use Body.setVelocity after creation. I realise this isn't obvious or convenient. It would be cool to make body.velocity set-able but by nature of the integration method it would be tricky.

mbchang commented 8 years ago

I see, it works now. Thank you!