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«First 1 2 Next»
#21
07/08/2009 (1:24 pm)
I already let the appropriate webmasters know about Mr. Berat
#22
07/14/2009 (12:50 pm)
Looks cool, doesn't look like it is network friendly unless you pass the commands from the server. FYI.
#23
12/03/2009 (1:55 am)
I'm a student @ Dakota State Univeristy, and i'm in a game design class with Dr. Jeff Howard. I'm trying to implement this resource and i'm running into troubles when i try to rebuild the solution. I'm very much a beginner at programming, and i'm wondering anyone can help me.

Basically, i can't find where i'm missing a ';', and i'm starting to think it might not be the problem.

here's the error messages i get:

1>c:\Torque\TGEA_1_8_1\engine\source\T3D/shapeBase.h(557) : error C2143: syntax error : missing ';' before '*'
1>c:\Torque\TGEA_1_8_1\engine\source\T3D/shapeBase.h(557) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>c:\Torque\TGEA_1_8_1\engine\source\T3D/shapeBase.h(557) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>c:\Torque\TGEA_1_8_1\engine\source\T3D/shapeBase.h(602) : error C2061: syntax error : identifier 'ParticleEmitterNode'

should i post my source code for shapeBase.h? or if anyone is willing to help, i can e-mail it to them.


I'd greatly appreciate any and all help!
Thanks,

Cody Thaler
#24
12/03/2009 (3:08 am)
Hi Cody,

Go ahead and send along your shapeBase.h file to ben@urbanbrainstudios.com and I'll take a look.
#25
12/03/2009 (3:49 pm)
i sent you a copy of the error messages, and also the shapeBase.h file. if there's anything else you might need, i'll be checking this thread often. It's CRUNCH time in my game design class. lol
#26
12/04/2009 (1:23 am)
I compared your shapeBase.h file with mine and the main difference is that you have at line 6:
#include "T3D/fx/particleEmitter.h"
class ParticleEmitter;
and at line 553:
class mountedParticle
{
	public:
		U32					 nodeNumber;
		ParticleEmitterNode*	       particleEmitter;
};

It looks like this is from a different resource that I don't have implemented. I think the compile error is because the "particleEmitterNode" data type is not defined. Maybe at the top, you meant to include "T3D/fx/particleEmitterNode.h" instead of or in addition to "T3D/fx/particleEmitter.h". I hope this helps and good luck with your game!
#27
12/05/2009 (4:25 pm)
Well, i figured out that the source code i was working with was being modified by another group in my class, so basically stuff from your resource and the resource and the other group was trying to implement interfered with each other. i'm going to try putting the necessary changes with your resource again, i'll post again if there's a problem.

Thanks again!

Cody Thaler
#28
12/05/2009 (4:54 pm)
Build worked! no more problems. Thanks again for taking a look at my code!

-Cody
#29
12/05/2009 (5:58 pm)
scriptsAndAssets/server/scripts/inventory.cs (134): Unable to find object: 'SwordAmmo' attempting to call function 'getName'


i know it's something simple, and i feel like an idiot asking this but, how do i fix this? i basically copied the torquescript example you have in this thread.
#30
12/05/2009 (9:06 pm)
In addition to the SwordImage Shape datablock shown in the resource, you will also need to add a Sword and SwordAmmo Item datablock. Here is an example that you can use:
datablock ItemData(Sword)
{
   // Mission editor category
   category = "Weapon";

   // Hook into Item Weapon class hierarchy. The weapon namespace
   // provides common weapon handling functions in addition to hooks
   // into the inventory system.
   className = "Weapon";

   // Basic Item properties
   shapeFile = "~/data/shapes/weapons/sword/rune_blade01.dts";
   mass = 1;
   elasticity = 0.2;
   friction = 0.6;
   emap = true;

   // Dynamic properties defined by the scripts
   pickUpName = "a sword";
   image = SwordImage;

   itemType= "melee";
};

datablock ItemData(SwordAmmo)
{
   // Mission editor category
   category = "Ammo";

   // Add the Ammo namespace as a parent.  The ammo namespace provides
   // common ammo related functions and hooks into the inventory system.
   className = "Ammo";

   // Basic Item properties
   shapeFile = "~/data/shapes/particles/EnergySphere.dts";
   mass = 1;
   gravityMod = 0;
   elasticity = 0.2;
   friction = 0.6;
   
   static = false;

   // Dynamic properties defined by the scripts
   pickUpName = "sword ammo";
   maxInventory = 1;

   image = SwordImage;
};

These are just from the crossbow example that comes with Torque. You'll have to use your own DTS file for the sword itself and the ammo DTS can be anything because it is never displayed.
#31
12/06/2009 (8:08 pm)
i have the flaming sword working! i've been working with my professor to get it working, and now my question is, how would i make different states? My final goal of this resource is to have a sword that will have 4 interchangeable states. Ice, Fire, earth, and air.
#32
12/06/2009 (8:40 pm)
This is Cody Thaler by the way. i just typed this in through my professors profile by mistake
#33
12/06/2009 (11:50 pm)
Unfortunately I can't think of an easy way to add multiple states because the "emitter on" state only happens in response to the sword gaining ammo. Maybe there is a way to trigger different states depending on what type of ammo is picked up?

In the mean time, I would just make four different sword datablocks, each with different emitter effects and switch them out depending on which effect you want. Let me know how this works out because we are thinking of doing something similar.
Page«First 1 2 Next»