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!