Jump to content

  • Log In with Google      Sign In   
  • Create Account


Trying to simulate the flamethrower from GTA2


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
13 replies to this topic

#1 ShaPe   Members   -  Reputation: 107

Like
0Likes
Like

Posted 12 October 2013 - 11:14 AM

Hello everyone,

I have got a simple game running on android - I have a character drawn in Paint.NET and some zombies as well. Here is a screenshot:
9gj5GUM.png

 

As you can see, there are two "thumb pads" which I use to control the character - left one is for moving and the right one is for rotating and shooting.

 

So far, I have implemented only a gun for the player to shoot with. However, I'd also like to add a flamethrower. I remembered that the flamethrower in GTA2 looked fluid and decided to try and copy it. For those of you who do not know how the flamethrower in GTA2 works here is a 33 second video video:

 

 

I really like the fluidity of the flames and how they bend, for example at 0:27 when the player starts rotating.

 

My initial idea was to create a simple fire sprite (16x16) which I spawn from the top of the gun of the player and move in the appropriate direction. However, I could not achieve the fluidity I wanted.

 

This leads to my question - does someone know how I can replicate the flamethrower from GTA2? What information I would need to store to be able to do all the calculations, etc.


Edited by ShaPe, 12 October 2013 - 11:15 AM.


Sponsor:

#2 SillyCow   Members   -  Reputation: 849

Like
0Likes
Like

Posted 12 October 2013 - 12:33 PM

I am not sure what is the problem with the flame you implemented, you should post a picture...

I will assume you got a straight line, even when the player turns.

If that is the case, then what you need to do is:

Your flame thrower should be a queue of flames.

Every frame, create a new flame, and delete the ones at the end of the queue (the oldest flames).

When you create a new flame, store that flame's angle.

That way, if you are turning, each flame should be at a slightly different direction.

 

Also, you might want to draw the flames differently, depending on where they are in the queue. The "older" flames should be bigger, because fireballs expand over time.


My new android game : Enemies of the Crown

My previous android game : Killer Bees


#3 ShaPe   Members   -  Reputation: 107

Like
0Likes
Like

Posted 12 October 2013 - 12:46 PM

The way I have implemented the flamethrower is this:

Every update I add a new flame to the end of the player's gun. I use an ArrayList to store the flames. Every flame has an angle (the same one as the player) and a direction (going away from the player). When the flame has traveled a set distance, I remove it from the array list. Here is a screenshot:

XhkmKQR.png

Also, the flame sprite:

yhtGIRD.png


I like the queue idea. However, what I'm really aiming at is to achieve the "bending" that the flames from gta2 do when the player rotates (seen at 00:27 in the video) I have no idea how I have to manipulate the flames to get such an effect.


Edited by ShaPe, 12 October 2013 - 12:52 PM.


#4 SillyCow   Members   -  Reputation: 849

Like
1Likes
Like

Posted 12 October 2013 - 01:30 PM

Do you mean that the near part of the flame in GTA is straighter, then the far part of the flame?

 

If so, make your flame decelerate as it get farther from your gun.

 

I will assume you are currently multiplying your flame's direction by the time elapsed.

Change it so that the more time elapsed, the farther & slower your flame should be.

 

instead of multiplying by time, multiply by some root of time.

 

Instead of: 

Position = FlameDirection * Time

Do either:

Position = FlameDirection * sqrt(Time)

or:

Position = FlameDirection * Math.pow( Time , float_number_smaller_than_one)

The smaller float_number_smaller_than_one is, the more "bending" you will have.


Edited by SillyCow, 12 October 2013 - 01:33 PM.

My new android game : Enemies of the Crown

My previous android game : Killer Bees


#5 ShaPe   Members   -  Reputation: 107

Like
0Likes
Like

Posted 12 October 2013 - 01:43 PM

What I mean is that in GTA2 when the player is stationary the endpoint of the flame is a given distance away from the player. However, when the player starts rotating, the endpoint of the flame is closer to the player. After the player stops rotating, the flame "straightens".

The way I'm calculating the positions of each flame object is by multiplying the speed of the flame by the sin() and cos() of the angle it is moving in and adding the result to the Y and X coordinates respectively.



#6 SillyCow   Members   -  Reputation: 849

Like
0Likes
Like

Posted 12 October 2013 - 02:03 PM

