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!
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?
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
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.
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.
My platformer engine does actually have some drawing surface bullet code in it.. since mario could throw knives!
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 :)
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.
Pah, don't be shy. It's still readable and the concepts are cool no matter what.
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. ;)
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.
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)
if you give me pixel shaders I shall give you my devotion
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?
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
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.
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 :-)
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. ;)
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 :)
look at the AGSeggle code in the tech forum.. that has pretty extensive code on calculating vectors and stuff.
Who would I be without your open source attitude...
Not the copycat I am now ;D
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?
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).
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.)
Hehe, that totally reminds me at the early demo scene :D
Thanks for the help!
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
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.
One could also post a code sample :P
@Wyz: Yay :D
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.
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;
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.
And the float tmp does what exactly in that piece of code, dkh?
EDIT: OOps. Typo fixed in my function above.
haha.. okay I think I really have a lot of choices now :D
Thanks a bunch, guys!
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.
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 ;)
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...
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.
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]
Oh dear, thanks Steve!
Next time I'll only listen to Monsieur OUXX, when I understand what I write. :-[
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 :-/