Monday, 8 July 2013

Game Programming Essentials: Motion of Objects

First topic: making stuff move, obvious stuff.

When I first started making stuff move I tried the following:

for(int x=0;x<640;x+=10)
  draw_enemy(x,20);

But it quickly became obvious that this kind of idea won’t work.


Quite quickly you find out about the game loop:


while(!game_over)
{
  move_everything();
  draw_everything();
}

Which clears matters up and you figure out what you need to do.






Tip:
For those programming XNA: those are the Update() and Draw() functions.
For Unity3D & Flash: its you don’t usually see the Draw() function, you just have a script attached to a sprite/game object, which is in charge of transforming the object.
But generally its the same kind of code.


 


Simple position based movement


So we usually end up with some code to move our player which looks a little like this:



Sprite player;
...
const float SPEED=10;
if (KeyDown(LEFT))
  player.x-=SPEED;
if (KeyDown(RIGHT))
  player.x+=SPEED;
if (KeyDown(UP))
  player.y-=SPEED;
if (KeyDown(DOWN))
  player.y+=SPEED;

And this works just fine.






Tip: Dealing with variable frame rates

Many game engines (especially the 3D ones), are variable frame rate. That means that they might run 30-60 frames per second depending upon how much drawing has to be done. To overcome this variableness (you don’t want your player running twice as fast on some machines), you will include the delta time (amount of time elapsed between two frames) included in the computation:



if (KeyDown(DOWN))
  player.y+=SPEED * deltaTime;


You will need to look in your game engines documentation to check what delta time is in your code.


 

Simple velocity based movement


The next thing we think about it usually how to make a bullet/bomb/car move at a fixed speed in a direction. Normally we end up with code like this:



Sprite bullet;
...
const float SPEED=10;
bullet.y= -SPEED * deltaTime; // -SPEED as its going up

Any this works provided all bullets/bombs/cars go in the same direction, but usually that’s not the case. Therefore we need to have a velocity variable added to our Sprite class (I’m going to assume its vx & vy for simplicity). And our code becomes:



Sprite bullet;
const float SPEED=10;
bullet.vx=0; // not moving left/right
bullet.vy= -SPEED; // moving up
...
bullet.x = bullet.x + bullet.vx * deltaTime; // move by vx
bullet.y = bullet.y + bullet.vy * deltaTime; // move by vy

Does this look familiar to you? How about this?

image

