A simple AGS Shoot 'em up

Started by Arboris, Tue 03/11/2009 20:51:04

Previous topic - Next topic

Arboris

Hey, I've been busy with a simple shooter concept as of late and have a question about the maximum number of objects one can use on a single screen. Well, I've run into this problem fairly quickly since every bullet I use on screen has to be it's own object. This including with the enemies I top the maximum objects very, very quickly.

Now, I could reuse the old objects and just designate a certain number row of objects to enemies, bullets and so on (e.g. object 10 to 19 for enemies, 20 to 29 for projectiles) and change their given appearance. But this is allot more fiddly and less readable then designate objects to a fixed object number.

So, is it possible to up the maximum? Or am I going to have it do the hard way  :'(

 
Concept shooter. Demo version 1.05

Shane 'ProgZmax' Stevens

You really shouldn't use objects for anything as numerous as bullets, Arboris.  The ideal solution, really, is to use the rawdraw functions and make your own bullet structure so you can create as many bullets as needed.  Another approach, and one I take in games that don't require 'a lot' of moving things, is to use characters instead of objects since you can have an infinite number of them and they're room independent.  Ultimately though, for a shooter where you typically have dozens of onscreen bullets at any given time you'll want to create and render and recycle the sprites yourself.

Arboris

I was using objects because of the wonderful pixel perfect collision detection plugin (which also works with characters, I know. but that was before I knew of the max of 40 objects per screen). I'll try using the 'DrawImage' function instead then, and handle the amount of projectiles myself. That'll make a good exercise to keep me occupied for a while.
 
Concept shooter. Demo version 1.05

suicidal pencil

