Game Development Community

dev|Pro Game Development Curriculum

Flaming Sword in TGEA 1.8.1

by Ben McIntosh · 07/05/2009 (12:08 am) · 33 comments

I needed to make a flaming sword for our TGEA 1.8.1 game, so here is how I ended up doing it in the easiest way possible.

Note: I will be using patch notation where lines added are preceded with '+' and lines removed are preceded with a '-'.

First you need to install the Melee Port for TGEA 1.8.1 resource to implement a melee sword weapon.

Next, have you ever tried attaching an emitter to a specific node in a weapon image? You can try with a stateEmitterNode[] definition, but it doesn't work because of a bug that was never fixed, so:

In shapeImage.cpp:
...
void ShapeBaseImageData::initPersistFields()
{
   ...
   addField("stateEmitter", TypeParticleEmitterDataPtr, Offset(stateEmitter, ShapeBaseImageData), MaxStates);
   addField("stateEmitterTime", TypeF32, Offset(stateEmitterTime, ShapeBaseImageData), MaxStates);
   - addField("stateEmitterNode", TypeS32, Offset(stateEmitterNode, ShapeBaseImageData), MaxStates);
   + addField("stateEmitterNode", TypeString, Offset(stateEmitterNode, ShapeBaseImageData), MaxStates);
   ...
}
...
Wow this bug has been present since at least 2004!

Now, before actually attaching a fire emitter to the sword, it would be much nicer looking if the fire was emitted in a line connecting two nodes in the weapon image. To do this, make the following changes:

In shapeBase.h:
...
   struct StateData {
      ...
      F32 emitterTime;              ///<
      S32 emitterNode;
      + S32 emitterNodeEnd;
   }
   ...
   /// @name Nodes
   /// @{
   S32 retractNode;     ///< Retraction node ID.
                        ///
                        ///  When the player bumps against an object and the image is retracted to
                        ///  avoid having it interpenetrating the object, it is retracted towards
                        ///  this node.
   S32 muzzleNode;      ///< Muzzle node ID.
                        ///
                        ///
   S32 ejectNode;       ///< Ejection node ID.
                        ///
                        ///  The eject node is the node on the image from which shells are ejected.
   S32 emitterNode;     ///< Emitter node ID.
                        ///
                        ///  The emitter node is the node from which particles are emitted.
   + S32 emitterNodeEnd;  ///< Emitter end node ID.
   +                      ///
   +                      ///  The emitter node end is the node to which particles are emitted from start node.
   /// @}
   ...
   struct ImageEmitter {
      S32 node;
      + S32 nodeEnd;
      F32 time;
      SimObjectPtr<ParticleEmitter> emitter;
   };
   ...
Again back to shapeImage.cpp:
...
bool ShapeBaseImageData::onAdd()
{
   ...
   s.emitter = stateEmitter[i];
   s.emitterTime = stateEmitterTime[i];
   s.emitterNode = -1; // resolved in load
   + s.emitterNodeEnd = -1; // resolved in load
   ...
}
...
bool ShapeBaseImageData::preload(bool server, String &errorStr)
{
   ...
   if (stateEmitterNode[i] && stateEmitterNode[i][0])
   + {
      + char buff1[32];
      + char buff2[32];
      + dSprintf(buff1,sizeof(buff1),"%sStart",stateEmitterNode[i]);
      + dSprintf(buff2,sizeof(buff2),"%sEnd",stateEmitterNode[i]);
      - s.emitterNode = shape->findNode(stateEmitterNode[i]);
      + s.emitterNode = shape->findNode(buff1);
      + s.emitterNodeEnd = shape->findNode(buff2);
   + }
   if (s.emitterNode == -1)
   + {
      s.emitterNode = muzzleNode;
      + s.emitterNodeEnd = muzzleNode;
   + }
   ...
}
...
void ShapeBase::updateImageAnimation(U32 imageSlot, F32 dt)
{
   ...
   // Particle emission
   for (S32 i = 0; i < MaxImageEmitters; i++) {
      MountedImage::ImageEmitter& em = image.emitter[i];
      if (bool(em.emitter)) {
         if (em.time > 0) {
            em.time -= dt;

            MatrixF mat;
            + MatrixF matEnd;
            getRenderImageTransform(imageSlot,em.node,&mat);
            + getRenderImageTransform(imageSlot,em.nodeEnd,&matEnd);
            Point3F pos,axis;
            + Point3F posEnd,axisEnd;
            mat.getColumn(3,&pos);
            mat.getColumn(1,&axis);
            + matEnd.getColumn(3,&posEnd);
            + matEnd.getColumn(1,&axisEnd);
            - em.emitter->emitParticles(pos,true,axis,getVelocity(),(U32) (dt * 1000));
            + em.emitter->emitParticles(pos,posEnd,axis,getVelocity(),(U32) (dt * 1000));
         }
         else {
            em.emitter->deleteWhenEmpty();
            em.emitter = 0;
         }
      }
   }
}
...
void ShapeBase::startImageEmitter(MountedImage& image,ShapeBaseImageData::StateData& state)
{
   ...
   bem->time = state.emitterTime;
   bem->node = state.emitterNode;
   + bem->nodeEnd = state.emitterNodeEnd;
   bem->emitter = new ParticleEmitter;
   bem->emitter->onNewDataBlock(state.emitter);
   if( !bem->emitter->registerObject() )
      delete bem->emitter;
}
...
Now, to use the new code, you simply need to have a node defined in your weapon where you want the emitter to start (e.g. fireStart) and a node where you want the emitter to end (e.g. fireEnd). These two nodes must end with "Start" and "End" respectively. The stateEmitterNode[] should be set to the first part of the name of both nodes, in this case "fire". If you are using the Melee resource, then using "damage" will work out of the box because the included sword DTS model contains "damageStart" and "damageEnd".