1. Their player is walking as he is spinning. Is your player advancing while spinning? 

 

2. In the GTA demo video, the player is not the origin of the flame. The end of the gun is the origin of the flame. If you look at your example, and move your player "back" a little bit you will see that the player get's closer to rotated flames.

 

3. You are not multiplying by speed. You are multiplying by speed * time. If you were multiplying by speed, your flames would not move (Speed is constant).

 

So, for simplicty: let's take just the Cos component of your flame. The same will work for cos. (This would be easier if you publish some code).

 

I assume what you are doing now is:

FlameX = FlameOriginX + Cos( FlameDirection ) * Speed * FlameTime

Where FlameTime is how long ago this flame was created, or it's index in the array, or whatever you use to measure when this particular flame should die.

 

You need to do two things:

 

1. Make the origin far away from your player:

FlameX = FlameOriginX + Cos( FlameDirection ) * LengthOfGun + Cos( FlameDirection ) * Speed *FlameTime

2. Then add deceleration:

FlameX = FlameOriginX + Cos( FlameDirection ) * LengthOfGun + Cos( FlameDirection ) * Speed * Sqrt(FlameTime)

Do the same for Y and Sin.

 

EDIT: You can simplify algebrically a little bit:

FlameX = FlameOriginX + Cos( FlameDirection ) * (  LengthOfGun + Speed * Sqrt(FlameTime) )

Edited by SillyCow, 12 October 2013 - 02:06 PM.

My new android game : Enemies of the Crown

My previous android game : Killer Bees


#7 ShaPe   Members   -  Reputation: 107

Like
0Likes
Like

Posted 12 October 2013 - 02:20 PM

I have downloaded GTA2 specifically to test this. If the player is not moving, only rotating, the endpoint of the flame gets closer to the player. So, whether the player is moving in any direction is not significant.

As for the formulas you posted - I do not really understand the need to multiply by the time.

Let's look at the X coordinate of a flame that is moving at an angle of 60 degrees with a speed of 5. The X coordinate should be increased by the cos(angle) * 5 every frame, or in other words the X coordinate should be increased by 5 / 2 pixels each update.

The Y coordinate should be increased by sin(angle) * 5 or by 5 * sqrt(3) / 2 each frame.

When you look at the increase in X and Y, the overall increase in distance is:
sqrt(25/4 + 75/4) = 10/2 = 5

EDIT: After looking at what you posted, it is the same thing. However, you increase the X coordinate with cos(angle) * speed every millisecond (for example) why I do once every 17ms (60fps)


Edited by ShaPe, 12 October 2013 - 02:26 PM.


#8 SillyCow   Members   -  Reputation: 849

Like
0Likes
Like

Posted 12 October 2013 - 03:02 PM

I see what you are doing here...

In my example I do not change the coordinate of the flames,

I calculate them from scratch every frame.

You are using a discrete system, while I am using a continuous one.

 

It doesn't matter though, using your discrete system we can achieve the same result:

 

1. Set the initial coordinate for the flame to be farther away from your "man". It should be exactly "gun-length" far from your man in the direction it is pointing.

 

2. To achieve deceleration:

For every frame decrease the speed of your particle.

You don't store speed for every particle?

Have no fear, you can extrapolate the speed by how much time your particle has existed: How long does it have until it needs to be destroyed? That is your time.

The example you posted is bad. When you are saying: "The Y coordinate should be increased by 'constant' " you are note changing your speed, and therefore not decelerating.

 

PS: Time is not 1ms, it is in whatever unit you decide to measure it. In most of my games, I measure time in logic-frames which are 20ms (50fps). 


My new android game : Enemies of the Crown

My previous android game : Killer Bees


#9 WiredCat   Members   -  Reputation: 257

Like
0Likes
Like

Posted 13 October 2013 - 03:42 AM

//---------------------------------------------------------------------------


#pragma hdrstop

#include "WEAPON_FLAMETHROWER.h"
//#include "FC_OPENGL.h"

//---------------------------------------------------------------------------

#pragma package(smart_init)

