Showing posts with label Day 1. Show all posts
Showing posts with label Day 1. Show all posts

21 July 2011

Day 1 Part 2 - Shooting

The next part to making this game is to allow the player to shoot on a click of the mouse.  To do this I need a movieclip for the bullet, and I'm going to need a whole lot of variables in the code.  Because there can be more than one bullet on screen at a time I'll need arrays to hold all the bullet data.  For each bullet I need to know its x and y coordinates, its x and y speeds, its rotation speed (the bullets will rotate), how much damage it does to the enemy, how much it pushes the enemy back, and an ID number which identifies which bullet I'm working with.  So I need to create arrays for all of these variables.  I'm not sure if there is a more efficient way of doing this, but if there is, let me know in the comments!

The next thing I need is a few functions to control the bullets.  I need a function to create bullets, a function to move the bullets every frame, and a function to remove the bullets once they leave the screen.

I'll run through the code function by function:

Function makebullet()

function makebullet(){
    //bullet appears at end point of barrell
    //bl = barrell length, barrell_rad = barrell angle in radians
    bxl = Math.cos(barrell_rad) * bl; 
    byl = Math.sin(barrell_rad) * bl;
    bullet_xs[bulletcount] = gunbarrell._x + bxl;
    bullet_ys[bulletcount] = gunbarrell._y + byl;
    bulletspeed = 10;
    
    //split bulletspeed into an x and y value
    ratio = bulletspeed / bl;
    bullet_xspeeds[bulletcount] = bxl * ratio;
    bullet_yspeeds[bulletcount] = byl * ratio;
    bullet_rspeeds[bulletcount] = 10; //rotation speed
    bullet_hps[bulletcount] = 10;     //damage
    bullet_powers[bulletcount] = 4;   //repelling power
    bullet_IDs[bulletcount] = nextbulletID;
    
    //attach a new instance of bullet from the library with name bullet_1, bullet_2 etc.
    bul = _root.attachMovie("bullet","bullet_"+nextbulletID,_root.getNextHighestDepth());
    bul._x = bullet_xs[bulletcount];  //position the movie clip
    bul._y = bullet_ys[bulletcount];
    nextbulletID++;   //increment IDs
    bulletcount++;    //increment bullet count
}

This function will create a new bullet.  It will be called whenever the mouse is clicked.  The comments in this function make it pretty self explanatory but I'll run through it quickly.

    Lines 4 - 7 are finding the end point of the barrell which is also the start point of the bullet.

    Line 8 sets the speed of the bullet

    Lines 11 - 13 convert the bullets speed into an x and y value

    Lines 14 - 17 set up all the other properties of the bullet

    Lines 20 - 22 create the bullet and move it into position

    Lines 23 and 24 increment the variables bulletcount and nextbulletID.

You might wonder why I have two variables, bulletcount and nextbulletID.  This is because bulletcount will be decremented when a bullet is removed (or goes off) the screen but nextbulletID will always increment.

Function bulletstep()
function bulletstep(){
    for (a=0;a<bulletcount;a++){  //step through the array from 0
        curID = bullet_IDs[a]  
        bullet_xs[a] += bullet_xspeeds[a];  //change x and y values based
        bullet_ys[a] += bullet_yspeeds[a];  //on xspeed and yspeed
        _root['bullet_'+curID]._x = bullet_xs[a];  //apply new positions to
        _root['bullet_'+curID]._y = bullet_ys[a];  //relevant bullet on screen
        _root['bullet_'+curID]._rotation += bullet_rspeeds[a]; //rotate bullet
        bulletcheck(a);  //check screen boundaries etc.
    }
}

This function will be called every frame, so 30 times a second, and will move the bullets across the screen.  Again pretty self explanatory but I'll run through it.
    Line 2 - a loop that steps through the array from 0 with the variable a representing position in the array.
    Line 3 - Get the ID of the current bullet.
    Lines 4 and 5 - change the x and y values of the bullet based on the speed.
    Lines 6 and 7 - apply the new position to the bullet on screen.
    Line 8 - rotate the bullet, just cos it looks cool.
    Line 9 - calls a function which we'll write in a second.

