box2d top-down old-school-rpg-like player movement (libgdx)

Discuss issues specific the Java port of Box2D
nicobu
Posts: 2
Joined: Mon May 05, 2014 3:39 pm

box2d top-down old-school-rpg-like player movement (libgdx)

Postby nicobu » Mon May 05, 2014 3:47 pm

Hi guys,

first of all thank you so much for box2d. I really like it. And second: I have read the FAQ but I have to say that I cannot program C++. :)

Here my problem:

I am currently fiddling with box2d and semi-realistic player movement using forces and impulses. This is my current code that is called every frame:

Code: Select all

PhysicsBody phys = physicsMapper.get(e);
Vector2 pos = phys.body.getWorldCenter();
currentVector = phys.body.getLinearVelocity();

float impulse = 1f;
float maxVel = 10f;

if(this.velocity > 0f) {
  // accelerate
  impulseVector.set(impulse, 0f).rotateRad(this.angle);
  phys.body.applyLinearImpulse(impulseVector, pos, true);
  currentVector = phys.body.getLinearVelocity();
 
  if(currentVector.len() > maxVel) {
    // decelerate excess impulse
    impulseVector.set(currentVector).nor().scl(-(currentVector.len() - maxVel));
    phys.body.applyLinearImpulse(impulseVector, pos, true);
  }
} else {
  // decelerate
  impulseVector.set(currentVector).nor().scl(-1f * impulse);
  if(currentVector.len() - impulseVector.len() < 0f) {
    // apply only as much impulse as needed to stop
    impulseVector.scl(currentVector.len() / impulseVector.len());
  }
  phys.body.applyLinearImpulse(impulseVector, pos, true);
}

velocity (set to 1) and angle are set in my InputProcessor when WASD are pressed.
My body and its fixture is loaded from this XML file (values are set accordingly):

Code: Select all

<PhysicsBody type="dynamic">
  <fixtures>
    <fixture density="1.0f" friction="1.0f" restitution="0.0f">
      <shape type="Polygon" vertices="16f,0f 16f,16f 0f,16f 0f,0f"/>
    </fixture>
  </fixtures>
</PhysicsBody>

Now with impulse = 1f, maxVel = 10f and density of the fixture = 1f that seems to work fine. I have smooth movement, acceleration and deceleration. However, my goal is to make my player character weighing 80 kg and apply some kind of forces that simulate the process of running. When I set the density of the player fixture to 80 which makes the player weigh 80 kg (my box2d-to-world-ratio is 16 and the Polygon is 16x16 units big) and the impulse to 80 I would expect that I get kind of like the same movement as when I set both values to 1. This is not the case. The movement is completely messed up. Acceleration seems to be ok, deceleration is slow and the velocity is not capped at maxVel.
Ultimately I want the player character to be able to wear some sort of armor influencing the player movement using the physics system.
I really tried to understand impulse and force and stuff but I am not sure how this can be fully applied to box2d. In every tutorial I found they just set some values that seemed to feel good but I want to understand the basics.
That is why I put together a simple example:

Code: Select all

PhysicsBody phys = physicsMapper.get(e);
Vector2 pos = phys.body.getWorldCenter();
currentVector = phys.body.getLinearVelocity();

float force = 800f;
float maxVel = 10f;

if(velocity > 0f) {
  // accelerate
  impulseVector.set(force, 0f).rotateRad(angle);
  phys.body.applyForceToCenter(impulseVector, true);
  currentVector = phys.body.getLinearVelocity();
  logger.info("Timer: " + timer + " current Velocity: " + currentVector.len());
  timer += world.getDelta();
} else {
  // decelerate
  impulseVector.set(currentVector).nor().scl(-1f * force);
  phys.body.applyForceToCenter(impulseVector, true);
}

From what I thought I knew about physics I think I need 800 N to accelerate my 80 kg character to 10 m/s within 1 second. Right??? (80 kg * 10 m/s^2, could be wrong though :)) And I don't know but I think I have to apply that force continously within that second. When I run the code above I see that the player character reaches 10 m/s at approximately 10 seconds:

Code: Select all

INFO: PlayerControlSystem - Timer: 9.92004 current Velocity: 9.917547
INFO: PlayerControlSystem - Timer: 9.937308 current Velocity: 9.934353
INFO: PlayerControlSystem - Timer: 9.954114 current Velocity: 9.9504795
INFO: PlayerControlSystem - Timer: 9.970241 current Velocity: 9.967404
INFO: PlayerControlSystem - Timer: 9.987165 current Velocity: 9.984491
INFO: PlayerControlSystem - Timer: 10.004252 current Velocity: 10.000299
INFO: PlayerControlSystem - Timer: 10.020061 current Velocity: 10.017794
INFO: PlayerControlSystem - Timer: 10.037555 current Velocity: 10.034302
INFO: PlayerControlSystem - Timer: 10.054063 current Velocity: 10.050466

So using a force of 8000 fixes that. But why? And when I want to decelerate like in my first code is that an impulse then? And how do I then cap the velocity at 10 m/s? That also seems not to work in this example using the deceleration codes from the first example, not when I use an impulse and not when I use force?

Thanks for your help,
Nico

nicobu
Posts: 2
Joined: Mon May 05, 2014 3:39 pm

Re: box2d top-down old-school-rpg-like player movement (libg

Postby nicobu » Tue May 06, 2014 2:13 am

Hi guys,

I think I figured out a lot. :)

Code: Select all

PhysicsBody phys = physicsMapper.get(e);
Vector2 pos = phys.body.getWorldCenter();
currentVector = phys.body.getLinearVelocity();

float impulse = Velocity.DEFAULT_IMPULSE * world.getDelta();
float maxVel = Velocity.DEFAULT_MOVE_SPEED;

if(velocity > 0f) {
   // accelerate
   impulseVector.set(impulse, 0f).rotateRad(angle);
   
   // impulseVector has to be applied directly to allow direction changes at max velocity
   phys.body.applyLinearImpulse(impulseVector, pos, true);
   currentVector = phys.body.getLinearVelocity();
   
   if(currentVector.len() >= maxVel) {
      // set velocity to maxVel
      currentVector.nor().scl(maxVel);
      phys.body.setLinearVelocity(currentVector);
   }
} else {
   // decelerate
   impulseVector.set(currentVector).nor().scl(-1f * impulse);
   
   if(currentVector.len() - impulseVector.len() / phys.body.getMass() > 0f) {
      // only apply impulse does not stop and accelerate the body in the opposite direction
      phys.body.applyLinearImpulse(impulseVector, pos, true);
   } else {
      // if impulse would accelerate in opposite direction set velocity to 0 instead
      phys.body.setLinearVelocity(0f, 0f);
   }
}

An impulse changes the velocity vector instantly. that means if I want to apply a force of 800 N in one second I have to (or I can) apply multiple impulses every 1/60 of a second that make 800 in sum. This works. At one second I the body is at a velocity of 1 m/s. The only thing that still bothers me is that I would like to not apply an acceleration impulse when it would make my body move at more than maxVel but if I don't apply the impulse directly before checking velocity I cannot change direction at max velocity (because the direction change impulse is not applied anymore).


Return to “Java”



Who is online

Users browsing this forum: No registered users and 1 guest