void __fastcall  drawbillboardF(float cx, float cy, float cz, float size)
{
double m_PickInfo_ModelView[16];// of ;

  t3dpoint Pos;
glGetDoublev(GL_MODELVIEW_MATRIX, m_PickInfo_ModelView);

BillboardX.x = m_PickInfo_ModelView[0];
BillboardX.y = m_PickInfo_ModelView[4];
BillboardX.z = m_PickInfo_ModelView[8];


BillboardY.x =	m_PickInfo_ModelView[1];
BillboardY.y =	m_PickInfo_ModelView[5];
BillboardY.z =	m_PickInfo_ModelView[9];



glBegin(GL_QUADS);

glTexCoord2f(0.0, 1.0);

Pos.x = cx-BillboardX.x*size+BillboardY.x*size;
Pos.y = cy-BillboardX.y*size+BillboardY.y*size;
Pos.z = cz-BillboardX.z*size+BillboardY.z*size;

glVertex3f(Pos.x,Pos.y,Pos.z);
glTexCoord2f(1.0, 1.0);

Pos.x = cx+BillboardX.x*size+BillboardY.x*size;
Pos.y = cy+BillboardX.y*size+BillboardY.y*size;
Pos.z = cz+BillboardX.z*size+BillboardY.z*size;

glVertex3f(Pos.x,Pos.y,Pos.z);

glTexCoord2f(1.0, 0.0);
Pos.x = cx+BillboardX.x*size-BillboardY.x*size;
Pos.y = cy+BillboardX.y*size-BillboardY.y*size;
Pos.z = cz+BillboardX.z*size-BillboardY.z*size;
glVertex3f(Pos.x,Pos.y,Pos.z);

glTexCoord2f(0.0, 0.0);
Pos.x = cx-BillboardX.x*size-BillboardY.x*size;
Pos.y = cy-BillboardX.y*size-BillboardY.y*size;
Pos.z = cz-BillboardX.z*size-BillboardY.z*size;
glVertex3f(Pos.x,Pos.y,Pos.z);

glEnd();

}



void __fastcall  tflamethrower::DRAW()
{
int i;
float d; t3dpoint v;


make_next_frame();
//glpushmatrix();
glColor4f(1,1,1,0.7);
glDepthMask(GL_FALSE);

glEnable(GL_BLEND);
//
glBlendFunc(GL_ONE,GL_ONE);
//glEnable(GL_TEXTURE_2D);
for (i = 0; i < 129; i++)
if (particles[i].draw == true)
{
//	                                                                 glbindtexture(GL_TEXTURE_2D,sprite.animtextures[particles[i].frame]);


// particles[i].size = 300.0f;
drawbillboardF(particles[i].pos.x,particles[i].pos.y,particles[i].pos.z,particles[i].size);
 //glrotate(-angle,1,0,0);
//  glmatrixmode(GL_TEXTURE);glrotate(-angle,0,1,0); glmatrixmode(GL_MODELVIEW);
//glpopmatrix();

}


//glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
}


  __fastcall tflamethrower::tflamethrower()
  {
   sprite = new tglanimtexture;
  }
  __fastcall tflamethrower::~tflamethrower()
  {
   sprite->free();
   delete sprite;
  }

void __fastcall  tflamethrower::load(AnsiString filename)
{
 sprite->loadtex(filename);
}


void __fastcall  tflamethrower::make_next_frame()
{
try {
int i;
t3dpoint k;

for (i = 0; i < 129; i++)
if (particles[i].draw == true)
{

k.x = 100000000.0f*(sin(particles[i].glop*imopi)*cos(particles[i].heading*imopi));
k.z = 100000000.0f*(cos(particles[i].glop*imopi)*cos(particles[i].heading*imopi));
k.y = 100000000.0f*(sin(particles[i].heading*imopi));
 particles[i].size        = (float(GetTickCount())-particles[i].time)/100.0f;
  k=Normalize(k);


   if (NORMAL_TIME == 0) {        //czyli jak false
	   particles[i].pos = vectors_frontadd(particles[i].startpos,vector_multiple(k,power*(GetTickCount()-particles[i].time)/1000.0f));
   } else {

 particles[i].pos = vectors_frontadd(particles[i].pos,vector_multiple(k,2.00*power*(GetTickCount()-particles[i].time)/1000.0f)); //end else
		   }
particles[i].time = GetTickCount();
//					 particles[i].rotate = particles[i].rotate +1;
/*if (GetTickCount()-particles[i].frametime >= sprite.speed ) {
	  particles[i].frametime = GetTickCount();

//particles[i].frame = particles[i].frame + 1;       odkomentowac
//if (particles[i].frame > high(sprite.animtextures)  )then begin particles[i].frame := 0; particles[i].draw := false; end; odkomenotwac

}

  */

checkparticle(i);
}

	 } catch (...) {

	 }
}
                                     //tutaj jak wielkosc przekracza maks wielkosc to usuwamy z rysowania particle
