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.

14 comments:

  1. Awesome game dude! I've spent 20 minutes playing that hahah really cool!

    ReplyDelete
  2. lol I posted it about 5 minutes ago, but thanks bro ;)

    ReplyDelete
  3. ive always wanted to learn how to to this stuff. mountains of coding for little things it seems.

    ReplyDelete
  4. I'm messing around with pseudo-code and you're here doing something like this. Nice work! I wish I knew how to code properly, but I don't have the time to actually sit down and learn. It's a bit of a shame.

    Following!

    ReplyDelete
  5. This is really cool and I like your progress.
    Once again, it's really interesting to see how the game is made step by step. :)

    ReplyDelete
  6. Awesome progress! Can't beleive how its taking shape already!! Strong respect!

    ReplyDelete
  7. oh man that game was actually a lot of fun. great job dude keep it up

    ReplyDelete
  8. great stuff mate! i a programmer also and your experience is very helpful :) +follow

    ReplyDelete
  9. this is awesome stuff man, good job

    ReplyDelete
  10. I was too busy reading and by the time i saw the game i was at -1 lives. :[

    ReplyDelete
  11. Very well written and in depth.
    This will be fun to follow :D
    Cant wait for the final game ^^

    ReplyDelete
  12. This is VERY relevant to my interests - I'm also working on a game!!

    ReplyDelete
  13. A start and restart option would be good...but BAH, ignore me - this is still in development, naturally!

    Thus far comin' along good, m'man!

    ReplyDelete