• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
ShaPe

Trying to simulate the flamethrower from GTA2

13 posts in this topic

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:

 

http://www.youtube.com/watch?v=_ilvq10JSjk

 

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
0

Share this post


Link to post
Share on other sites

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.

0

Share this post


Link to post
Share on other sites

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
0

Share this post


Link to post
Share on other sites

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
1

Share this post


Link to post
Share on other sites

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.

0

Share this post


Link to post
Share on other sites

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
0

Share this post


Link to post
Share on other sites

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
0

Share this post


Link to post
Share on other sites

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). 

0

Share this post


Link to post
Share on other sites
//---------------------------------------------------------------------------


#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.

0

Share this post


Link to post
Share on other sites
//---------------------------------------------------------------------------


#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?

0

Share this post


Link to post
Share on other sites

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 ___
0

Share this post


Link to post
Share on other sites

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

0

Share this post


Link to post
Share on other sites

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
1

Share this post


Link to post
Share on other sites

+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).

0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0