Finally, we need to set up the state machine for the sword. There are a couple of problems that need to be addressed with this.

First, stateEmitter[] is defined on each state, so how can we make a continuous flame that stays lit for all time? Looking through the code for shapeBaseImage, in the startImageEmitter function there is a nice surprise:
// If we are already emitting the same particles from the same
   // node, then simply extend the time.  Otherwise, find an empty
   // emitter slot, or grab the one with the least amount of time left.
Great, so if we define the same emitter on the same node for all states, we should get a continuous flame with no problems. But what if we are in one state for too long? We can set the stateEmitterTime[] to a very high number, but that seems a little messy. Instead, I chose to create two "Ready" states that circulate between each other and continually refresh the flame emitter (you need two states because you cannot loop a state back to itself).

Okay, but what if we want to turn the flame on and off at will? You could implement the Setting an ImageState Manually resource, but to me, that looks like a ton of mess for something so simple. I chose to create a third "Ready" state that has no emitter defined. I get to this "Off" state by controlling the sword ammo. Yes, swords have ammo! So when there is ammo, the sword lights up and when there is no ammo, it stops burning. Here is my sword dataBlock:
datablock ShapeBaseImageData(SwordImage)
{
   shapeFile = "~/data/shapes/weapons/sword/rune_blade01.dts";
   emap = true;
   mountPoint = 0;
   correctMuzzleVector = false;
   className = "WeaponImage";
   item = Sword;
   ammo = SwordAmmo;
   customLookAnim = "looknw";

   // Here are the Attacks we support
   hthNumAttacks = 3;
   hthAttack[0]                     = OneHandedAttackSwing;
   hthAttack[1]                     = OneHandedAttackSlice;
   hthAttack[2]                     = OneHandedAttackThrust;

   // Initial start up state
   stateName[0]                     = "Preactivate";
   stateTransitionOnLoaded[0]       = "Activate";
   
   // Activating the sword.  Called when the weapon is first mounted
   stateName[1]                     = "Activate";
   stateTransitionOnTimeout[1]      = "ReadyEmitterOn1";
   stateTimeoutValue[1]             = 0.6;
   //stateSequence[1]                 = "Activate";

   // Ready to fire, just waiting for the trigger, emitter on
   stateName[2]                     = "ReadyEmitterOn1";
   stateTransitionOnNoAmmo[2]       = "ReadyEmitterOff";
   stateTransitionOnTriggerDown[2]  = "Fire";
   stateTransitionOnTimeout[2]      = "ReadyEmitterOn2";
   stateTimeoutValue[2]             = 1;
   stateWaitForTimeout[2]           = false;
   stateEmitter[2]                  = "TorchFireEmitter";
   stateEmitterTime[2]              = 1;
   stateEmitterNode[2]              = "damage";

   // Ready to fire, just waiting for the trigger, emitter on
   stateName[3]                     = "ReadyEmitterOn2";
   stateTransitionOnNoAmmo[3]       = "ReadyEmitterOff";
   stateTransitionOnTriggerDown[3]  = "Fire";
   stateTransitionOnTimeout[3]      = "ReadyEmitterOn1";
   stateTimeoutValue[3]             = 1;
   stateWaitForTimeout[3]           = false;
   stateEmitter[3]                  = "TorchFireEmitter";
   stateEmitterTime[3]              = 1;
   stateEmitterNode[3]              = "damage";

   // Ready to fire, just waiting for the trigger, emitter off
   stateName[4]                     = "ReadyEmitterOff";
   stateTransitionOnAmmo[4]         = "ReadyEmitterOn1";
   stateTransitionOnTriggerDown[4]  = "Fire";

   // Fire the weapon. Calls the fire script which does the actual work.
   stateName[5]                     = "Fire";
   stateTransitionOnTimeout[5]      = "Reload";
   stateTimeoutValue[5]             = 0.2;
   stateFire[5]                     = true;
   stateAllowImageChange[5]         = false;
   stateScript[5]                   = "onFire";

   // Play the reload animation, and transition into
   stateName[6]                     = "Reload";
   stateTransitionOnTimeout[6]      = "ReadyEmitterOn1";
   stateTimeoutValue[6]             = 0.8;
   stateAllowImageChange[6]         = false;
   stateEjectShell[6]               = false;
};

Just control the ammo to turn the emitter on and off using setInventory(SwordAmmo, 1) or setInventory(SwordAmmo, 0) respectively.


