Adventure Game Studio

AGS Support => Advanced Technical Forum => Topic started by: abstauber on Tue 27/04/2010 20:08:46

Title: Design choice to render bullets?
Post by: abstauber on Tue 27/04/2010 20:08:46
Okay, this might be a bit of a stupid question, but it's bothering me since AeroNuts.

In AeroNuts I had two GUIs and each had 30 transparent buttons. In my bullet struct I only had a reference to the mapped button, because this button has all the cool attributes, like x,y, Graphic and so on. It can even be animated and or checked if it's animating.

The only drawback I know about is the 30 Button limit per GUI limit, but I think that's sufficient for a low res game (30 bullets for the player, 30 bullets for everyone else)
The other possibility would be to include those attributes in the bullet struct, do the animation handling myself and draw them on a GUIs background surface.

I wonder which one's faster and/or the better solution. Do you have any hints?  :)


Thanks!
Title: Re: Design choice to render bullets?
Post by: GarageGothic on Tue 27/04/2010 20:43:10
Yeah, just use DrawingSurfaces. They work fine to particle systems up to 150-200 particles, so bullets should be no problem. I'm sure you're doing most stuff (collision detection etc.) in script already, so why restrict yourself by using GUI elements?
Title: Re: Design choice to render bullets?
Post by: abstauber on Tue 27/04/2010 20:49:10
Hmm... I could imagine the internal animate command is faster than my scripted method.
That's about my biggest concern, pixel perfection isn't possible with both methods, but I refuse to use room objects :)

edit
QuoteThey work fine to particle systems up to 150-200 particles
Argh, I've read to fast. Of course I could beef up Jarakeen's particle module some more and simply use that. Thanks for that hint! :D
Title: Re: Design choice to render bullets?
Post by: Ryan Timothy B on Tue 27/04/2010 21:06:47
Quotepixel perfection isn't possible with both methods
Not unless you write your own pixel perfect collision scripts.  But using Game.SpriteWidth or Game.SpriteHeight can get the rectangle boundaries.

I definitely agree with drawingsurface.  It's not all that hard to animate something by using drawingsurfaces.  You simply just need to have a counter per 'object' that once it hits the certain count it changes to the next frame.  If it's on the last frame of the loop, it starts back at the first frame - if it's supposed to repeat.
Title: Re: Design choice to render bullets?
Post by: GarageGothic on Tue 27/04/2010 21:13:33
I would even call a particle system overkill - unless you're doing some really funky bullet trajectories, most of what you need could be handled with simple integer maths even for angled shots, and there's certainly no need to involve force vectors like gravity.

Most likely what you're doing at the moment would still work fine. Simply draw the bullet at its coordinate instead of moving the button there.

Also, using DrawingSurfaces and the GetPixel command, you *could* do pixel perfect detection drawing the enemy planes first, then checking the bullet coordinate on the DrawingSurface to see if it's transparent or not, and if it isn't, run your normal rectangular collision detection to see what enemy is at that spot.
Title: Re: Design choice to render bullets?
Post by: Calin Leafshade on Tue 27/04/2010 21:18:43
My platformer engine does actually have some drawing surface bullet code in it.. since mario could throw knives!
Title: Re: Design choice to render bullets?
Post by: abstauber on Wed 28/04/2010 07:51:42
Yeah, after thinking this particle stuff through again - yep way to much overhead and I'm sure I won't need gravity.

As for pixel perfection: isn't GetPixel one of the slowest commands, AGS has to offer? I use it in a dissolve module and I'd rather stay away from it when it comes to real time action.
If course this issue can be solved by using circular bullets :)

Anyway thanks for your input. I guess, you've convinced me to stick to drawing surfaces. (aww.. no copy and paste from Aeronuts)

@Calin
I know, I've read your code so many times :)



Title: Re: Design choice to render bullets?
Post by: Calin Leafshade on Wed 28/04/2010 08:07:17
Quote from: abstauber on Wed 28/04/2010 07:51:42
@Calin
I know, I've read your code so many times :)

That's depressing.  :P

Messiest code ever.
Title: Re: Design choice to render bullets?
Post by: abstauber on Wed 28/04/2010 08:10:28
Pah, don't be shy. It's still readable and the concepts are cool no matter what.
Title: Re: Design choice to render bullets?
Post by: GarageGothic on Wed 28/04/2010 12:42:49
Quote from: abstauber on Wed 28/04/2010 07:51:42As for pixel perfection: isn't GetPixel one of the slowest commands, AGS has to offer? I use it in a dissolve module and I'd rather stay away from it when it comes to real time action.