It took me a while to figure out that you can dynamically reference attached movieclips based on variables using the block brackets [ ] like this : _root['bullet_'+curID]so that's pretty cool.  Well it's not cool that it took me a while to figure it out but it's cool that you can do that...anyway on to the next function!

 Function bulletcheck(arraypos:Number)
function bulletcheck(arraypos:Number){
    cx = _root['bullet_'+bullet_IDs[arraypos]]._x;  //get x and y position
    cy = _root['bullet_'+bullet_IDs[arraypos]]._y;
    //check stage boundaries
    if (cx < stagexmin || cx > stagexmax || cy < stageymin || cy > stageymax){
        killbullet(arraypos);
    }
} 

Ok, this one is very simple. For the given array position it checks if the bullet is outside the stage boundaries. If it is it calls a function killbullet(arraypos) which will destroy the bullet.

 Function killbullet(arraypos:Number)

function killbullet(arraypos:Number){
    removeMovieClip(_root['bullet_'+bullet_IDs[arraypos]]); //remove the bullet
    if (!(arraypos == (bulletcount - 1))){        //if its not the end of the array
        for (i = arraypos+1;i<bulletcount;i++){ //then move everything in the array    
            bullet_xs[i-1] = bullet_xs[i];        //above this point down one position
            bullet_ys[i-1] = bullet_ys[i];
            bullet_xspeeds[i-1] = bullet_xspeeds[i];
            bullet_yspeeds[i-1] = bullet_yspeeds[i];
            bullet_rspeeds[i-1] = bullet_rspeeds[i];
            bullet_powers[i-1] = bullet_powers[i];
            bullet_hps[i-1] = bullet_hps[i];
            bullet_IDs[i-1] = bullet_IDs[i];
        }
    }
    bulletcount--;  //decrement bullet count
}
  

Again a pretty simple function, this one removes the movieclip from the stage and, if this bullet is not the last one in the array it moves all the higher array elements down once...if that makes sense? LOL it makes sense in my head but I'm getting tired now! ANYWAY, if it IS the last element in the array then you don't need to worry, because the data will be overwritten later.

The last little bit of code we need calls bulletstep every time the gunbarrell enters frame, which is every frame because gunbarrell is attached in the first line of code.

gunbarrell.onEnterFrame = function(){
 bulletstep();
}


So the complete code now looks like this, and it works. The barrel will follow the mouse pointer and a projectile will be fired when the mouse is clicked. There is a reload time so the gun won't fire as fast as the mouse is clicked. Test it out below!

_root.attachMovie ("gunbarrell","gunbarrell",_root.getNextHighestDepth())
gunbarrell._x = 275;
gunbarrell._y = 780;

//bullet vars
bullet_xs = new Array()
bullet_ys = new Array();   
bullet_xspeeds = new Array(); 
bullet_yspeeds = new Array();
bullet_rspeeds = new Array(); //rotation speed
bullet_powers = new Array();  //amount the bullet repels enemies
bullet_hps = new Array();  //amount of damage done by the bullet
bullet_IDs = new Array();
bulletcount = 0;  //how many bullets exist at the moment
shottimer = 0;  //controls shooting speed
stagexmin = 0;  //stage boundaries
stagexmax = 550;
stageymin = 0;
stageymax = 800;
nextbulletID = 0;
curID = 0;
reloadspeed = 200;  //lower number = faster reload speed
bl = gunbarrell._height;

gunbarrell.onEnterFrame = function(){
 bulletstep();
}

onMouseDown = function () {  //when the mouse is clicked
 timesinceshot = getTimer() - shottimer;
 if (timesinceshot >= reloadspeed){
  shottimer = getTimer();
  makebullet();
 }
}
onMouseMove = function () {  //when mouse is moved
    mouse_xdist = _root._xmouse-gunbarrell._x;
    mouse_ydist = _root._ymouse-gunbarrell._y;
    // calculate the angle
    barrell_rad = Math.atan2(mouse_ydist, mouse_xdist);
    // convert to degrees and set rotation
    barrell_angle = toDeg(barrell_rad) + 90;
    gunbarrell._rotation = barrell_angle;
}
 