I hope someone finds this useful. :)

About the author

Ben is the co-founder of Urban Brain Studios; a new independent developer of games and game development tools.

Page «Previous 1 2
#1
07/05/2009 (2:03 am)
Awesome!
#2
07/05/2009 (3:33 am)
Useful indeed! Thanks Ben!
#3
07/05/2009 (4:17 am)
Useful indeed! Thanks Ben!
#4
07/05/2009 (5:23 am)
Nice work! Way to make good use of the existing engine functionality. :)
#5
07/05/2009 (11:42 am)
nice.
it drops straight in to T3D, with no changes, by the way
#6
07/05/2009 (1:06 pm)
Sounds pretty cool. I can just imagine being able to fling drops of blood everywhere when you slice into people :D
#7
07/05/2009 (2:59 pm)
@deepscratch: Awesome, thanks for trying it in T3D.

@Michael: Ooo I like the idea of replacing the fire with a blood emitter :)
#8
07/05/2009 (4:36 pm)
maybe put in a switch :) on hit switch the emitter for a blood emitter, then replace the blood with fire again after X amount of time :)
#9
07/05/2009 (6:47 pm)
Wow Ben. Cool! Thanks...and... "Fight On!" ;)
#10
07/06/2009 (7:20 am)
Hey guys,

I am trying to get point about car collıson: There is a car on the road but ıt should go contantly wıthout collısion on the road. I am trying to create pavement near to road. But my car shouldnt get out of road and last thing that I want to domy pavement need to be invisible. How can I do all things that I clarified above?

thanks ın advance,
#11
07/06/2009 (7:54 am)
umm... hello Nazif. First of all. I have noticed you have been interrupting several threads with questions like this. Post your Questions in the appropriate and seperate threads.

Next Question. Which Version of Torque are you using? (everyone else, just relax, I will handle Nazif :)
#12
07/06/2009 (8:00 am)
@Nazif:
It's one thing to spam your question across a dozen different threads in the space of 20 minutes, but you should show a little respect to the resource posters on here and don't ask for help here -- unless it pertains to the resource in question, of which your question has no relationship to.

Steering this back on topic:
How about a water gun? Or even a water hose? The water hose idea would be a great candidate for something like this. Firefighter game, maybe? Or even water cannon sports! We used to have so much fun washing Greenpeace(rs) away from our nuclear powered aircraft carrier :D
#13
07/06/2009 (8:04 am)
Dear frıends,

I am so sorry for disturbıng all of you because I am ıntern at company and need to learn torque efficently .You are rıght I also realized that I am disturbing you:( Forgive me:) Because I am so flurried to learn torque. As you reaılze I want to learn this wholeheartedly:) anyway, please forgıve me, deal?

#14
07/06/2009 (8:06 am)
Dear frıend,

actually, I am not aware of whether ıt has realtıonshıp betwwen them because someone saıd me to send message here, because of this fact, ı sent you message about this ıssue.
#15
07/06/2009 (9:22 am)
@Nazi f be rat,
post your questions Here
#16
07/06/2009 (9:26 am)
I want to apologize to Ben for NAzif. Nazif, You need to answer 2 questions, or you will pretty much find yourself being ignored for spamming and generally ignored.

Question 1: What version of torque are you using, TGE, TGEA, T3D?

Question 2: What is your issue? Create 1 thread post, and stick to it. If you dont immediatly get a response, relax, it was 4th of July weekend in the USA where most forum members here reside. Its a major holiday where we unplug. We will try to help where we can. Answer question 1 first.
#17
07/06/2009 (10:58 am)
Edward ,please dont apologize to ben for me because I apologized ok? As I mentioned before, I am beginner and I dont know exactly what to do to be able to learn torque so that as everyone can, I can do mıstakes . I am saying again, I am so sorry now for disturbing you and askıng ırrelevant questions but I would really need to you. please understand me. This will good lesson to me, no worries at all.

Questıon: I am using, as a company,Torque 3d. but if you tell for torque advanced is also helpful for me.

Ouestion2:they want me to do road and car which meet with road signgoing on the road wıthout collısıon to pavement will pop up my company's websıte on the left sıde of the wındow.

To sum up, If you direct me to correct forum adress, I wıll be pleasure because I couldnt fınd and somone recommended me to post my issue here.

Thanks again,
#18
07/06/2009 (1:21 pm)
@Edward, deepscratch, & Michael: Haha thanks for the crowd control ^_^

@Nazif: I'm not sure that I (nor anyone) completely understands your problem, so it's hard to help. Not to mention it has nothing to do with this resource. As others said, I would recommend posting in one of the forums and try to clearly articulate your problem. Try drawing some diagrams and let everyone know what code you have tried and what the specific problems are. People are more likely to help debug code rather than solve the whole problem for you. Try breaking the problem down into several tiny pieces.
#19
07/07/2009 (4:01 pm)
The possibilities can't wait.
#20
07/07/2009 (8:53 pm)
Ben cool stuff, thanks for sharing :)

Re:Nazif... he is not a torque owner, so .......
Page «Previous 1 2