Yes, it is indeed quite slow. I've been using it for some experiments with realtime lighting of bumpmapped 2D sprites, and sampling more than a couple of hundred pixels per loop does cause a framerate hit even on a fast CPU. However, if you use it after a rectangular collision check has returned true (never mind what I said before about doing this in reverse order) you could probably keep it under 10 bullets to check for per loop. Other optimizations include checking bullets closest to the enemy first, so you don't waste CPU cycles on checking collisions for an enemy that was already destroyed.

Quote from: Calin Leafshade on Tue 27/04/2010 21:18:43since mario could throw knives!

...and still some people claim that games have gotten more violent.  ;)

Title: Re: Design choice to render bullets?
Post by: Calin Leafshade on Wed 28/04/2010 12:49:54
This is why CJ should make the pixel shader interface available to devs. The engine already has pixel shading it seems silly to keep it hidden.

imagine bloom in 320x200.. it would be a thing of beauty.
Title: Re: Design choice to render bullets?
Post by: GarageGothic on Wed 28/04/2010 12:57:49
Quote from: Calin Leafshade on Wed 28/04/2010 12:49:54imagine bloom in 320x200.. it would be a thing of beauty.

Amen, brother. And that's why CJ, our almighty but merciful lord and savior, gave us the plugin API :)
(and why I, his humble disciple, will be spending my summer writing a really neat rendering plugin)
Title: Re: Design choice to render bullets?
Post by: Calin Leafshade on Wed 28/04/2010 13:00:06
if you give me pixel shaders I shall give you my devotion
Title: Re: Design choice to render bullets?
Post by: abstauber on Wed 28/04/2010 13:10:12
err... yes ;D
(and keep in mind that a lot of mac and linux users have now tears in their eyes :P )

Quote
Other optimizations include checking bullets closest to the enemy first, so you don't waste CPU cycles on checking collisions for an enemy that was already destroyed.

I'm already doing the fast box collision check - but how to manage this one? And since the bullets need to be aware of solid walls, I'll have to check each bullet anyway, right?
Title: Re: Design choice to render bullets?
Post by: GarageGothic on Wed 28/04/2010 13:17:48
Well, I don't know the scope of what you're trying to implement - I imagined just a more a sophisticated version of AeroNuts, so I was thinking iterating through the bullet array back-to-front (based on an assumption that bullets fired first would be closer to your target than those fired later - which may still hold true).

How are the solid walls implemented?

Quote from: Calin Leafshade on Wed 28/04/2010 13:00:06if you give me pixel shaders I shall give you my devotion

cash would be better  ;D
Title: Re: Design choice to render bullets?
Post by: abstauber on Wed 28/04/2010 13:29:49
Ah, now I see. That bullet array is already being iterated back to front. :) But it's also possible, that those bullets don't line up in order since the 30 button limit is pretty tight.

As for those walls: It's a tile based map and each tile has a flag whether it's solid or not. My plan is to check the space in front of the bullet:

if it's a solid tile,  remove the bullet
if it's an enemy, do a collision check, if collision add damage and  remove the bullet
if it's the screen border...remove the bullet.
Title: Re: Design choice to render bullets?
Post by: Monsieur OUXX on Wed 28/04/2010 13:36:43
Quote from: abstauber on Wed 28/04/2010 13:10:12
(and keep in mind that a lot of mac and linux users have now tears in their eyes :P )

...And this is why a primitive rendering library will be inside AGSHack :-)
Title: Re: Design choice to render bullets?
Post by: GarageGothic on Wed 28/04/2010 13:56:04
Ah, tile based, excellent. I don't think there's much point in struggling with further optimization since tiles make the rectangular collision check so very basic - and 30 bullets is hardly anything at all. The technique you're using should still work fine.

Quote from: abstauber on Wed 28/04/2010 13:10:12(and keep in mind that a lot of mac and linux users have now tears in their eyes :P )

... and that'll teach those whiny bastards not to expect compatibility standards from indie developers that commercial games haven't offered in decades.  ;)
Title: Re: Design choice to render bullets?
Post by: abstauber on Thu 29/04/2010 08:16:08
Quote from: GarageGothic on Tue 27/04/2010 21:13:33
most of what you need could be handled with simple integer maths even for angled shots

*cough*

