Game Development Community

Shooting Particles Bug..?

by Maarten Visser · in Torque Game Builder · 06/24/2009 (5:34 am) · 11 replies

Okay, right, I have my object shooting a particle.. It's kinda like a flametank, so when you press the fire key, the flames will keep coming out of the tank.. It works fine, really, except after about.. 20 seconds of pressing, it doesn't work anymore. Nothing happens, and the console says (Allthough it's said that from the moment I start 'shooting the particle'):

"t2dSceneGraph::AddToScene() Object '2236' is already in a scenegraph!."
"t2dSceneGraph::AddToScene() Object '2239' is already in a scenegraph!."
"t2dSceneGraph::AddToScene() Object '2242' is already in a scenegraph!."

etc.. Does this mean that my particle is not properly removed from the scene, and I have about 1000 invisible particles in the scene? (I'd like my flamethrower to continue firing, even after 20 seconds..)

!UPDATE: Whether I press the firekey or not, after 20 seconds of in-scene time, the particle stops working..

Furthermore, is it possible to make this flame deal damage? (The particle has one point, from where it is fired, allthough the particle fire is 'just an animation', the actuall emitter doesn't move)

I really hope someone can help out on this.. Thanks

#1
06/24/2009 (8:03 am)
I'd be curious to see the code where you do the firing, and set up the emitter.

It sound like its something odd with how the emitter is being started or handled, but it's hard to say without seeing the code.
#2
06/24/2009 (8:06 am)
I'm using the "Game -> Shoots" behaviour template to shoot my particle..


%template.addBehaviorField(projectile, "The projectile to clone and shoot", object, "", t2dSceneObject);

function ShootsBehavior::fire(%this, %val)
{
if (%val == 0)
{
cancel(%this.fireSchedule);
return;
}

if (!isObject(%this.projectile))
return;

%projectile = %this.projectile.cloneWithBehaviors();

%projectile.setPosition(%this.owner.position);
%projectile.setRotation(%this.owner.rotation);
%projectile.setLinearVelocityPolar(%this.owner.rotation, %this.projectileSpeed);

if (!isEventPending(%this.fireSchedule))
%this.fireSchedule = %this.schedule(%this.fireRate * 1000, "fire", 1);
}
#3
06/24/2009 (9:46 am)
Are you using the emitter as the projectile? That's kind of what it looks like is probably happening. So instead of having the emitter stay in one spot and shoot out particles, you are actually creating new emitters and throwing them out.

Probably the best approach would be to modify the ShootsBehavior, maybe call it ShootsParticleBehavior.

Inside the behavior you would aim the emitter, then start it up if the button is being pressed, or stop it if it isn't being pressed. Instead of creating a new one each time.

I can help you out with writing the behavior some more later today if you need help with it. This is a really good article on the particle engine, it will tell you all about aiming particle emitters and everything else you might want to do.

As for doing damage with the particles, this is a problem I've been dealing with recently. The problem is that you can be alerted when a particle collides with something, but you don't get any information on what it collided with. What I've found to be best is to just damage everything in the area of effect, do a little bit of damage at a regular pace.

As I think about it this would probably be something a lot of people would want to do. I'll write up a tutorial on this, although I won't be able to get started on it until later this afternoon.
#4
06/24/2009 (10:28 am)
So, we would need something like:
%effect = FlameParticle.cloneWithBehaviors()
   
%effect.setPosition(PlayerTank.position);
%effect.setRotation(PlayerTank.rotation);
%effect.setLinearVelocityPolar(PlayerTank.rotation, 0);
// 0 is for projectilespeed, as we don't want the particle emitter to have a speed, just the emission..

%effect.stopEffect()
// So it doesn't go firing straight away

%effect = $particlefire
// Maybe not neccesairy, I don't know..

moveMap.bind("keyboard", "Space", "Fire", "Makes the emitter emit the particles");
// Setting the 'fire'-key

And another function:

function fire(%this)
{
$particlefire.playEffect();
// and from here, it probably would need to check if the Space bar is still pressed.. Something like this I have in mind.. But I have no idea if it 's actually a working script.. I'm not good at it yet..
}
#5
06/24/2009 (11:51 am)
Yes, similar to that, except you wouldn't want to clone the flame particle on the first line. and you wouldn't want to set the linear velocity, you want it to just stay in position at the tank shooting out particles.

I'm thinking you would probably want to attach the behavior to the particle emitter. Then mount the particle emitter onto the tank, and have it inherit rotation from the tank. All the behavior would need to do is turn the emitter on or off based on when the key is pressed, so something like this:

function ShootsParticleBehavior::fire(%this, %val)
{
 if (%val == 0)
 {
  %this.owner.stopEffect();
  return;
 }

 %this.owner.playEffect();
}
#6
06/24/2009 (5:58 pm)
So, I'm using this piece of code now, for the fire:

function ShootsBehavior::fire(%this, %val)
{  
   if (%val == 0)  
   {  
   Flames.stopEffect();  
   return;  
   }  
   Flames.playEffect();  

   Flames.setPosition(%this.owner.position);
   Flames.setRotation(%this.owner.rotation);
}

and it works fine, except the fire doesn't move and rotate along with the object it's emitted from (say I press my spacebar, and keep it pressed, then move/rotate my Tank, the fire remains emitted from the position the tank was initially standing.

While when I press space the 2nd time, while the object is at it's new position, that is the spot where the new emission is, and stays, untill I let go of my fire key..

I want to constant update these two:

Flames.setPosition(%this.owner.position);
Flames.setRotation(%this.owner.rotation);

and them to be the location and rotation of the flames all the time.. Is this doable?

Thanks,
MaarX

PS: Hows the tutorial going, on making particle emission collide with other stuff?
#7
06/24/2009 (6:17 pm)
Well, I got a behavior written, the problem is that the solution I was using in my game won't quite work in this particular case.

I've got a different idea on how to get it to work, but it's going to take some playing with to get working. It seems like what you could do is have a series of trigger zones in front of the tank, each one mounted on the one behind it, and the final one mounted to the tank. They would have a mounting force, so that as the tank moved they would take a little while to catch up. This would allow you to approximate the size of the flame cone, and damage anything caught within. It feels kind of kludgey, but I'm really not seeing any other way of doing it.

For your problem with the flames not tracking the tank correctly, try removing lines 10 and 11, and mounting the flame emitter onto your tank. Then it will track the movements of the tank automatically.
#8
06/25/2009 (3:18 am)
Most things seem to work now (thanks for the mounting tip)

I made sure the flames shoot an invisible object with it, with the same speed as the flames, the object is invisible and deals damage, and offcourse has a quick firerate, like the flames have.. Seems like a good solution.. :)

Would there be any chance by the way, to make sure the flames work for X time, and then need an Y reload time.. (Or even better: To emit the flames for say 20 seconds, when the fuel is empty, slowly recharge the fuel, while during the recharge, you can shoot too, making the fuel go empty again etc.. )

- So when pressed firekey, the bar goes down, while releasing it will make the bar slowly fill up again

Thanks so far!
#9
06/25/2009 (9:53 am)
That's good to hear that the invisible objects are working for doing damage.

As far as a fuel gauge, that shouldn't be difficult. I think the best way would be to set a variable in the emitter that keeps track of how much fuel you have. Use the ontimer method to manage the fuel. If the player is shooting, then onTimer decreases the fuel amount until zero, otherwise it adds until it hits maximum. If it hits zero, then turn off the emitter. You could set the maximum fuel and starting fuel in the onBehaviorAdd method, along with starting the onTimer method, something like this:

function ShootsBehavior::onBehaviorAdd(%this) {
 ...
 %this.owner.maximumFuel = 500; //or whatever you want
 %this.owner.currentFuel = %this.owner.maximumFuel;
 %this.owner.setTimerOn(200); //adjust this to increase or decrease the rate that the fuel changes
 ...
}

Then you would need to define the onTimer call back for the emitter class, it could look something like this:

function EmitterClass::onTimer(%this) {
 if($isShooting) {//however you determine that the player has the button held down
  if(%this.currentFuel > 0) {
   %this.currentFuel -= 2; //adjust this to change the rate at which fuel drains
  } else {
   %this.currentFuel = 0;
   %this.stopEffect();
  }
 } else {
  if(%this.currentFuel < %this.maximumFuel)
   %this.currentFuel += 1; //adjust this to change the rate at which fuel recharges
 }
}
Of course you'll want to change EmitterClass to whatever class your emitter is a member of. That should get you started, I think I saw a thread around here on how to make a progress bar, you could use that to visually display how much fuel you have left.

I hope that help.
#10
06/26/2009 (3:50 am)
function ShootsBehavior::onBehaviorAdd(%this)
{
   if (isObject(moveMap))
      moveMap.bindObj(getWord(%this.fireKey, 0), getWord(%this.fireKey, 1), "fire", %this);

Flames.maximumFuel = 20; 
Flames.currentFuel = Flames.maximumFuel;  
Flames.setTimerOn(8);    

}

function ShootsBehavior::fire(%this, %value)
{  
   if (%value == 0)  
   {  
   Flames.stopEffect(true, false); 
   Player.playAnimation( TankAnim ); 
   cancel(%this.fireSchedule);
   return;  
   }  
   if (!isObject(%this.projectile))
      return;
   
   Flames.playEffect(false);

   Player.playAnimation( TankFireAnim ); 

   %projectile = %this.projectile.cloneWithBehaviors();
   %projectile.setPosition(%this.owner.position);
   %projectile.setRotation(%this.owner.rotation);
   %projectile.setLinearVelocityPolar(%this.owner.rotation + 90, %this.projectileSpeed);
   
   if (!isEventPending(%this.fireSchedule))
      %this.fireSchedule = %this.schedule(%this.fireRate * 1000, "fire", 1);
}

function Flames::onTimer(%this, %value) 
{
 if(%value != 0) {
  if(Flames.currentFuel > 0) {
   Flames.currentFuel -= 2; 
  } else {
   Flames.currentFuel = 0;
   Flames.stopEffect();
  }
 } else {
  if(Flames.currentFuel < Flames.maximumFuel)
   Flames.currentFuel += 1; 
 }
}

That's what I'm using now.. It's not working, the flames aren't coming out at all like this.. Maybe I'm not calling the fire function in the good way? (Using : %value != 0)

Also I've tried putting the function Flames::onTimer(%this, %value) directly into the ShootsBehavior::fire function, which didn't work either (the flames wouldn't come out anymore)

What am I doing wrong?
#11
06/26/2009 (6:55 am)
Well, one problem is that the onTimer function only passes one argument, the %this argument, so %value will always be zero.

Another possible problem is that with the amount of fuel you set, and with the rate you drain it, you'll run out of fuel 160 milliseconds after pressing the fire button. On line 8, setTimerOn the argument is how many milliseconds until onTimer gets called, so your onTimer will get called every 8 milliseconds, or 125 times per second.

Beyond that, I'm not sure. I'll have to take a closer look at it this afternoon.