30 July 2011

Another quick update

I just realised it's been a few days since I posted or even looked at anyones blog, so sorry about that.  I've been pretty busy this week trying to find a job, but I found one and start on Tuesday!  Woohoo!  With that in mind I'm going to try to do as much work on the game this weekend as possible because after I start working I will have less time to work on it.  I will still be active and updating the game and blog but probably only once or twice a week.  I have done some work on the game today, with a bit more to do, but I'll have an update within the next few hours.

On another note, one of the comments I received mentioned HTML5 as being more modern and available on more devices (i-crap), and after doing some research I've realised this is correct. Flash is old, it's been around since the early 2000s.  AS3 has modernised it a bit, but AS3 is so different to AS2 that I figure why bother putting effort into learning a new version of old technology, why not just learn how to use the new technology.  From what I have read HTML5 is extremely powerful when combined with simple javascript, and I think it will be more useful in the long run.  So I'm going to finish making circle attack, get it more polished and looking good over the next few weeks, but more than likely the next game I build will be HTML5/Javascript.  What do you guys think?  Let me know in the comments, Flash or HTML5, or would I be better learning some other language / environment?

27 July 2011

Day 4 - Levels, enemy types, menus and other screens



Am I awesome or what! While very basic, I now have a working game with multiple levels, different enemy types, and inter level screens! It's certainly not what I'd call polished yet, and there are still many features to add like an upgrade store, power ups, bosses, and maybe other stuff once it's looking cool.  Only 3 levels at the moment, and they're just to show you the different enemy types, I'll be making more levels and making them longer soon.

There is quite a lot of new code today, but a lot of it's pretty basic. So rather than run through it line by line I'm just going to explain how I did each different part. I'll post the entire code on a separate page. And as usual, if you've got any questions, suggestions, comments or criticisms, please let me know in the comments!

Main Menu & other screens
The first thing I want to say before diving into this is that the way I have implemented the main menu, game over screen, and level complete screen is...less than elegant shall we say.  But it works.  I know that there is another way to do this using frames, and I'm sure it's pretty simple, but I couldn't be bothered with searching for a tutorial today so this will do for now.  It works fine so I don't really care lol.

Lots of buttons.
So basically what I did is created new symbols in the library for the main menu, game over screen, level screen and level complete screen.  An instance of each of these is attached to the stage at runtime.  I then created a function called clearscreen() which, funnily enough, clears the screen.  Basically clear screen first gets rid of any enemies and bullets on the screen, and then moves all remaining movieclips to ._y = 2000.  This means that the movieclips are all still "visible" but they are off the stage so the user can't see them.  When I want to go to the main menu, or game over screen or whatever, I just move the relevant movieclip back into the stage area.  As I said, it's not elegant but it works.  If I haven't been very clear here, sorry, have a look at the code at function clearscreen() and gomainmenu() and you should see what I mean.

The buttons within these screens come from the inbuilt buttons library that comes with Flash CS3.  There are heaps of different buttons to choose from, and once you've added one to your library you can edit it like any other symbol.  I only changed the font and some of the colours for my buttons, but you can customise them as much as you want, or even create your own from scratch.  All the graphical stuff like changing when its moused over and clicked is already built in to these buttons which makes them really easy to use.