function toDeg(Rad:Number){return (Rad * 180 / Math.PI);}
function makebullet(){
    //bullet appears at end point of barrell
    //bl = barrell length, barrell_rad = barrell angle in radians
    bxl = Math.cos(barrell_rad) * bl;
    byl = Math.sin(barrell_rad) * bl;
    bullet_xs[bulletcount] = gunbarrell._x + bxl;
    bullet_ys[bulletcount] = gunbarrell._y + byl;
    bulletspeed = 10;
     
    //split bulletspeed into an x and y value
    ratio = bulletspeed / bl;
    bullet_xspeeds[bulletcount] = bxl * ratio;
    bullet_yspeeds[bulletcount] = byl * ratio;
    bullet_rspeeds[bulletcount] = 10; //rotation speed
    bullet_hps[bulletcount] = 10;     //damage
    bullet_powers[bulletcount] = 4;   //repelling power
    bullet_IDs[bulletcount] = nextbulletID;
     
    //attach a new instance of bullet from the library with name bullet_1, bullet_2 etc.
    bul = _root.attachMovie("bullet","bullet_"+nextbulletID,_root.getNextHighestDepth());
    bul._x = bullet_xs[bulletcount];  //position the movie clip
    bul._y = bullet_ys[bulletcount];
    nextbulletID++;   //increment IDs
    bulletcount++;    //increment bullet count
}

function bulletstep(){
    for (a=0;a<bulletcount;a++){  //step through the array from 0
        curID = bullet_IDs[a] 
        bullet_xs[a] += bullet_xspeeds[a];  //change x and y values based
        bullet_ys[a] += bullet_yspeeds[a];  //on xspeed and yspeed
        _root['bullet_'+curID]._x = bullet_xs[a];  //apply new positions to
        _root['bullet_'+curID]._y = bullet_ys[a];  //relevant bullet on screen
        _root['bullet_'+curID]._rotation += bullet_rspeeds[a]; //rotate bullet
        bulletcheck(a);  //check screen boundaries etc.
    }
}

function killbullet(arraypos:Number){
    removeMovieClip(_root['bullet_'+bullet_IDs[arraypos]]); //remove the bullet
    if (!(arraypos == (bulletcount - 1))){        //if its not the end of the array
        for (i = arraypos+1;i<bulletcount;i++){ //then move everything in the array   
            bullet_xs[i-1] = bullet_xs[i];        //above this point down one position
            bullet_ys[i-1] = bullet_ys[i];
            bullet_xspeeds[i-1] = bullet_xspeeds[i];
            bullet_yspeeds[i-1] = bullet_yspeeds[i];
            bullet_rspeeds[i-1] = bullet_rspeeds[i];
            bullet_powers[i-1] = bullet_powers[i];
            bullet_hps[i-1] = bullet_hps[i];
            bullet_IDs[i-1] = bullet_IDs[i];
        }
    }
    bulletcount--;  //decrement bullet count
}

function bulletcheck(arraypos:Number){
    cx = _root['bullet_'+bullet_IDs[arraypos]]._x;  //get x and y position
    cy = _root['bullet_'+bullet_IDs[arraypos]]._y;
    //check stage boundaries
    if (cx < stagexmin || cx > stagexmax || cy < stageymin || cy > stageymax){
        killbullet(arraypos);
    }
}



I think I might have to muck around with the size of the stage because it looks a lot bigger on the blog than it does in cs3. Anyway that's enough for tonight, I'm going to have a drink!

Day 1 - Player movement etc.

I've decided that I'm going to make a "shooter" type game in which the player will be represented by a gun or cannon which is fixed to the middle of the bottom of the stage.  The barrel of the gun will follow the mouse cursor and a bullet of some sort will be fired when the mouse is clicked.

Although this is quite a simple game, I have chosen it because I am confident that I can create the mechanics of this game relatively easily, and I figure it's best to start with a simple project that I have a chance of completing.  I can also add things like upgrades, different types of enemies, different types of weapons etc. to make the game more interesting.