I know how to move the bullets in a 45° angle, so 8 way shooting is easy. But for those other angles, is there any smart way to avoid calculating triangles and such?

For 45° angles I simply give the bullet the same vertical speed as horizontal speed. I know I should work with inclination, but I'm afraid I overslept that in school too :)
Title: Re: Design choice to render bullets?
Post by: Calin Leafshade on Thu 29/04/2010 08:32:25
look at the AGSeggle code in the tech forum.. that has pretty extensive code on calculating vectors and stuff.
Title: Re: Design choice to render bullets?
Post by: abstauber on Thu 29/04/2010 08:34:47
Who would I be without your open source attitude...

Not the copycat I am now ;D
Title: Re: Design choice to render bullets?
Post by: Shane 'ProgZmax' Stevens on Thu 29/04/2010 08:48:10
I find it interesting how you used guis to do what most people would use dynamic surfaces for, and I mean that in a good way.  I never would have thought of messing about with a gui for a shooter, but you seem to have the logic down for it well so I'd say stick to it if it works.  The only thing that confuses me is what you said about making the bullets part of the gui.  That would then make them buttons with animated sprites, I gather?  How can you make them leave the gui limits and continue to a target, or are the hero and enemy guis just full screen overlapping at all times (that sounds a bit sloppy to me)?  I like the concept of using a gui for a big boss ship with things to destroy (buttons), but I definitely think there are advantages to using dynamic sprites for, say, bullets and pickups and minor enemies.  With aeronuts, were the enemies just buttons floating on a huge gui?
Title: Re: Design choice to render bullets?
Post by: abstauber on Thu 29/04/2010 09:09:50
Yes - I simply used GUIs as Layers. 2 Bullet layers, and one enemy layer. The GUIs are full screen and you're right - all that's moving are floating buttons :)

Well, this GUI button concept in AeroNuts worked great before I realised that pixel perfection is not possible with GUI buttons. So I had to convert the player's plane to a character and that resulted in a nasty z-level problem since GUI appear in front of characters. It ended in converting the carrier into a room object, but the turrets are still GUI buttons.
At that stage I decided it's better not to release the source anymore ;)