Providing the functionality for these buttons is really easy.  For example to write the function for when the user clicks the main menu button in the game over screen I simply write gameoverscreen.btnmainmenu.onPress = function(){//code}

Enemy types
Creating different enemy types is actually very easy, partly because I deliberatley wrote the makeenemy() function so that it would be.  Before coding anything, I need to create symbols in the library for my new enemies.  Now the symbols for all the enemies are exactly the same size (so that the progressbars fit correctly) and then they are resized dynamically in function makeenemy().  So all I did was create two new symbols exactly the same as enemy_1 called enemy_2 and enemy_3, the only difference is the colour.  Once this is done, with a few extra lines added to makeenemy() we can create new enemies.  Basically we to extend the current switch statement so that it will now read like this:
switch(type){
    case 1:
        enemy_maxspeeds[enemycount] = 4;
        enemy_starthps[enemycount] = 20;
        size = 40;
        break;
    case 2:
        enemy_maxspeeds[enemycount] = 5.5;
        enemy_starthps[enemycount] = 12;
        size = 30;
        break;
    case 3:
        enemy_maxspeeds[enemycount] = 2.8;
        enemy_starthps[enemycount] = 35;        
        size = 55;
 break;
}

So now I can pass any number from 1 to 3 to makeenemy() and it will create the correct type of enemy for me. Simple!


Levels and timing
The final, and in some ways most complex part that I did today was to create multiple levels to show off my shiny new enemies.  In order to do this I have created a new function called levelautomation() as well as several new variables to control how the levels work.  I have included a new variable gamestate which determines the current state of the game (duh!) (0=not playing, menus etc, 1 = playing).  Then every frame if gamestate = 1 levelautomation() is called.

Because levelautomation() is called every frame, but obviously we don't want an enemy to appear 30 times a second, we need some way to time how long it has been since the last enemy appeared.  I used the function getTimer() earlier to control the shooting or reload speed of the gun, but I didn't really explain it.  getTimer() returns the number of milliseconds since the game loaded (i think).  This means with some code like this:

leveltimer = getTimer();
onEnterFrame = function (){
    time = getTimer() - leveltimer;
    if (time>=1000){
        //it is 1 second since this code was executed
        leveltimer = getTimer;
    }
}

we can create an effective timer.  Then we can use this to time events down to the millisecond (although I don't know how accurate it is).  This is the levelautomation() function which currently for levels 1 2 and 3 produces the 5 of the relevant enemy type (1, 2 or 3) one every 3 seconds.

function levelautomation(){
    time = getTimer() - leveltimer;
    switch (currentlevel){
        case 1: case 2: case 3:
            if (levelcounter==0||(levelcounter > 0 && levelcounter < 5 && time >= 3000)){
                leveltimer = getTimer();
                levelcounter++;
                makeenemy(currentlevel);
                if (levelcounter==5){
                    endlevel = true;}
                }
                break;
    }
}

Once endlevel becomes true a function is called to go to the completed level screen.

Now, I've decided, because there's so much code, I'm not going to go through every change each update anymore. Theres many more small changes throughout the code to make everything work properly but I'm not going to go through them all, as many are just adding 1 or 2 lines to a function or whatever. I've posted the full code of the project again, and if you have any questions about how I did something or how something works, ask in the comments!

Next update - power ups, and hopefully an upgrade system + different guntypes (lazors maybe..?)

A drawing for my blog!

Sorry, no update on the game today, but I'll definitely have one tomorrow.  I'm in an awesome mood so I just gotta show you guys this.  I had a really long day today, I only got home like an hour ago and its 12.50 am now.  Anyway I come home to find this on my blogspot dashboard:

How AWESOME is this picture?!?!

Haunter at Haunter's Fun Blog drew this for me.  This guy is a crazy talented artist who posts about all kinds of interesting things, from sketches like this one, to making gift boxes out of playing cards, to making his own board game from scratch using cardboard and paper clips.  It's a really interesting blog and you should check it out!

As if that wasn't enough, I found out that Genetics from Understanding Genetics and Physics has posted a link to my blog at the top of his one, just because I answered a question he asked correctly!  How cool is that!  This blog is also really interesting with lots of posts about physics, biology, and wine.  This guy is very intelligent and really knows what he's talking about, so follow his blog if you're interested in physics, biology, or science in general!

So I just wanted to say thanks to both of these bloggers, that really made my day! :D

And yeah, I promise I'll have an update on the game by tomorrow night.

26 July 2011

Upgrade complete

Hey guys this post is nothing to do with my game, but I just found this awesome game, Upgrade Complete.  I don't know maybe you've seen it before, maybe not, but it's a really cool game where you have to upgrade absolutely everything.  Also an interesting commentary on the movement of game developers from focusing on storyline to focusing on upgrade systems.  Definitely worth playing!


[Edit] Blogger really annoys me sometimes, for some reason that's not working properly somtimes, or for some people, or something. It works for me, but if it doesn't for you you can go here to play it instead.

25 July 2011

Day 3 - Enemy collision detection and other things.

Welcome back everyone, I hope you all had a good weekend. My head is sore today and my wallet feels too light, but I had fun so it's all good!

Before I jump straight into the next functions for the games I wanted to answer I question a couple of people have asked in the blog relating to debugging. Yes debugging is a big part of programming, and it can be pretty frustrating at times. Today I was overconfident and rushing a bit, so I wrote all the functions without testing anything till the end. Then when it didn't work I had like 80 lines of code to debug instead of 20 if i'd tested each function.

The way I approach debugging usually is to first of all think about where I think the bug might be and then start commenting out blocks of code to see if the problem goes away. This way you can effectively test each function and figure out where the problem is. After that you can use a function in AS2 called trace() which will output anything you pass to it to the compiler. So you can use this to test variables halfway through a function and see where things are going wrong. It can be frustrating at times, but in the end it's still really satisfying when you fix the problem and get everything working.


Bullet / Enemy collision detection
I already have a function bulletcheck() which checks when a bullet goes off screen, so I will add a few lines of code to this function to check if the bullet collides with an enemy at the same time.  AS2 has an inbuilt collision detection function called hitTest() which works well for simple shapes.  For example if you have two movieclips named shCircle and shSquare you can use the code shCircle.hitTest(shSquare) to test for a collision between the two.  This doesn't work very well for complex shapes, though, so if I make more detailed enemies or bullets I may have to change this.

Here are the lines I added to bulletcheck():

for (b=0;b<enemycount;b++){
    if (_root['bullet_'+bullet_IDs[arraypos]].hitTest(_root['enemy_'+enemy_IDs[b]])){
        hitenemy(b,arraypos);
    }
}

Pretty simple, this just steps through the array of enemies and checks if the current bullet has hit any of them.  If so it calls a function hitenemy().  Incidentally, the bug that I had today, which took me about 20 minutes to find, was with this small piece of code.  It's also the reason i'm using b as the counter in this loop instead of a.  Because bulletcheck() is called from within a for loop which uses a as the counter, when I set another for loop with a as the counter it stopped the first loop from being able to count properly...but it took quite a while to figure out!

function hitenemy(enemyarraypos, bulletarraypos:Number)
Ok, so the next function I need to write is the function which is called when a bullet hits the enemy.  Because I'm planning to have different types of guns, I need to know which bullet as well as which enemy is involved, so that I can provide the correct damage.  Also, as usual, all the new variables introduced here like money and score need to be initialised at the start of the program.


function hitenemy(enemyarraypos:Number, bulletarraypos:Number){
    enemy_hps[enemyarraypos]-=bullet_hps[bulletarraypos];
    if (enemy_hps[enemyarraypos]<=0){
        money += enemy_starthps[enemyarraypos]/4;
        score += enemy_starthps[enemyarraypos];
        killenemy(enemyarraypos);
    } else {
        score += bullet_hps[bulletarraypos];
        //split the bullets power into x and y vals based on the angle of the bullet to the enemy.
        c = Math.sqrt(Math.pow(bullet_xspeeds[bulletarraypos],2) + Math.pow(bullet_yspeeds[bulletarraypos],2));
        xforce = bullet_powers[bulletarraypos] * bullet_xspeeds[bulletarraypos] / c;
        yforce = bullet_powers[bulletarraypos] * bullet_yspeeds[bulletarraypos] / c;
        enemy_xspeeds[enemyarraypos]+=xforce;
        enemy_yspeeds[enemyarraypos]+=yforce;
        //apply a rotation based on which side of the enemy was struck
        if (_root['bullet_'+bullet_IDs[bulletarraypos]]._x < _root['enemy_'+enemy_IDs[enemyarraypos]]._x){
            enemy_rspeeds[enemyarraypos] += Math.round(Math.random()*4)+1;
        } else {
            enemy_rspeeds[enemyarraypos] -= Math.round(Math.random()*4)+1;
        }
        _root['enemy_'+enemy_IDs[enemyarraypos]].progressbar._width = (enemy_hps[enemyarraypos] / enemy_starthps[enemyarraypos]) * 40;
    }
    killbullet(bulletarraypos);
} 

Although it looks complex this function is actually pretty simple. Basically it checks if the enemies health is less than 0. If it is, it increases two new variables, money and score, and then calls another function killenemy().  If not, a force is applied to the enemy from the direction it is hit and applies a rotation to the enemy based on where it was hit.  Thats it really, but if you got any questions, you can ask in the comments.

function killenemy(arraypos)
function killenemy(arraypos:Number){
 removeMovieClip(_root['enemy_'+enemy_IDs[arraypos]]);
 if (!(arraypos == (enemycount - 1))){
  for (i = arraypos + 1;i<enemycount;i++){
   enemy_xspeeds[i-1] = enemy_xspeeds[i];
   enemy_yspeeds[i-1] = enemy_yspeeds[i];
   enemy_rspeeds[i-1] = enemy_rspeeds[i];
   enemy_maxspeeds[i-1] = enemy_maxspeeds[i];
   enemy_hps[i-1] = enemy_hps[i];
   enemy_starthps[i-1] = enemy_starthps[i];
   enemy_IDs[i-1] = enemy_IDs[i];
  }
 }
 enemycount--;
}
I'm not really going to explain this one, because it's pretty simple, and it's almost exactly the same as the killbullet() function from the other day.

function enemycheck(arraypos)
While I have been testing the game, I have been getting frustrated by how easily the enemies go off the sides of the screen.  The combination of a force being applied by the bullet and the random placement of the enemies means that when an enemy spawns close to the edge of the screen it goes off the edge with only one or two hits.  So I decided to make the enemies bounce off the sides of the screen, and I do that with this function.  This function also detects when an enemy goes past the bottom of the screen.

function enemycheck(arraypos:Number){
    //make enemy bounce off wales and also detect when enemy goes off the bottom of the screen
    ex = _root['enemy_'+enemy_IDs[arraypos]]._x;
    ey = _root['enemy_'+enemy_IDs[arraypos]]._y;
    er = _root['enemy_'+enemy_IDs[arraypos]]._width / 2;
    if ((ex < stagexmin+er) || (ex > stagexmax-er)){ //if hit wall 
        enemy_xspeeds[arraypos]*=-1; //bounce
    } 
    if (ey > stageymax - er){ //if off bottom of screen
        killenemy(arraypos);
        lives--;
    }
}



Displaying score etc.
Because I've started using variables for score, lives, money etc. I thought I'd better have some way to display them.  I've implemented a very quick solution for the moment. It looks real ugly, but hey, I'm hungry, I wanna go make dinner, so it'll do for now and I'll pretty it up in a couple days.  For the moment I've just put 3 static text boxes and 3 dynamic text boxes on the stage.  The dynamic text boxes are called txtScore, txtGold and txtLives.  I created a new boolean variable called statuschanged and set it to false.  Now, I just searched the code for any reference to the variables score, lives or money and whenever they changed I set statuschanged to true.  Then I added the following code to gunbarrell.onEnterFrame:

if (statuschanged == true){
    txtMoney.text = money;
    txtScore.text = score;
    txtLives.text = lives;
}

I also added a few other bits and pieces to do things like keep creating enemies until you run out of lives, and suddenly we've got a game. Still very simple, and very ugly, but it's starting to come together. Next time I'll try to look at upgrades and enemy types.

Right, dinner time! I'm starving!



Theres a couple of bugs, but i'm going to fix them tomorrow. Oh I just realised I didn't put a reset button or anything in so you'll have to refresh the page to play again. Also, the codes like 230 lines now so I've posted it on a separate page here.

23 July 2011

Day 2 - Creating enemies

I know I said I wasn't going to update the game over the weekend, but I was bored this arvo so I did a little bit of work.  I didn't get much done, but I now have a function which will create an enemy which will move across the screen.  I only had about half an hour so I haven't coded the hit detection for when the enemy gets shot by a bullet or anything like that yet, but that will come soon enough.

So the first thing  I need to do is create a new MC in the library to represent the enemy.  So I created a circle which will represent the enemy for now, I can always make something better looking later.  I also want some way for the user to know how much health each enemy has left.  So I created another movieclip called progressbar which is just a red rectangle.  I will attach an instance of progressbar to each new enemy and then as the enemies health decreases I will decrease the length of progressbar.  I don't know if that makes sense but you'll see what I mean.

Function makeenemy()
The first function I need to code is the function to create an enemy, which I will call makeenemy().  I'll post the code and then run through it.
enemy_xspeeds = new Array();
enemy_yspeeds = new Array();
enemy_rspeeds = new Array();
enemy_hps = new Array();
enemy_starthps = new Array();
enemy_maxspeeds = new Array();
enemy_IDs  = new Array();
enemycount = 0;
nextenemyID = 0;
function makeenemy(type:Number){
    switch(type){
        case 1:
            enemy_maxspeeds[enemycount] = 4;
            enemy_starthps[enemycount] = 20;
            size = 40;
            break;
    }

    enemy_xspeeds[enemycount] = 0;
    enemy_yspeeds[enemycount] = enemy_maxspeeds[enemycount];
    enemy_hps[enemycount] = enemy_starthps[enemycount]; 
    enemy_IDs[enemycount] = nextenemyID;
    enemy_rspeeds[enemycount] = 0;
    en = _root.attachMovie("enemy_"+type,"enemy_"+nextenemyID,_root.getNextHighestDepth());
    en._x = Math.round(Math.random()*(stagexmax-stagexmin-10))+5;
    en._y = stageymin + (enemy_sizes[enemycount]/2);
    en._width = enemy_sizes[enemycount];
    en._height = enemy_sizes[enemycount];

    progressbar = en.attachMovie("progressbar","progressbar",_root.getNextHighestDepth());
    progressbar._width = 40;
    progressbar._height = 6;
    nextenemyID++;
    enemycount++;
}
Now like the bullets, we need a whole lot of arrays to keep track of the enemy data, and they are declared in lines 1 to 7. Like with the bullets, I need arrays for x, y and rotation speeds. I also need an arrays for health, the enemies starting health (for changing the length of the progressbar), and the enemies max speed.

line 10 declares the function to create a new enemy.

Line 11 is a switch statement which is like an if statement but is better suited to multiple different scenarios. Because I don't know how many different types of enemies i might end up with in the game it is better to use this statement instead of a simple if statement.

What this switch statement is saying is if type = 1 then perform all the code between case 1: and break; and then jump to the closing } bracket of the switch statement.

Lines 19 - 23 are pretty self explanatory, just setting up variables for the new enemy. The enemy appears at a random point at the top of the screen.

Lines 24 - 28 attach the enemy to the screen, resize it and move it into position.

Lines 30 - 32 attach the progressbar to the enemy movieclip and resize it.

Function enemystep()

The second function is very similar to the bulletstep() function I discussed the other day.  It is called every frame and controls the movement of each enemy on screen at that moment.

 
function enemystep(){
    for (a=0;a<enemycount;a++){
        if (enemy_yspeeds[a] < enemy_maxspeeds[a]){enemy_yspeeds[a] += gravity;}
        if (enemy_yspeeds[a] > enemy_maxspeeds[a]){enemy_yspeeds[a] = enemy_maxspeeds[a];}
        if (enemy_xspeeds[a] != 0){enemy_xspeeds[a] *= xfriction;}
        _root['enemy_'+enemy_IDs[a]]._x += enemy_xspeeds[a];
        _root['enemy_'+enemy_IDs[a]]._y += enemy_yspeeds[a];
        _root['enemy_'+enemy_IDs[a]]._rotation += enemy_rspeeds[a];
    }
}  
A pretty simple function, line 2 is a loop to step through all the enemies on screen. Line 3 applies gravity to the enemies vertical (y) speed if it isn't already at it's maximum speed and Line 4 reduces the enemies speed if it goes over the maximum.
Line 5 applies friction to the enemies horizontal (x) speed so that it doesn't just keep going endlessly.
Lines 6 - 8 move the actual object on stage.

Lastly I added a new line to gunbarrell.onEnterFrame() which calls enemystep() every frame.

Ok that's all for now, I'm going out tonight, more probably on Monday. Sorry no working prototype today, you'll just have to wait until Monday.

22 July 2011

Quick update

I realised while lying in bed last night that there is no reason to have the arrays keeping track of the x and y values of the bullets, because those values can be taken from the bullets on screen once they've been created. So I got rid of those arrays and changed all references to bullet_xs[] and bullet_ys[] to _root.['bullet_'+bullet_IDs[a]]]._x and ._y. Its always better to reduce the number of variables and arrays used in the program to conserve memory. I mean, this particular change will probably make very little difference to the memory usage of this game, but it's better practice, so I changed it.

Also, I'm not going to be doing any more work on the game until after the weekend, so it'll be a few days until the next post. I just wanted to say thanks for all the comments, I'm glad you guys are interested and enjoying the blog!

Have a good weekend everyone, and I'll still be checking everyone's blogs over the weekend even if I'm not posting.

Oh, one more thing, if you like the blog and you use stumbleupon you should thumb me up!

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!

20 July 2011

Welcome!

Hi and welcome to my new blog!  I am Hom and I am a 24 year old student living in Sydney, Australia.  I currently am studying Environmental Science, however I have always had a strong interest in computers and electronics.  I was lucky enough to go to a high school which offered a programming class and from then on I have been fascinated with programming and understand how computers and their software work.

I have recently got my hands on a copy of Flash CS3 and started teaching myself Actionscript, and I have decided to start making flash games.  Or at least one game.  I'm going to try and make this game, and if I am successful, hopefully I can make some more, put some ads in them, and have myself a nice little side income.

In the meantime I thought that I would create this blog to chart my progress.  I hope to not only provide help and examples to other people who are trying to teach themselves flash, but also to get feedback from more experienced developers.  And I figure if anyone else out there is also trying to learn how to make flash games they can follow along as I learn.

So have a look around, hopefully you'll find it interesting, and PLEASE comment, I welcome all feedback.  As I said I'm quite new to actionscript and flash and if you see something that I could do better, or do in a different way, or whatever, please let me know!

[EDIT]
A little bit on the structure etc. of this blog.  I intend to create a post every day that I do work on the game, hopefully 3 times a week or so.  If I do a lot in one day then I might split it into multiple posts.  I will post all the code for the project, and I will also upload the .swf files for you guys to download if you want.  You can use my code if you want (I don't know that any of it will be any good :P) but if you do I would request an attribution or acknowledgement or something.

With the comments, I also encourage you guys to post suggestions and improvements which I will consider and may include in the game if I am able to and think it's a good idea.