Tuesday, December 22, 2015

libgdx - Movement and rotation of bodies in top down game


We are developing a 2D top down game with libgdx using the included box2d as physics engine.


We don't want our bodies to accelerate but to instantly move with the wanted velocity. The bodies are moved like this, which works perfectly for our purpose:


Vector2 currentVelocity = body.getLinearVelocity();
Vector2 targetVelocity = velocity.velocityApplyVector.cpy();
targetVelocity.sub(currentVelocity);

targetVelocity.scl(body.getMass());


body.applyLinearImpulse(targetVelocity, body.getWorldCenter(), true);

The problem now is, that we also want the bodies to instantly rotate to the direction they are facing. The direction of the player is determined by the mouse position, so it is possible that the body is moving in another direction than it is facing. I tried to accomplish it like this:


float bodyAngle = body.getAngle() * MathUtils.radDeg;
float totalRotation = (inputRotation - bodyAngle) % 360;
if(Math.abs(totalRotation) > 1) {
if ( totalRotation < -180) totalRotation += 360;
if ( totalRotation > 180) totalRotation -= 360;
float impulse = body.getInertia() * totalRotation;
body.applyTorque(impulse, true);

}

This causes the body to rotate in the wanted direction but it needs multiple frames until it totally reaches the wanted angle.


The body mass is set to 70 and the angular damping to 8 (I tried some values for the damping and 8 was the best value for my cause, but that's, of course, not a final solution...).


I guess I have to somehow increase the needed impulse depending on the body mass and the angular damping (we want to be able to change it) but I can't figure out how to get the values right. I don't want to use setTransform since it totally destroys the physically correct behaviour of the bodies. How can I calculate the needed values? Is there any better way to solve my problem?



Answer



Simple Solution


If you want the body to instantly rotate just call Body::setTransform and pass the current position and the desired angle, don't bother applying torques or anything.


The function call could be something like this:


body.setTransform(body.getPosition(),myDesiredAngle);


Physics Solution


If you want the player body to interact with bodies while rotating to the cursor position (i.e. you can knock them out of the way) then I would recommend another method:


FollowCursor setup


The numbers in the figure above are described here:



  1. The player body. An isoceles triangle shows the direction the body is pointing

  2. A kinematic body. This is not necessary for your game, it just plays the roll of continuously moving the player to the right while allowing it to face another direction. It is connected to the player body via a revolute joint

  3. A cursor body. This body should not collide with anything. This body will follow the cursor as closely as possible (more on that later)

  4. A wheel joint connecting the player body to the cursor body. We really want it to act as a slider (prismatic joint), but we also don't need to constrain the angle of the cursor body. This joint will force the player body to face the cursor body at all times. As such we want the frequency, damping ratio, motor speed, and max motor speed all set to zero.



The result:


followcursor gif


Now you will notice that in the rube editor it's just the mouse (mouse joint) that I used to move the cursor body around, and I didn't achieve the instantaneous rotation you are looking for. But do not dispair, because you can get around this in one of two ways:



  1. Set the transform of the cursor body at every step like described in the simple solution above. I'm not sure that this won't break physics all the time. If the player body is locked in a corner it might just move through other bodies to satisfy the joint constraint.

  2. Create a second cursor body. Connect the second cursor body to the first using a motor joint (box2d 2.3.0). I used a max force of 10N and a correction factor of 0.90. In the game, the transform of the second cursor body is then set to the position of the cursor every time step.


In the gif below, the second cursor body is the larger square body:


follow cursor motor joint



As a side note, the motor joint was surprisingly stable. I tried to get it to work with a distance joint and kept getting jitters and/or cases where the high damping caused a slow contraction of the joint.


My intuition tells me there will still be kinks for you to work out. The problems you encounter will depend on your gameplay.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...