This is the standard equations of motion (https://en.wikipedia.org/wiki/Equations_of_motion) or from your O-level/high-school physics text book.

Look at equation [2], and the code above:



bullet.x = 0 + bullet.vx * deltaTime; // move by vx
s = ut + ½ 0 t2 [2]
// Simplify:
bullet.x = bullet.vx * deltaTime; // move by vx
s = u * t

Guess what? The physics stuff which you studied because you had to pass the physics paper is actually used in your game.








Comment: vx,vy verses vel.x, vel.y

You might be using a game engine with a Vector2 or similar so instead of:



bullet.x = bullet.vx * deltaTime; // move by vx
bullet.y = bullet.vy * deltaTime; // move by vy


You have:



bullet.pos.x = bullet.vel.x * deltaTime; // move by vx
bullet.pos.y = bullet.vel.y * deltaTime; // move by vy


Or even:



bullet.pos = bullet.vel * deltaTime; // move by vel

The concept is the same, it’s just a matter of fitting it to the syntax/game engine.

Remember, I’m selling concepts here, not exact code.

 


Comment: 1D/2D/3D

All my examples here are 2D based (x,y), but the code work 100% the same for 3D (x,y,z) or even for 1D (if you wanted that). If there is anything which is different between 2D & 3D, I will mention it.


 


Adding acceleration to our velocity based movement


Well if the physics stuff works with our velocity routines, we could also add in the acceleration routines. Lets assume the sprite class has an ax, ay for acceleration:



Sprite bullet;
const float SPEED=10;
const float GRAVITY=2;
bullet.vx=0; // not moving left/right
bullet.vy= -SPEED; // moving up
bullet.ax=0; // no acceleration
bullet.ay=GRAVITY; // gravity is down
...
bullet.vx += bullet.ax * deltaTime; // apply acceleration
bullet.vy += bullet.ay * deltaTime; // apply acceleration
bullet.x += bullet.vx * deltaTime; // apply velocity
bullet.y += bullet.vy * deltaTime; // apply velocity

Any there we have it. We launch our bullet, it goes up in the air, and then down again.

 






Comment: Gravity is not always 9.8

I’m sure a few of you looked at the code & said ‘gravity is not 2, its 9.8’. Sorry you are wrong. Gravity on earth is 9.8m/s, but this is not earth, this is a computer game. Our unit of measurement is not meters, its pixels (or some other abstract unit). You will need to fiddle with your SPEED/GRAVITY values until they are the right amount for your game.


 

Adding direction to our velocity based movement


Ok, so a bullet up/down is rather boring, you probably want some kind of directional aiming (like a cannon trajectory), like this:

image

OK, well dust off your maths textbooks & answer me the following question:

image

“But Sir, this is maths!” I hear the cry.

Yes it is! Just like the physics we did earlier!

So get over it & solve it.

...

A bit of head scratching and sin/cosine laws later and we get:






vx = vel cos(elev)

vy = vel sin(elev)


Looking back over our code from earlier we could make the following changes:



const float GRAVITY=2;
const float VELOCITY=5;
const float ELEVATION=PI/2;
// bullet.vx=0; // not moving left/right
bullet.vx=VELOCITY * cos(ELEVATION); 
// bullet.vy= -SPEED; // moving up
bullet.vy=VELOCITY * sin(ELEVATION);

...

And ‘hey presto’, we have a moving object flying on a nice trajectory. All of a sudden all these math classes seem to have some use Smile

 

Next Step: Space Flight


How much new stuff do you think you need to learn to move on to asteroids?

image

Well, pretty much nothing new. It’s just a matter of applying it in a slightly different context.

So here is our basic idea:

The player has a position, a velocity and a rotation. When the press the forward key, it will generate a thrust (acceleration) in whichever direction the player is facing right now. The original asteroids game often did not include a deceleration (drag), but we can add it in later if we chose to.

So here is our basic code:



Sprite player;
const float SPEED=10, TURN_SPEED=PI/2;
player.position=...; // set initial position
player.angle=0; // no angle
player.velocity=Vector2(0,0); // no velocity
...
if (KeyDown(LEFT)) player.angle-=TURN_SPEED * deltaTime;
if (KeyDown(RIGHT)) player.angle+=TURN_SPEED * deltaTime;
if (KeyDown(THRUST))
{
  Vector2 accel;
  accel.x= SPEED * sin(player.angle);
  accel.y= SPEED * cos(player.angle);
  player.velocity += accel * deltaTime;
}
player.position += player.velocity * deltaTime;

 






Comment: Introducing Vector2

I’m getting a little tired of x+= vx; y+= vy; So I’m going to assume some kind of class (Vector2) which holds an X & Y parameter that supports the basic arithmetic operation (+ - * /). Otherwise this code is going to get more and more tedious.


Take note how, when the player presses thrust the velocity changes. But the movement happens regardless of whether the player is thrusting or not. This means that once the player begins moving, they will never stop. The player can only stop if they deliberately turn to face the opposite direction and thrust to stop themselves. This is accurate physics, but not necessarily good for our game.






Comment: Accuracy vs Playability

A key thing to remember in all games is we are aiming for fun, not realism. When I used to play counter-strike in the LAN shop & used to get annoyed at the expert players who would jump out of second floor windows, shooting as they fell & kill me. Or the players who would be continually jumping all over the place while shooting me.
Is it accurate to allow players to jump out of windows without issues? Of course not, but this is an example of where accuracy of the physics is ignored for the benefit of the game.


 

Adding a slowdown: Drag


Now back on topic: lets add in some kind of friction to slow the player down. Actually without some kind of drag it would be possible for the player to reach infinite speed, since they can accelerate forever in one direction. The drag also allows the player who doesn’t press a key to eventually come to a stop. It’s actually quite simple to do:



Sprite player;
const float SPEED=10, TURN_SPEED=PI/2, DRAG=0.01f;
...
if (KeyDown(THRUST))
{
  ...
}
player.velocity -= player.velocity * DRAG * deltaTime; // drag
player.position += player.velocity * deltaTime; // move player

The basic idea here is that, there will always be a bit of drag to slow the player down. The drag is proportional to the players speed. If the drag is 1/10 that’s quite a bit of friction & it will slow down fast. If its 1/1000 that’s very little and will only slow eventually. If the friction is 0.5 or even 1.0, then the object will hardly move at all.

I think the top speed should be SPEED/DRAG, but check it in your game to see how it works & adjust the factors to fit your need.

 

Conclusion


That’s our basic movement. It was only 2D, but the 3D version is just the same. That’s enough for basic gameplay.

Happy Coding:

Mark

No comments:

Post a Comment