But if you like, I can upload the source somewhere (be warned, it's MAGS code).
Title: Re: Design choice to render bullets?
Post by: Gilbert on Thu 29/04/2010 09:39:44
It's actually quite straight forward to code bullets moving in any direction. You may check this quick mess (http://files.myopera.com/gilbot/AGSStuff/TESTBULLETS.zip) that I made in a couple of minutes (sorry, no module or comments present, check script in room1 and compile with V3.1.2+). In fact the bullet layer DynamicSprite can even be used as a collision map! (If bullets are not in single pixels and you want the collision maps to be in single pixels just paint on another sprite at the same time to create the collision map.)
Title: Re: Design choice to render bullets?
Post by: abstauber on Thu 29/04/2010 10:23:07
Hehe, that totally reminds me at the early demo scene :D

Thanks for the help!
Title: Re: Design choice to render bullets?
Post by: Wyz on Thu 29/04/2010 15:43:46
A minor optimization when you end up firing bullets in the same direction often: Use unit vectors to do the calculations. In that way you can just multiply the vector by any distance.


int sourceX, sourceY; // player position
int destX, destY;        // mouse pointer or something

float vx = IntToFloat(destX - sourceX);
float vy = IntToFloat(destY - sourceY);
float n = Maths.Sqrt(vx*vx + vy*vy);

vx /= n;
vy /= n;

now you can move a bullet like this:
float distance = ...; // Distance the bullet has traveled in pixels

int bulletX = sourceX + FloatToInt(vx * distance);
int bulletY = sourceY + FloatToInt(vy * distance);



Yay math! :D
Title: Re: Design choice to render bullets?
Post by: Khris on Thu 29/04/2010 17:00:04
Actually one would store a movement vector (directional vector * initial speed) for each bullet and add that to the bullet's position each frame. It's faster (additions only) and the speed can directly be modified my changing the movement vector. One can also incorporate reflections this way.

Also, one should use only floats until the object is drawn.
Title: Re: Design choice to render bullets?
Post by: abstauber on Thu 29/04/2010 17:31:14

One could also post a code sample :P


@Wyz: Yay :D
Title: Re: Design choice to render bullets?
Post by: Calin Leafshade on Thu 29/04/2010 17:43:25
Basically a unit vector is simply a measurement of direction. It has no speed value associated with it.

so (1,1) is 45 degrees up-right, (-1,1) is 45 degrees up left (using Cartesian cords)

Wyz suggests storing this vector and then multiplying it by a scalar (just a number) speed.

so you'd have something like:



vector.x = 1;
vector.y = 1;
scalarspeed = 8;

bullet.x += vector.x * scalarspeed; // i.e 1 * 8 = 8;
bullet.y += vector.y * scalarspeed;



Khris suggests just save the vector as a product of the unit vector and the scalar speed so you dont have to do it every loop.. thus less math.



Title: Re: Design choice to render bullets?
Post by: Khris on Thu 29/04/2010 18:57:17
Actually, the length of (1,1) is Sqrt(2) ~ 1.414. A unit vector pointing up-right would be (Sqrt(2)/2, -Sqrt(2)/2).

In actual code one would use a vector class.

float vector_class::Length() {
  return Maths.Sqrt(this.x*this.x + this.y*this.y);
}

void vector_class::Normalize() {
  float l = this.Length();
  if (l == 1.0 || l == 0.0) return;
  this.x = this.x/l;
  this.y = this.y/l;
}


Now I can do this:

vector_class v;

// inside function

  v.x = IntToFloat(mouse.x - player.x);
  v.y = IntToFloat(mouse.y - player.y);

  v.Normalize();
  v.x = v.x*speed;
  v.y = v.y*speed;
Title: Re: Design choice to render bullets?
Post by: DoorKnobHandle on Thu 29/04/2010 19:06:14
An even faster way to normalize a vector would be this:


void Vector::Normalize ( )
{
   float l = this.Length ( );

   if ( l == 1.0 || l == 0.0 )
      return;

   float tmp = 1.0 / l;

   this.x = this.x * tmp;
   this.y = this.y * tmp;
}


This substitutes one additional division by two multiplications which is worth it speed-wise.

:D

Smiley is there because, unless you are doing something very fancy, you're not going to notice a difference. Especially in AGS.
Title: Re: Design choice to render bullets?
Post by: GarageGothic on Thu 29/04/2010 19:16:00
And the float tmp does what exactly in that piece of code, dkh?
Title: Re: Design choice to render bullets?
Post by: DoorKnobHandle on Thu 29/04/2010 19:20:31
EDIT: OOps. Typo fixed in my function above.
Title: Re: Design choice to render bullets?
Post by: abstauber on Fri 30/04/2010 09:42:52
haha.. okay I think I really have a lot of choices now :D

Thanks a bunch, guys!
Title: Re: Design choice to render bullets?
Post by: Monsieur OUXX on Fri 30/04/2010 10:10:41
I'd recomand to work as much as possible with integers. That's a miracle solution for all time-consuming tasks.
That's how they made Doom so incredibly fast and good-looking in the first place, compared to Wolfenstein.

Integer arithmetics can of course not replace float arithmetics completely, but it should be used strictly every time you can exclude scenarios from your code - scenarios that would require you to calculate things repeatedly and with high accuracy.

Exclude scenarios. All the time. In every circumstances. Make your code straight-forward. Never mind if the result lacks accuracy : if it looks like it's correct, then for the player it's correct.

Example 1: As it has been mentionned before, calculating a displacement vector (using floats, if necessary) once and for all (at the time you set the direction of a moving object - i.e. when you create it or when it rebounds), and then always refer to that constant vector, expressed in integer pixels offest (e.g. "move 3 pixels to the right" - not 2.5, not 3.5, not 3.0, but strictly 3).

Example 2: pixel-perfect collision detection is very rarely needed. Use a box or a circle or a "vertical lozenge". Even for bullets.
Title: Re: Design choice to render bullets?
Post by: abstauber on Fri 30/04/2010 13:47:10
Okay, you may now start laughing. Even with all this helpI can't get it to work  :'(

Right now bullets can fly to 0° ,45° to the left  90° left and get stuck but render at 90° to the right.

I knew it wasn't just simply copy'n paste .

Anyhow here's the code.


//here's the bullet struct
struct cBullets {
  int x;
  int y;
  bool active;
 
  float vector_x;
  float vector_y;
  int speed;
  int gravity;
  int sprite;

  import float Length();
  import void Normalize();
};


float cBullets::Length() {
  return Maths.Sqrt(this.vector_x * this.vector_x + this.vector_y * this.vector_y);
}

void cBullets::Normalize ()
{
   float l = this.Length ();

   if ( l == 1.0 || l == 0.0 )
      return;

   float tmp = 1.0 / l;
   this.vector_x = this.vector_x * tmp;
   this.vector_y = this.vector_y * tmp;
}

// here's shooting
// TENG.get_free_bullet works fine btw.

static function TENG::shoot_player_bullet()
{
  int xpos;
  int ypos;
  int modifier;
 
  int nextFree;
 
  if (weapon[ego_stats.active_weapon].bullet_sprite >-1) {
   
    nextFree = TENG.get_free_bullet (true);
   
    if (nextFree > -1) {
     
      if (ego_stats.SpriteDirection == 4) {
        xpos = player.x + (TENG.get_char_width(player)/2);
        modifier = 1;
      }
      else {
        xpos = player.x - (TENG.get_char_width(player)/2);
        modifier = -1;       
      }
      ypos = player.y - (TENG.get_char_height(player)/2);
     
      player_bullet[nextFree].x = xpos;
      player_bullet[nextFree].y = ypos;
      player_bullet[nextFree].speed = weapon[ego_stats.active_weapon].speed * modifier ;
     
      player_bullet[nextFree].sprite = weapon[ego_stats.active_weapon].bullet_sprite;
      player_bullet[nextFree].active = true;
     
      if (weapon[ego_stats.active_weapon].type == eWT_Freeway) {
        player_bullet[nextFree].vector_x = IntToFloat(mouse.x - player.x);
        player_bullet[nextFree].vector_y = IntToFloat(mouse.y - player.y);
        player_bullet[nextFree].Normalize();
       
      }

    }
  }
}


// And here's the drawing, called by rep_exec
static function TENG::draw_bullets()
{

  DrawingSurface *surface = Room.GetDrawingSurfaceForBackground();
 
  int counter = 0;
 
  // draw player_bullets
  while (counter < MAX_BULLETS) {
    if (player_bullet[counter].active) {
      player_bullet[counter].x += FloatToInt(player_bullet[counter].vector_x) * weapon[ego_stats.active_weapon].speed;
      player_bullet[counter].y += FloatToInt(player_bullet[counter].vector_y) * weapon[ego_stats.active_weapon].speed;

      surface.DrawImage(player_bullet[counter].x, player_bullet[counter].y, player_bullet[counter].sprite);
    }
    counter ++;
  }
 
  surface.Release();
 
}



"Argh" says

n00bstauber


@Monsieur OUXX
As soon as this works I'll store the vector as an integer ;)
Title: Re: Design choice to render bullets?
Post by: Monsieur OUXX on Fri 30/04/2010 13:52:27
Quote from: abstauber on Fri 30/04/2010 13:47:10
Okay, you may now start laughing. Even with all this helpI can't get it to work  :'(

Yeah but you didn't say what actually doesn't work...
Title: Re: Design choice to render bullets?
Post by: abstauber on Fri 30/04/2010 13:54:18
Quote
Right now bullets can fly to 0° ,45° to the left  90° left and get stuck but render at 90° to the right.

That's about what's not working ;) Those bullets should fly in all directions.
Title: Re: Design choice to render bullets?
Post by: Kweepa on Fri 30/04/2010 14:36:22
The problem is you're storing the x and y positions of the bullets as ints, which loses all the precision of the bullet direction. You should store those (bullet.x and bullet.y) as floats, and only convert to int temporarily to draw the bullet.

EDIT: Or never, if you use the DrawAntialiased module! [/shameless plug off]
Title: Re: Design choice to render bullets?
Post by: abstauber on Fri 30/04/2010 14:45:38
Oh dear, thanks Steve!

Next time I'll only listen to Monsieur OUXX, when I understand what I write.  :-[

Title: Re: Design choice to render bullets?
Post by: Monsieur OUXX on Fri 30/04/2010 15:03:33
Quote from: abstauber on Fri 30/04/2010 14:45:38
Next time I'll only listen to Monsieur OUXX, when I understand what I write.  :-[

Yeah, if you wanted to use integers all the way you'd have needed to have calculated "speedx" and "speedy" from the very beginning (using floats), and stored their approximate values as integers. Then, when you move your sprite, you'd just need to add speedx to x and speedy to y.
You can't mix integers and floats (or you have to be careful), because if you do it too soon or too late, it screws the accuracy of the results and produces random values.

But still, your code is good! Nice script.
Damn, I'm dying to write fun things like that, when instead I've broken the "interval red-black binary trees" of AGSH again :-/