Puzz3l

Force sprite to rotate with P2 physics body

Recommended Posts

I have a sprite and a body that both rotate towards the pointer. On collision, though, the sprite sometimes gets turned away from the body. The body continues to point towards the mouse but the sprite does not (see image, definitely not the worst that it can be).

 

NeFKr9U.png

 

I haven't found a way to fix this, anyone know how? 

Share this post


Link to post
Share on other sites

It only seems to happen when I run into world bounds; it that useful?

 

EDIT: The specific code I use, could it be because I'm using an arcade function of a P2 body?

this.arrow.body.rotation = game.physics.arcade.angleToPointer(this.arrow);

Share this post


Link to post
Share on other sites

Never ever mix physics! In this case it's not really that you use a arcade method. It look only like a helper method. But you're overwriting the rotation.

So it's basically ignoring the p2 rotation. Remove it and you will see it works. If you want the ship to rotate in the direction of the pointer you have to use the property 'angularVelocity' or use the helper method rotateLeft & rotateRight on the body.

 

You can calculate what's needed to rotate to a desired angle with that basic formula (in game#update )

//that's the value which is missing to get to the desired angle//targetAngle would be the result of angleToPointer, currentAngle is the current rotation.deltaAngle = (targetAngle - currentAngle) //You could apply the full difference with rotateRight.//You don't need rotateLeft as this is done by passing negative values (or maybe you have to use rotateLeft.sprite.body.rotate.rotateRight(deltaAngle) //you could also use only a fraction of it so it's not a little bit lagging. Sometimes a desired effect.sprite.body.rotate.rotateRight(deltaAngle * 0.1)

I hope that helps and works!

Regards George

Share this post


Link to post
Share on other sites

There is no sprite.body.rotate.rotateRight() method, but there is a sprite.body.rotateRight(). Unfortunately it's giving worse results than before... Trying to figure out why atm.

 

EDIT:

I also don't understand why setting rotation directly is so bad; I'm not too concerned about complete accuracy of collisions, I just need the hitboxes to work and the sprite to line up with the body.

 

Why doesn't this work?

this.arrow.body.rotation = game.physics.arcade.angleToPointer(this.arrow);this.arrow.angle = (180/Math.PI) * this.arrow.body.rotation;

Share this post


Link to post
Share on other sites

Sorry for the typo with 'body.rotate'. That tool is the right one but your solution is also nearly working.

You have three possibilities:

1. Set body to fixedRotation (body.fixedRotation = true). Now the rotation of the sprite is in your hands- but decoupled from the body- obviously not want you want.

2. Update the rotation as you planned it. Simply do this:

  sprite.body.rotation = game.physics.arcade.angleToPointer(sprite) - Math.PI/2

-Math.PI/2 is -90deg end normalize the value from Math.atan2 so that 0deg is the top of the sprite. The rotation you set here will be synced with the body later in the body update automatically. With a physic engine, you have to let the engine do the work. So never touch the rotation property of the sprite itself. Downside of this method: If you want to apply any other force to the rotation you have to calculate it yourself. That brings us to the angularVelocity method via rotateLeft/Right.

 

3. Rotate the sprite via a velocity value. The angular velocity. 

Here you have to calculate a little more, as the angle jumps when the phase of the angle changes. You won't notice it in example 2, as there are no substeps.

 

This is the method I tested for you.

function update() {  //1. angleToPointer makes no assumption over our current angle- th thinks it's always 0  //2. so include the current rotation of our sprite in the expression  //3. subtract Math.PI/2 as the angle of atan2 (which is sued by angleToPointer) is rotated by 90deg (this is Math.PI/2)  //Result: Now we have a delta value that if applied directly to rotation would yield  //in a value so that the sprites top center points to the mouse.  deltaMouseRad = sprite.rotation - game.physics.arcade.angleToPointer(sprite) - Math.PI/2;    //don't be confused. I want the P of 'Phaser' to point to the mouse so rotate it again by -90deg  deltaMouseRad = deltaMouseRad - Math.PI/2  mod = Math.PI * 2  //modulo on float, works in js, means: clamp value to [-Math.PI*2,Math.PI*2]  deltaMouseRad = deltaMouseRad % mod;     //lets call it phase shift, angle would jump, lets fix it  if (deltaMouseRad != deltaMouseRad % (mod/2) ) {     deltaMouseRad = (deltaMouseRad < 0) ? deltaMouseRad + mod : deltaMouseRad - mod;  }  //speed is some factor to get the object faster to the target rotation.  //remember we are wotking with the angle velocity and let the engine  //rotate the body  speed = 150  sprite.body.rotateLeft(speed * deltaMouseRad);}

Here a fiddle of it. You can change the update method from update: update to update: updateSimple to watch the simple method mentioned in 2)

http://jsfiddle.net/wvaJ3/1/

 

I only did this summary of this topic so I could verify what I thought. With a little more of endurance you should have found it yourself.

 

Regards George

Share this post


Link to post
Share on other sites

Thanks! This works great. The only part I don't totally get is

if (deltaMouseRad != deltaMouseRad % (mod/2) ) {   deltaMouseRad = (deltaMouseRad < 0) ? deltaMouseRad + mod : deltaMouseRad - mod;}

It seems like this further acts to limit the range of deltaMouseRad to [-Math.PI, Math.PI], correct? This keeps it from jumping as there is no need to turn 270 counterclockwise if you can just turn 90 clockwise.

Share this post


Link to post
Share on other sites

You're correct. I spend quite some time on this formula by looking at the sine curve with my mind's eye and of course a lot of trial and error. It's much more clearer when using an if statement instead of that lazy ternary operator.

if (delta > Math.PI){   //too far on the right, jump back at the same position on the previous phase   delta = delta - Math.PI*2}else if (delta < Math.PI){   //too far on the left, jump back at the same position on the next phase   delta = delta + Math.PI*2}

Regards George

Share this post


Link to post
Share on other sites

George, could you explain this line please? I really love your solution and I wish to understand it.  :)

deltaMouseRad = sprite.rotation - game.physics.arcade.angleToPointer(sprite) - Math.PI/2;

Thank you very much! 

Share this post


Link to post
Share on other sites

Hey,

notice the 'delta' in the variable name stands? Delta is the mathematical name for the difference of two numbers.

So that line in question is basically:

difference = currentRotation - newRotation

where newRotation equals 'game.physics.arcade.angleToPointer(sprite) - Math.PI/2'

and currentRotation is 'sprite.rotation'.

 

If you add the calculated difference to the current rotation of the sprite, you would set the sprite instantly to the target angle (angle to the mouse pointer) you want to reach.

But here that value is used to provide the correct direction for rotateLeft & rotateRight so you get a smooth transition calculated by the physics engine.

 

You should read my comments in the code and fiddle with the given code example.

 

Have fun.

Regards George

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.