void __fastcall tflamethrower::checkparticle(int index)
{
//if ( particles[index].size >=  5000.0f ) particles[index].draw = false;

if (n3ddistance(particles[index].startpos,particles[index].pos) > 1000.0f)       
                               particles[index].draw = false;

}

void __fastcall tflamethrower::throw_next_free(t3dpoint pos, float  glop, float heading)
{
int i;
bool found;

if ( (GetTickCount()-shot_time) < SHOT_SPEED ) return;
shot_time = GetTickCount();
already_shot = true;
found = false;
for (i = 0; i < 129; i++)
if (particles[i].draw == false)
{
found = true;
last_fired_particle = i; break;
}

if (found == false){ last_fired_particle = 0; }

 particles[last_fired_particle].draw = true;
 particles[last_fired_particle].size        = 300;
 particles[last_fired_particle].startpos    = pos;
 particles[last_fired_particle].pos    = pos;
 particles[last_fired_particle].frame       = 0;
 particles[last_fired_particle].time        = GetTickCount();
 particles[last_fired_particle].glop        = glop;
 particles[last_fired_particle].heading     = heading;
 particles[last_fired_particle].frametime   = GetTickCount();
// particles[last_fired_particle].hitpos      = shot_pos;
// particles[last_fired_particle].hitindex    = shot_index;
 particles[last_fired_particle].rotate      = random(359);
 particles[last_fired_particle].rot_time    = GetTickCount();
 particles[last_fired_particle].stan = 1;


}

void __fastcall tflamethrower::reset()
{
	int i;
//RYSOWALEM_swiatlo[0] = false;
//RYSOWALEM_swiatlo[1] = false;
//RYSOWALEM_swiatlo[2] = false;
for (i = 0; i < 129; i++) particles[i].draw = false;
}

void __fastcall tflamethrower::setallinpos(t3dpoint pos)
{
int i;
for (i = 0; i < 129; i++) particles[i].startpos = pos;



}

//---------------------------------------------------------------------------

#ifndef WEAPON_FLAMETHROWERH
#define WEAPON_FLAMETHROWERH
//---------------------------------------------------------------------------
							 #include "DxcMath.h"
							 #include "glAnimTexture.h"

//const
//MAX_FLAME_SPRITE_SIZE = 5000;
   t3dpoint BillboardX;// : t3dpoint;
t3dpoint BillboardY;// : t3dpoint;
struct tflame_part {
float 			size;
t3dpoint 		pos;
t3dpoint 		startpos;
bool 			draw;
int 	frame;
int 	time;
int 	frametime;
float glop;
float heading;
float rotate;
int 		rot_time;
t3dpoint 	hitpos;
int 		stan;
bool 		stanC;
int 		hitindex;
t3dpoint odbity_wektor;
};

class tflamethrower {
public:
int NORMAL_TIME;
tglanimtexture * sprite;
float power;
tflame_part particles[129];
int last_fired_particle;
 int SHOT_SPEED;
 bool already_shot;
 int shot_time;
//RYSOWALEM_swiatlo : array[0..2] of boolean;

  __fastcall tflamethrower();
  __fastcall ~tflamethrower();
void __fastcall reset();
void __fastcall setallinpos(t3dpoint pos);
void __fastcall throw_next_free(t3dpoint pos, float  glop, float heading);
void __fastcall checkparticle(int index);
void __fastcall load(AnsiString filename);
void __fastcall DRAW();
private:
void __fastcall make_next_frame();



};

#endif

here is a sample what i did, uses time, works as intended.



#10 ShaPe   Members   -  Reputation: 107

Like
0Likes
Like

Posted 13 October 2013 - 11:17 AM

//---------------------------------------------------------------------------


#pragma hdrstop

#include "WEAPON_FLAMETHROWER.h"
//#include "FC_OPENGL.h"

//---------------------------------------------------------------------------

#pragma package(smart_init)

