0

I am using PhysicsJS to create a juggling game. All I have is a ball (circle shape) and a shoe (square using convex-polygon shape). The shoe is set to fixed and moves along the x-axis on mousemove.

Square:

square = Physics.body('convex-polygon', {
    x: 250,
    y: 400,
    vertices: [
        { x: -68, y: 29 },
        { x: 68, y: 29 },
        { x: 69, y: -29 },
        { x: -68, y: -29 }
    ],
    angle: -0.2,
    view: shoeImage,
    fixed: true,
    restitution: 1
});

world.add(square);

Mousemove event:

jQuery('canvas').on('mousemove', function (e) {
    var offset = $(this).offset();
    var relativeX = (e.pageX - offset.left);
    square.state.pos.set(relativeX, 400);
});

I also added a click event on the shoe to have a 'kick effect'. So far I've done this by changing the square.state.angular.pos and set it back to the previous angular position with a setTimeout function.

jQuery('canvas').on('click', function (e) {
    if (square.state.angular.pos == -0.2) {
        square.state.angular.pos = 0.3;
    }
    else {
        square.state.angular.pos = -0.2;
    }

    setTimeout(function() { resetShoe(square); }, 500);
});

function resetShoe(square) {
    if (square.state.angular.pos == -0.2) {
        square.state.angular.pos = 0.3;
    }
    else {
        square.state.angular.pos = -0.2;
    }
}

You can see it working here. It works fine but I'd like this to be animated rather than a stop-motion kind of thing. I just can't figure out a way to do it.

j.grima
  • 1,831
  • 3
  • 23
  • 45

2 Answers2

0

Ok so I figured out a way to solve this. I followed the example shown on the documentation where one can create new subtypes and have their own functions as well.

I created a shoe subtype to the convex-polygon with a kick function

Physics.body('shoe', 'convex-polygon', function( parent ){

    return {

        kick: function (operator) {
            if (operator == "add") {
                if (this.state.angular.pos < 0.3) {
                    this.state.angular.pos += 0.1;
                    timer = setTimeout(function() { square.kick("add"); }, 20);
                }
                else {
                    timer = setTimeout(function() { square.kick("subtract"); }, 20);
                }
            }

            if (operator == "subtract") {
                if (this.state.angular.pos > -0.2) {
                    this.state.angular.pos -= 0.1;
                    timer = setTimeout(function() { square.kick("subtract"); }, 20);
                }
                else {
                    clearTimeout(timer);
                }
            }
        }
    };
});

The kick function increases the angle up to 0.3 and reduces the angle back to the original angle which is -0.2. There might be a better way to do this.

So when the click event is fired the kick function is called: kick("add");

jQuery('canvas').on('click', function (e) {

    clearTimeout(timer);

    world.subscribe('step', function( data ){
        square.kick("add");
        // only execute callback once
        world.unsubscribe( 'step', data.handler );
    });
});

clearTimeout is used to eliminate repeated animation of the shoe just in case the mouse is clicked repeatedly.

j.grima
  • 1,831
  • 3
  • 23
  • 45
0

You've got the right general idea, but there is a better way to do this.

If you subscribe to the "integrate:positions" event (which is what behaviors do), you can incrementally change the position on each step until it's where you want.

rotate: function( endAng, vel ){

    var shoe = this; // depending on how you implement this...

    if ( vel === 0 ){
        return; // ... you'll never get there
    }

    // stop any previous animation attempt
    world.unsubscribe('integrate:positions', this.rotateCallback);

    this.endAng = endAng % (Math.PI * 2); // mod by 2Pi since it's circular position
    this.vel = vel;

    world.subscribe('integrate:positions', this.rotateCallback, this);
},

rotateCallback: function( data ){
    var shoe = this; // depending on how you implement this...
    var pos = shoe.state.angular.pos % (Math.PI * 2); // mod by 2Pi since it's circular position

    pos = shoe.state.angular.pos = pos + this.vel * data.dt; // change in pos = velocity x change in time

    // stop at the end
    if ( 
        this.vel > 0 && pos > this.endAng || // increasing
        this.vel < 0 && pos < this.endAng // decreasing
    ){
        world.unsubscribe(data.topic, data.handler);
    }
}

These are methods on some object... but it depends how you implement it. I'm assuming you're using these on the shoe body, but you could also make a "kick" behavior and pass it the shoe as a parameter... but that might be unnecessary.

Haven't tested, so there might be edge case bugs. but should be the general idea.

Note: in the beta release of physicsjs there will be an even better way to do this... but until then this is a good way.

Jasper
  • 1,193
  • 1
  • 9
  • 14