Player Creation
The "gun"
So the approach I am going to take to this game is first to create the main game mechanics, and then I can build things like the main menu and inter-level screens later.  So first things first, I create a new flash file with actionscript 2.0.  I am using AS2 because that is what I have some previous experience with, and from what I've seen AS3 is considerably different to AS2 in terms of structure and syntax.  So for the moment I'm just going to stick to what I know.  Later, if I'm successful with this project, I might try to convert it to AS3 as a way to learn it.

I have setup the flash movie to be 550 x 800 px and a frame rate of 30 fps.  Now the first thing I need to do is create a movieclip which is going to represent the player and place it on the screen.  I'm not the most creative person so for the moment I'm going to use pretty simple sprites that I can create with simple shapes in flash.  I've created a gun out of a grey circle, square, and rectangle which I've then converted to a symbol (right click, convert to symbol).  Be sure to give it a name (I've called mine gunbarrell) and in the linkage box click Export for ActionScript.  This will allow you to reference the object from the code.

There is also a section called registration with 8 white boxes and one black box.  This allows you to define which part of the symbol the _x and _y values of it will refer to.  Just choose the middle one for now as we will change this manually in a moment.

Once you convert it to a symbol it will appear in your library.  You can now delete the gun off the stage, because we will be creating an instance dynamically in the code.

Now before we jump into the movement code we need to do one more thing - change the registration of the gunbarrell movieclip.  Because I want the gun barrel to rotate around the center of the circle, as opposed to the center of the whole movieclip, we need to manually move the registration point to the center of the circle.  To do this double click the gunbarrell movieclip in the library, which will take you to the symbol editing mode.  A small black cross represents the registration point so drag this to the center of the circle, then click the blue back arrow on the toolbar to return to the stage.


Coding Player Movement
Okay, so we're ready to start coding!  Right click on the first (and only) frame in the timeline and click actions.  This is where we will be writing most of the code for the game.  The first thing we need to do is attach a copy of gunbarrell to the stage: 

_root.attachMovie("gunbarrell","gunbarrell",_root.getNextHighestDepth())

Pretty simple.  the function attachMovie(idname, newname, depth) attaches a movie clip to the stage.  idname is the name of the object in the library, newname is the name used to refer to the object in the code and depth is, well, the objects depth.

Now that we have the gunbarrell attached to the stage we can change it's x and y values, and it's rotation easily.  So let's move it to the center of the bottom of the screen.
gunbarrell._x = 275;
gunbarrell._y = 780; 


So, what we want is for the gun barrell to rotate in such a way as the end of the barrell always follows the mouse pointer.  It took me a little while to figure this out, so what I'm going to do is just post the whole function and then run through it.

onMouseMove = function () {
    mouse_xdist = _root._xmouse-gunbarrell._x;
    mouse_ydist = _root._ymouse-gunbarrell._y;
    // calculate the angle
    barrell_rad = Math.atan2(mouse_ydist, mouse_xdist);
    // convert to degrees and set rotation
    barrell_angle = toDeg(barrell_rad) + 90;
    gunbarrell._rotation = barrell_angle;
}

function toDeg(Rad:Number){
    return (Rad * 180 / Math.PI);
}

Ok, so the first line just creates a new function which will be executed whenever the mouse moves.  Then we set 2 variables, mouse_xdist and mouse_ydist to the x and y distances from the mouse pointer to the registration point of gunbarrell.  These two distances effectively form the two non-hypotenuse sides of a right angle triangle.  We want to find the angle that the barrell is pointing, which is also the bottom angle of this triangle, and we do this in the 5th line using trigonometry.

The reason we use the atan2 function (and why we don't need to worry about negative angles) is explained best by flash-creations.com
atan2 is often more useful than atan in applications involving rotation by a specified amount because it returns a positive beta for all angles between 0 and 180 degrees (even when x is negative). Thus it eliminates the need for extra code to deal with different values in different quadrants of the circle.
Because the actionscript trig functions return results in radians, but the _rotation attribute of a movieclip is accepted in degrees, we need to convert barrell_rad to degrees so I have written another function, toDeg(Rad), to do this.


The last thing I did today was create the bullet shooting and movement, however I'll post that in the next post because this one is long enough already.  Should be up in a few hours!