void __fastcall  drawbillboardF(float cx, float cy, float cz, float size)
{
double m_PickInfo_ModelView[16];// of ;

  t3dpoint Pos;
glGetDoublev(GL_MODELVIEW_MATRIX, m_PickInfo_ModelView);

BillboardX.x = m_PickInfo_ModelView[0];
BillboardX.y = m_PickInfo_ModelView[4];
BillboardX.z = m_PickInfo_ModelView[8];


BillboardY.x =	m_PickInfo_ModelView[1];
BillboardY.y =	m_PickInfo_ModelView[5];
BillboardY.z =	m_PickInfo_ModelView[9];



glBegin(GL_QUADS);

glTexCoord2f(0.0, 1.0);

Pos.x = cx-BillboardX.x*size+BillboardY.x*size;
Pos.y = cy-BillboardX.y*size+BillboardY.y*size;
Pos.z = cz-BillboardX.z*size+BillboardY.z*size;

glVertex3f(Pos.x,Pos.y,Pos.z);
glTexCoord2f(1.0, 1.0);

Pos.x = cx+BillboardX.x*size+BillboardY.x*size;
Pos.y = cy+BillboardX.y*size+BillboardY.y*size;
Pos.z = cz+BillboardX.z*size+BillboardY.z*size;

glVertex3f(Pos.x,Pos.y,Pos.z);

glTexCoord2f(1.0, 0.0);
Pos.x = cx+BillboardX.x*size-BillboardY.x*size;
Pos.y = cy+BillboardX.y*size-BillboardY.y*size;
Pos.z = cz+BillboardX.z*size-BillboardY.z*size;
glVertex3f(Pos.x,Pos.y,Pos.z);

glTexCoord2f(0.0, 0.0);
Pos.x = cx-BillboardX.x*size-BillboardY.x*size;
Pos.y = cy-BillboardX.y*size-BillboardY.y*size;
Pos.z = cz-BillboardX.z*size-BillboardY.z*size;
glVertex3f(Pos.x,Pos.y,Pos.z);

glEnd();

}



void __fastcall  tflamethrower::DRAW()
{
int i;
float d; t3dpoint v;


make_next_frame();
//glpushmatrix();
glColor4f(1,1,1,0.7);
glDepthMask(GL_FALSE);

glEnable(GL_BLEND);
//
glBlendFunc(GL_ONE,GL_ONE);
//glEnable(GL_TEXTURE_2D);
for (i = 0; i < 129; i++)
if (particles[i].draw == true)
{
//	                                                                 glbindtexture(GL_TEXTURE_2D,sprite.animtextures[particles[i].frame]);


// particles[i].size = 300.0f;
drawbillboardF(particles[i].pos.x,particles[i].pos.y,particles[i].pos.z,particles[i].size);
 //glrotate(-angle,1,0,0);
//  glmatrixmode(GL_TEXTURE);glrotate(-angle,0,1,0); glmatrixmode(GL_MODELVIEW);
//glpopmatrix();

}


//glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
}


  __fastcall tflamethrower::tflamethrower()
  {
   sprite = new tglanimtexture;
  }
  __fastcall tflamethrower::~tflamethrower()
  {
   sprite->free();
   delete sprite;
  }

void __fastcall  tflamethrower::load(AnsiString filename)
{
 sprite->loadtex(filename);
}


void __fastcall  tflamethrower::make_next_frame()
{
try {
int i;
t3dpoint k;

for (i = 0; i < 129; i++)
if (particles[i].draw == true)
{

k.x = 100000000.0f*(sin(particles[i].glop*imopi)*cos(particles[i].heading*imopi));
k.z = 100000000.0f*(cos(particles[i].glop*imopi)*cos(particles[i].heading*imopi));
k.y = 100000000.0f*(sin(particles[i].heading*imopi));
 particles[i].size        = (float(GetTickCount())-particles[i].time)/100.0f;
  k=Normalize(k);


   if (NORMAL_TIME == 0) {        //czyli jak false
	   particles[i].pos = vectors_frontadd(particles[i].startpos,vector_multiple(k,power*(GetTickCount()-particles[i].time)/1000.0f));
   } else {

 particles[i].pos = vectors_frontadd(particles[i].pos,vector_multiple(k,2.00*power*(GetTickCount()-particles[i].time)/1000.0f)); //end else
		   }
particles[i].time = GetTickCount();
//					 particles[i].rotate = particles[i].rotate +1;
/*if (GetTickCount()-particles[i].frametime >= sprite.speed ) {
	  particles[i].frametime = GetTickCount();

//particles[i].frame = particles[i].frame + 1;       odkomentowac
//if (particles[i].frame > high(sprite.animtextures)  )then begin particles[i].frame := 0; particles[i].draw := false; end; odkomenotwac

}

  */

checkparticle(i);
}

	 } catch (...) {

	 }
}
                                     //tutaj jak wielkosc przekracza maks wielkosc to usuwamy z rysowania particle