You basically described the problems I ran into (and worked around) when I made this (http://www.adventuregamestudio.co.uk/yabb/index.php?topic=39156.0).

I solved the problem by limiting the bullet to using one object only, and making sure that the object is not in use when the player is not firing again.

Code: ags

//Assume that Object 39 is reserved as a bullet
function shoot_bullet()
{
  if(object[39].Graphic == 0)
  {
    object[39].Graphic == 1; //sprite no.1 is the bullet (1x1 transparency)
  }
  else
  {
    //do nothing
  }
}


You can use objects to do what you need, just make sure that the script will ever hit the limit

Ryan Timothy B

#4
Quote from: ProgZmax on Tue 03/11/2009 21:49:40
[..]use characters instead of objects since you can have an infinite number of them and they're room independent.[..]
There is also a 40 character limit per room.

But to get more than that, you'd have to use drawingsurfaces.  And for an arcade game, you're best off with using drawingsurfaces anyway.  That way you can go from room to room if you keep your scripts in globalscripts, without need of creating objects for each room or changing all the characters rooms.

Edit:  I've actually drawn 400 'characters' at once, and with each one animating, using drawing functions.  It barely shows any sign of lag (just don't animate every game loop (obviously), since a game loop is very quick, you'd have to have an int used as an animating timer for each 'character').

Arboris

#5
Ah, sorry, but I've tackled this one a little while back.  I did tell and thank ProgZmax for mentioning you can simply draw sprites on the background, but never here. Still, thanks for the input.

As I said: I'm drawing the bullets on the background now, and keep their respective x, y, and angle in an array and have one dedicated function which checks the array if something falls between the 320x240 dimensions and then draws it on the background.  And it also checks if you're pressing the left mouse button. This will put 'new' projectile coordinates in the array where ever the y coordinate has exceeded the 0 limit (top of the screen) and then 'breaks'out of this while loop, because it successfully registered in the array. With this I can create a ridiculous amount of bullets on screen without AGS slowing down it seems.

Code: ags
function MachineGunLaser(){//drawing it on the background instead of using objects now. int laserPosition[200]; is a global array.
  int x=0;
  if(Mouse.IsButtonDown(eMouseLeft)==1){
    PlaySound(1); //pewpew sound
    while(x<=199){//the array has I'm checking has 200 places. it was a random number without thought tbh.
      if(laserPosition[x+1]<0){//checking if the Y position of any other projectile is out of bounds
        laserPosition[x]=mouse.x+8+Random(18);//to get the gatling effect I put in a bit of random X position
        laserPosition[x+1]=185;//his Y start position
        laserPosition[x+2]=Random(2)-1;//random angle at which the bullet will fly
        x=199; //breakfunction
      }
      x=x+3;//every bullet takes up 3 places in the array. X, Y and Angle.
    }
  }
  x=0;
  background=Room.GetDrawingSurfaceForBackground(); //drawing all the bullets that are stored in the array and do not fall out of bounds
  while(x<=199){//again, checking the entire array
    if(laserPosition[x+1]>=0){//only draw the bullets that are on screen
      background.DrawImage(laserPosition[x], laserPosition[x+1], 64);
      laserPosition[x+1] = laserPosition[x+1]-5;//speed it flies upwards
      laserPosition[x] = laserPosition[x]+laserPosition[x+2]; //angle
    }
    x=x+3;
  }
  background.Release();
}


Here's the result. This is an old version btw, and hasn't implemented the angled shots yet.
 
Concept shooter. Demo version 1.05

Ryan Timothy B

I quickly scanned through your code and saw this comment:
Code: ags
x=x+3;//every bullet takes up 3 places in the array. X, Y and Angle.


To make things easier on yourself also with less chances of programming errors, would be to use a struct and array.
Like this:

Code: ags
struct sBullets {
  int x;
  int y;
  int a;  //angle
};
sBullets bullet[100];


Then you can call the variables like so:
Code: ags

bullet[i].x = mouse.x+8+Random(18);
bullet[i].y = 185;
bullet[i].a = Random(2)-1;

Then when you run a while statement you don't have to add 3, or whatever amount, each time.  Just one.

Scarab

Does your game shoot bullets only or can you shoot a lazer like in your sig and in RaidenX?

I tried to download your demo/game, but my screen resolution could not handle the resolution  :-[

peace
scar

Arboris

The concept demo link in my signature has upgradable weapons, and that laser is your temporary powerup weapon. Try running it in window, you can select it from the setup that comes with it.
 
Concept shooter. Demo version 1.05

Shane 'ProgZmax' Stevens

It reminds me very much of an old snes shooter!  Are you going to add enemies or is it just going to be like it is now, with an every increasing number of asteroids coming at you?  Some enemies might spice it up a bit, as well as the ability to move vertically (like up to about half the screen would be good for avoiding projectiles). 

Arboris

#10
I was planning on making it with enemy waves. At the moment I can send enemies forward in sequence rather then the randomness of the asteroids demo. The only problem I have with it is that the function that will handle these events will become a giant 'else if' statement.

Code: ags
function eventHandler(){//this is where every enemy encounter will come
  if(gameTimer==50){
    Zigzag(100, 40.0, 60.0, 2, 26, 1, -1, 10);
  }else if(gameTimer==250){
    Zigzag(40, 50.0, 25.0, 2, 26, 3, -1, 80);
    Zigzag(100, 8.0, 15.0, 2, 26, 2, -1, 50);
    Zigzag(200, 5.0, 5.0, 2, 26, 1, -1, 70);
  }else if(gameTimer==400){
    Zigzag(0, 200.0, 140.0, 1, 26, 1, -1, 10);
  }else if(gameTimer==450){
    Zigzag(220, 0.1, 0.1, 2, 2000, 1, -1, 80);
    Zigzag(80, 0.1, 0.1, 2, 2000, 1, -1, 80);
  }else if(gameTimer==460){
    Zigzag(210, 0.1, 0.1, 2, 2000, 1, -1, 80);
    Zigzag(90, 0.1, 0.1, 2, 2000, 1, -1, 80);
  }else if(gameTimer==470){
    Zigzag(200, 0.1, 0.1, 2, 2000, 1, -1, 80);
    Zigzag(100, 0.1, 0.1, 2, 2000, 1, -1, 80);
  }else if(gameTimer==480){
    Zigzag(190, 0.1, 0.1, 2, 2000, 1, -1, 80);
    Zigzag(110, 0.1, 0.1, 2, 2000, 1, -1, 80);
  }else if(gameTimer==650){
    gameTimer=0;
  }
  gameTimer++;
}


and this is just for a few encounters. So as you can see, with a medium sized level this thing would become ginormous
 
Concept shooter. Demo version 1.05

Helme

Just some feedback for your demo: I would prefer to use the arrow-keys instead of the mouse.

Arboris

#12
I'll be changing that soon. It was never intended to do all this, and I kinda got carried away with it (Dodging the asteroids was the main idea, without even the ability to fire). And since what I'm working on now requires you to move up and down as well, I'll just implement a keyboard control scheme.  But I'll be working on a redo of the spaceship sprite first.
 
Concept shooter. Demo version 1.05

Victor6

Quote from: Arboris on Sat 14/11/2009 11:15:15
I was planning on making it with enemy waves. At the moment I can send enemies forward in sequence rather then the randomness of the asteroids demo. The only problem I have with it is that the function that will handle these events will become a giant 'else if' statement.

I wrote a flash shooter once and encountered a similar coding issue. I got around it by keeping track of the current 'wave' of enemies, and storing the spawn data in a 2d array.

That way you only need to check the timer against the timer value for the next wave each time.

I believe AGS doesn't support 2d arrays, but you probably can use multiple arrays to simulate the same effect.

suicidal pencil

Quote from: Victor6 on Sat 14/11/2009 20:09:17
I believe AGS doesn't support 2d arrays, but you probably can use multiple arrays to simulate the same effect.

you can cheat using struct
Quote from: Khris on Sat 24/10/2009 10:40:04
Code: ags
// header

struct str_x {
  int y[100];
};

// script

str_x x[100];

// now one can do:
  x[40].y[20] = 3;


Arboris

#15
Well, I've been working on it a bit more, and I was afraid this would happen. It's pretty easy to get the game to crawling slowdown now. Of course at the moment you can spawn more then 40 bullets in one go, but even with half of that you'll notice a serious slowdown.

this is mainly because unlike enemy fire which only has to check hits vs your ship, every bullet you shoot has to be checked vs every visable object on screen. And I've done this with a loop within a loop approach.

test version 1.02

*EDIT*
Luckily the slowdown was caused by a simple oversight I made on when to calculate the hitboxes.
Test version 1.03
 
Concept shooter. Demo version 1.05

Ryan Timothy B

I noticed your enemy ships just Appear right on the top of the screen.  Why not make them appear above the edge of the screen and move down.  You can use drawimage even if the rectangle boundaries of that image is off the drawing surface (eg: y==-10).  
I've done it plenty of times without AGS giving any errors.  It must be calculated in the drawimage function within AGS.




Also one thing you're doing that seems a little backwards is how you determine which number the new bullet should spawn as.  Your code here:
Code: ags
while(x<=199){//the array has I'm checking has 200 places. it was a random number without thought tbh.


What I do to prevent having to run this while statement, is I have an integer of How many bullets are on the screen.  Whenever you want to add a bullet you just do this instead:

Code: ags

  if (Mouse.IsButtonDown(eMouseLeft)==1)
  {
    if (BulletCount<200) //checks to see if you aren't at max
    { 
      PlaySound(1); //pewpew sound
      bullet[BulletCount].x = mouse.x+8+Random(18);  //to get the gatling effect I put in a bit of random X position
      bullet[BulletCount].y = 185;  //his Y start position
      bullet[BulletCount].a = Random(2)-1;  //random angle at which the bullet will fly
      BulletCount++;  //you add the number After you assign it to the Array. That way 1 will be 0 on the array.
    }
  }


Then to see if a Bullet needs to be removed you'd have to run a while loop in the rep_ex (also in this while loop you should also cross check if the bullet in question is touching an enemy ship, that way you aren't wasting a while loop):

Code: ags

int i;
while [i<BulletCount] 
{
  if (bullet[i].y<0) //remove the bullet  OR  if this bullet has touched an enemy ship.
  {
    bullet[i].x=bullet[BulletCount].x;  //this replaces the current bullet with the Last bullet in the array.
    bullet[i].y=bullet[BulletCount].y;
    bullet[i].a=bullet[BulletCount].a;
    BulletCount--;
    i--; //so this number gets ran again
  }
  i++;
}


I wrote this all in here, so I hope there aren't any issues.
This is how I do it, just to prevent any unnecessary while loops, and to maximize cpu efficiency.

Khris

Great stuff!

Small suggestion: set mouse bounds & add esc = quit. I understand the game is in early alpha, but it makes testing a lot more convenient.

Arboris

#18
Quote(also in this while loop you should also cross check if the bullet in question is touching an enemy ship, that way you aren't wasting a while loop)

Good idea. I'll move the hitdetection the moment it also actually draws. It'll safe some while loops. Altho it'll be less readable then one my current single hitdetection function. But I'm the only one looking at it anyway ;)

QuoteI noticed your enemy ships just Appear right on the top of the screen.

This is because of the 'odd' difference between object and DrawImage. for an object the Y coordinate is the bottom left corner, and for DrawImage it's the upper left corner. It's a minor tweak i still need to do ;)
 
Concept shooter. Demo version 1.05

Radiant

Quote from: Arboris on Tue 03/11/2009 20:51:04
Hey, I've been busy with a simple shooter concept as of late and have a question about the maximum number of objects one can use on a single screen. Well, I've run into this problem fairly quickly since every bullet I use on screen has to be it's own object.

I recommend rawdraws.

SMF spam blocked by CleanTalk