Saturday, February 23, 2008

Tanks!

Well after my last PC crashed, half my RTS game went out the window.

I've been making a little tank game, nothing too complex. The script is in the link below.

Tank script


Note that while I'm calling it Tanks! right now, thats an unofficial name, just something I'm using so I have something to go by. It going to be a very simple game, in essence, its capture the flag. In each level, there is a flag and a boss tank guarding the flag. It is your job to pick up that flag.

At this stage, the game is far from complete, but I have the player controls nearly where I want it. The tank moves forward and backwards with W and S, and it turns with A and D, leaving the player free to use the mouse with ease, as is convention with many games. The mouse is used to aim the turret and to fire the turret. The turret and the tank are separate models with separate actions.

The player's tank:


action playerTank
{
player = my;
my.fat = on;
my.narrow = off;
my.health = 100;
while(my.health > 0)
{
my.speedx = velocity * (key_w - key_s);
my.pan += 1 * (key_a - key_d);
move_mode = ignore_passable + glide;
ent_move(my.speedx,nullvector);
camera.x = my.x;
camera.y = my.y - 750;
wait(1);
}
ent_remove(me);
}
What you have here is the script that is attached to the body of the tank. It is a very simple script right now, and is likely to remain simple throughout the development of the game as it already does everything I think it needs to do.

First line within the action sets the pointer. This is rather important as other entities will be dealing with the player and they need to know what entity they are suppose to be interacting with. Next three lines give it a wide bounding box and set the player's health to a starting point of 100.

Now everything the tank does is done while the tank is alive. Thus we need to put the remainder of the instructions in a while loop and set the following instructions to continue processing as long as the tank is alive.

First thing to do is to get the tank to move. The tank needs to respond appropriately to the WSAD keys. And I need to use the command ent_move(relative distance,absolute distance) to get collision detection and to make the tank turn properly. The two parameters in ent_move() are vectors. My.speedx is the vector in this instance and velocity is the forward speed, set initially to 10 and modified elsewhere in the script (as of right now, turbo().) Velocity needs to be modified with keys on the keyboard.

Once we have the speed defined in the vector, we need the keybindings. Every key on the keyboard is assigned a variable that can be called. The W key is assigned to key_w and it is equal to 1 when pressed and 0 if not. Thus, we can simply use these variables in the equation.

my.speedx = velocity * (key_w - key_s);
What does this do? Its simple really. Velocity is 10 (we will ignore turbo() for now.) key_w and key_s are both 1 or 0 depending on if they are pressed or not. You have 4 possibilities:

If W is pressed, my.speedx = 10 * (1 - 0) = 10 * 1 = 10
If S is pressed, my.speedx = 10 * (0 - 1) = 10 * -1 = -10
If neither is pressed, my.speedx = 10 * (0 - 0) = 10 * 0 = 0
If both are pressed, my.speedx = 10 * (1 - 1) = 10 * 0 = 0

The next line:
my.pan += 1 * (key_a - key_d);
works the same way only instead of affecting forward/backward movement, it causes the tank to rotate around its z-axis (up and down), in a pan movement. The next line ensures that the tank ignores anything marked "passable" and that it glides when it hits something, rather than getting stuck. Then ent_move is called and the tank will move according to whichever buttons are being pressed. My.speedx is placed in the first parameter of ent_move() because that parameter uses the model's origin rather than the world's origin. This is important because if I pan the tank, the tank's origin rotates but the world's origin does not. Turning the tank makes its x-axis rotate, but not the world's x-axis. If I used the world's origin, then if I turned the tank, the tank would still go forward, even though the body is facing in another direction. So I use the first parameter in ent_move(). The second parameter, unlike the first, uses the world's origin. I don't need that, so I set it to 0 (nullvector is (0,0,0) .)
ent_move(my.speedx,nullvector); 
The next two lines put the camera above the tank and back a bit. This position is updated every frame (and there are about 30 frames per second, depending on the computers performance and a number of factors) Now it may seem odd that I used the y axis to move the camera back. It should be the x-axis, but I believe that this is due with the position and the orientation of the camera as it is placed in Gamestudio. The camera is behind the tank, looking down at about 70ยบ from the horizontal.

The last line in the while() loop is a wait. This ensures that all the instructions execute themselves in that frame before the loop goes back to the beginning. Otherwords, the script will attempt to call these instructions on top of each other and the loop will be endless and the game will crash.

The last line is ent_remove(me) and that was put in simply for testing. Later it will be removed and the game will, at player death, bring up a menu that allows for the player to try again, go to a saved game, or quit the game.

Well, we have a tank that moves. But it has no turret and it doesn't shoot anything! The turret is a separate model and I will talk about that in the next post.