void __fastcall tflamethrower::checkparticle(int index)
{
//if ( particles[index].size >=  5000.0f ) particles[index].draw = false;

if (n3ddistance(particles[index].startpos,particles[index].pos) > 1000.0f)       
                               particles[index].draw = false;

}

void __fastcall tflamethrower::throw_next_free(t3dpoint pos, float  glop, float heading)
{
int i;
bool found;

if ( (GetTickCount()-shot_time) < SHOT_SPEED ) return;
shot_time = GetTickCount();
already_shot = true;
found = false;
for (i = 0; i < 129; i++)
if (particles[i].draw == false)
{
found = true;
last_fired_particle = i; break;
}

if (found == false){ last_fired_particle = 0; }

 particles[last_fired_particle].draw = true;
 particles[last_fired_particle].size        = 300;
 particles[last_fired_particle].startpos    = pos;
 particles[last_fired_particle].pos    = pos;
 particles[last_fired_particle].frame       = 0;
 particles[last_fired_particle].time        = GetTickCount();
 particles[last_fired_particle].glop        = glop;
 particles[last_fired_particle].heading     = heading;
 particles[last_fired_particle].frametime   = GetTickCount();
// particles[last_fired_particle].hitpos      = shot_pos;
// particles[last_fired_particle].hitindex    = shot_index;
 particles[last_fired_particle].rotate      = random(359);
 particles[last_fired_particle].rot_time    = GetTickCount();
 particles[last_fired_particle].stan = 1;


}

void __fastcall tflamethrower::reset()
{
	int i;
//RYSOWALEM_swiatlo[0] = false;
//RYSOWALEM_swiatlo[1] = false;
//RYSOWALEM_swiatlo[2] = false;
for (i = 0; i < 129; i++) particles[i].draw = false;
}

void __fastcall tflamethrower::setallinpos(t3dpoint pos)
{
int i;
for (i = 0; i < 129; i++) particles[i].startpos = pos;



}

//---------------------------------------------------------------------------

#ifndef WEAPON_FLAMETHROWERH
#define WEAPON_FLAMETHROWERH
//---------------------------------------------------------------------------
							 #include "DxcMath.h"
							 #include "glAnimTexture.h"

//const
//MAX_FLAME_SPRITE_SIZE = 5000;
   t3dpoint BillboardX;// : t3dpoint;
t3dpoint BillboardY;// : t3dpoint;
struct tflame_part {
float 			size;
t3dpoint 		pos;
t3dpoint 		startpos;
bool 			draw;
int 	frame;
int 	time;
int 	frametime;
float glop;
float heading;
float rotate;
int 		rot_time;
t3dpoint 	hitpos;
int 		stan;
bool 		stanC;
int 		hitindex;
t3dpoint odbity_wektor;
};

class tflamethrower {
public:
int NORMAL_TIME;
tglanimtexture * sprite;
float power;
tflame_part particles[129];
int last_fired_particle;
 int SHOT_SPEED;
 bool already_shot;
 int shot_time;
//RYSOWALEM_swiatlo : array[0..2] of boolean;

  __fastcall tflamethrower();
  __fastcall ~tflamethrower();
void __fastcall reset();
void __fastcall setallinpos(t3dpoint pos);
void __fastcall throw_next_free(t3dpoint pos, float  glop, float heading);
void __fastcall checkparticle(int index);
void __fastcall load(AnsiString filename);
void __fastcall DRAW();
private:
void __fastcall make_next_frame();



};

#endif

here is a sample what i did, uses time, works as intended.

 

 

 

