Categories

Friday, December 7, 2012

Create games with LIBGDX library in Android (2) - short physics lesson


   This is a preview before the next improvement in our "Bob" game.



  IMPLEMENTING MOVEMENT IN A GAME LOOP



   Since our next step is to implement Bob's jumping ability, I consider it usefull to discuss a few physics aspects before moving on.

   The game loop is implementing character's position update every couple of miliseconds. This means that the character doesn't actually move on the screen continuously, but rather at discrete time intervals. However, these intervals are so small that the human eye perceives a continuous movement. So all we need to do is translate some simple mechanics formulas in discrete time.

   If you skipped those physics class in high-school , don't worry as it's not at all complicated.



   Moving at a constant speed

   When moving left and right, Bob's speed is constant and changing his position is very simple.
   
   Let v be the constant speed (velocity). A basic, easy to understand formula for the distance traveled at this speed is: 

                x = v*t

   where x is the distance traveled in a time interval equal to t.
   
   How to use this formula? 

   First of all, you need to pay attention at the length and time units you use. 

   In real life, if you are travelling by car, you measure your speed in miles per hour or kilometers per hour. Let's say you are driving at v=100 km/h. If you want to know how much distance can you travel in t=2 hours, simply multiply and get the result:  x = v*t = 100 km/h * 2 h = 200 km. However, if you want to know the distance for t = 30 minutes, first you must transform minutes in hours and then multiply: x = 100*0.5 = 50 km. Easy.
   In our game, we measure distance not in meters or kilometers, but rather in pixels. While theoretically correct would be to go ahead with that and measure Bob's speed in pixels/second, calculations may become complicated when we try to count the pixels on the screen and especially since resolution is different between devices.
   So in our program we decided to express Bob's speed in units/second, where a unit has several pixels (depending on the resolution).

        private static final float SPEED = 4f; // units per second

   This means that if you keep the left button pressed (or equivalent trigger) for a second, Bob will move to the left 4 units (in our world, 4 blocks).

  
   Now that you understand the formula and the measuring units, it's easy to compute the "distance" Bob moves at each update in the game loop:

        position.add(velocity.tmp().mul(delta));

   As you can see, this function adds to the current position the "distance" computed inside the paranthesis. To compute this distance, we use a copy of current velocity's value - velocity.tmp() - and multiply it with the time delta using velocity object's method mul.


   Moving at continuously changing speed


   We only consider the case when the speed changes uniformly  in time. This means that the object has a constant acceleration.
   An example of movement with constant acceleration that will help you understand better this abstract subject is movement under gravity. When you let an object fall from your hand, his speed of dropping towards the earth is not constant, but instead it increases, meaning the object drops faster and faster.
  
   The physics of acceleration may be rather complicated so there's no need to get into the details. The only thing worth mentioning is that the speed follows a similar formula as the position:
  
       v = a*t
  
    where v is the modification in speed in the time interval t.
   
   I'll go straight to using acceleration in our game loop. 
   Since acceleration is an increase (or decrease) in speed, this means that the update method should not only modify the position with the formula above, but will also modify the speed. Without writing any aditional code, we should anticipate that the previous formula used for constant speed movement will actually be replaced by two similar formulas:

        position.add(velocity.tmp().mul(delta));
        velocity.add(acceleration.tmp().mul(delta));


  I believe the code to be self-explanatory. First we modify current position using current speed value, and then we modify the speed's value by adding the product of the acceleration and the time interval delta.

    
   Obviously, the reason why we use such complicated statements (instead of simple variables v for speed, a for acceleration) is because there are two dimensions to consider! Bob doesn't only move left and right, he can also jump and fall, so there are two coordinates (left/right, up/down) upon which we need to compute the modifications. And instead of using two sets of variables for each coordinate, we choose the built-in class Vector2 which basically does both in the same time!



    This is  quick guide to implementing movement before writing the code. If you need more detailed information about vectors, I'll gladly write a standalone article about that. Just leave a comment so I'll know anyone is interested on the subject.

5 comments:

  1. after the physics, i'd like to learn how to make a homepage and a menu :)

    ReplyDelete
    Replies
    1. Sure. After I implement movement and jumping, menu comes next since it was in the schedule anyway.

      Delete
  2. Hey now, don't hog Bogdan, we need to learn some shooting and running animations!

    Also, I think that it might be better to use something other than velocity.tmp or acceleration.tmp, as Libgdx gives warnings on using these items. Perhaps using something like this?:

    velocity.add(bob.acceleration.x);
    position.add(bob.velocity.x * delta, bob.velocity.y * delta );

    might have to add or modify some of Bob.java's methods or whatever you call them.

    ReplyDelete
  3. By the way, I would use .cpy() instead of .tmp() for position and velocity changes, because using .tmp() can cause issues when it is being used twice in the same program.

    ReplyDelete
    Replies
    1. Only now I've seen this post here, but as you suggested later this was modified to act directly on components. Good suggestion though

      Delete