Thank you for writing all of that! However, I have still not started using OpenGL (I'm drawing on Android using a Canvas object) so I find it hard to understand what you are doing. Can you outline the logic behind your code so that I can try and implement it in my project?



#11 WiredCat   Members   -  Reputation: 257

Like
0Likes
Like

Posted 14 October 2013 - 07:18 AM

Delete all OpenGL code (OpenGL is used only for drawing), math is math

 

 

commented code:

 

 

 

// particles[i].rotate = particles[i].rotate +1;
/*if (GetTickCount()-particles[i].frametime >= sprite.speed ) {
particles[i].frametime = GetTickCount();

//particles[i].frame = particles[i].frame + 1; odkomentowac
//if (particles[i].frame > high(sprite.animtextures) )then begin particles[i].frame := 0; particles[i].draw := false; end; odkomenotwac

}

*/

is for animating sprites (simple array of textures that index is changed

t3dpoint __fastcall vectors_frontadd(t3dpoint v1, t3dpoint v2) //plaszczyzna XY
{
t3dpoint result;
result.x = v1.x+v2.x;
result.y = v1.y-v2.y;
result.z = v1.z-v2.z;

return result;
}
 

rest is just math

 

oh and glop angle is actually a 'heading' angle. / heading here is up down angle


Edited by ___, 14 October 2013 - 07:19 AM.


#12 ankhd   Members   -  Reputation: 1240

Like
0Likes
Like

Posted 19 October 2013 - 07:03 PM

hi.

I just a a quick look but I think its your heading for the particles are all using the current units facing direction.

So all your particles need to have there own heading vector and that should do the trick.

rolleyes.gif



#13 slayemin   Members   -  Reputation: 2466

Like
1Likes
Like

Posted 30 October 2013 - 05:56 PM

You can do this by simply using a bunch of fire particles. Your player will have a particle emitter positioned a set distance away from the center of the player and based on the players orientation. There are a few things you'll want to think about when setting the properties for your emitter:
-The frequency/rate of particle emission

-The initial velocity of a particle (which should also factor in the current velocity of the emitter)

-The initial size of a particle

-etc. etc.

 

For your flame thrower, all you'd really have to do is create a flame particle every fraction of a second, give it an initial position and an initial velocity, and then let your particle system update all of the particles in its list of active particles. Once a particle has been emitted from the emitter, its position and velocity should be completely independent of the emitter.

It looks like the GTA2 flame thrower is shooting out flame particles at a high rate and the lifespan of each particle is quite short. I predict that if your character is initially stationary and shooting flames, then the distance of the farthest flame from the player should be at its standard length (let's call it 1 Flame Length). I also predict that if your character suddenly starts moving towards the direction of flames, the flame length should shorten for a brief period since the player is moving with the slower moving flame particles and the flame length may be 0.8 units of flame length until the first flame emitted with the player velocity reaches its lifespan. If the player moves backwards, then we should expect the flame length to extend (1.2 flame length?) for a brief moment (and we may even expect to see gaps between particles if the emitter frequency is too low).

Anyways, here are a few things you can try:
-You don't need to store the angle on a flame particle. Give it a velocity instead. Velocity is a 2D or 3D vector. The orientation of the vector contains the direction of movement. The magnitude of the velocity vector contains the speed. Every frame, all you have to do is add the velocity vector to the position coordinate.
-If you use spherical fire sprites, you don't have to rotate your flames. If you simply must use non-spherical sprites, you can still get the direction of travel by normalizing your velocity vector and using those normalized values to rotate your sprite. A normalized 2D velocity vector already contains [Cos(theta), Sin(theta)]
-I don't know if you're doing it already, but I'd recommend using a memory pool for your particles (you don't want to allocate and deallocate memory for each particle each time its created and dies).
-If you're feeling crazy, you can also store an "Acceleration" value in a particle. If you remember your calculus and physics, velocity is the change in position over time. Accelleration is the change in velocity over time. (And surge is the change in accelleration over time, such as a gas pedal on a car). For each frame and for every active particle, you'd do: Velocity += Accelleration; Position += Velocity; If you set a small negative number to the accelleration, your particle will gradually slow down as it travels.


Edited by slayemin, 30 October 2013 - 06:04 PM.

Eric Nevala

Indie Developer | Dev blog


#14 Norman Barrows   Crossbones+   -  Reputation: 2058

Like
0Likes
Like

Posted 08 November 2013 - 10:26 PM

+1 what slayemin said.

 

and don't forget to change the sprite based on particle age. if you look at the GTA2 screen shots, the particles near the flamethrower are white-yellow, and change to orange-red over time (as they move farther away).


Norm Barrows

Rockland Software Productions

"Building PC games since 1988"

 

rocklandsoftware.net

 





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS