Game Development Community

Particles not following particle node.

by Patrick Webber · in Torque 3D Professional · 08/28/2012 (5:19 pm) · 27 replies

Hi, I've been playing with this problem for quite a while now, and quite honestly I have no idea what might be causing it. I've created a class that extends ShapeBase to use as a collision object that has a Particle Emitter Node mounted to it. The ShapeBase class moves downwards by 0.05 every tick.

Here's the first problem, when I go into the editor in game, I can select and watch the objects move downwards in perfect harmony. However, the actual visual particle effect remains where the object was spawned.

Now another interesting part. I created a class extending a particle emitter node that moves itself up 0.5 every tick. When I add that to the game, it moves along with the visual particle effects. When I mount the extended particle emitter node, and watch it in the editor the ShapeBase and extended particle emitter both move down. However, the visual particle effect moves up into the sky.

Is there something extra special happening when I call the position update from inside the class? Is this some sort of problem that is well known? I've searched the forums repeatedly to try and find something but I haven't had any luck. Does any one have any suggestions or advice?

Extended ShapeBase Class:
//IMPLEMENT_CONOBJECT(EffectType);
IMPLEMENT_CO_NETOBJECT_V1(EffectType);
IMPLEMENT_CO_DATABLOCK_V1(EffectTypeData);
static const U32 sgAllowedDynamicTypes = 0xffffff;

EffectTypeData::EffectTypeData()
{
   dynamicTypeField     = 0;

   shadowEnable = true;

   noIndividualDamage = false;
}

void EffectTypeData::initPersistFields()
{
   addField("noIndividualDamage",   TypeBool, Offset(noIndividualDamage,   StaticShapeData), "Deprecatednn @internal");
   addField("dynamicType",          TypeS32,  Offset(dynamicTypeField,     StaticShapeData), 
      "@brief An integer value which, if speficied, is added to the value retured by getType().nn"
      "This allows you to extend the type mask for a StaticShape that uses this datablock.  Type masks "
      "are used for container queries, etc.");

   Parent::initPersistFields();
}

void EffectTypeData::packData(BitStream* stream)
{
   Parent::packData(stream);
   stream->writeFlag(noIndividualDamage);
   stream->write(dynamicTypeField);
}

void EffectTypeData::unpackData(BitStream* stream)
{
   Parent::unpackData(stream);
   noIndividualDamage = stream->readFlag();
   stream->read(&dynamicTypeField);
}
//----EffectType begin----
void EffectType::initPersistFields()
{
   Parent::initPersistFields();
}

bool EffectType::onAdd()
{
   if(!Parent::onAdd() || !mDataBlock)
      return false;

   // We need to modify our type mask based on what our datablock says...
   mTypeMask |= (mDataBlock->dynamicTypeField & sgAllowedDynamicTypes);

   addToScene();
 
   if (isServerObject())
      scriptOnAdd();

   nodeData->addToScene();
   nodeData->setPosition(this->getPosition());
   


   return true;
}

bool EffectType::onNewDataBlock(GameBaseData* dptr, bool reload)
{
   mDataBlock = dynamic_cast<EffectTypeData*>(dptr);
   if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload))
      return false;

   scriptOnNewDataBlock();
   return true;
}

void EffectType::onRemove()
{
   scriptOnRemove();
   removeFromScene();
   Parent::onRemove();
}


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

void EffectType::onCollision(SceneObject *object, const VectorF &vec){
	Con::warnf("hit");
}

void EffectType::processTick(const Move* move)
{
   Parent::processTick(move);
   // Image Triggers
   if (move && mDamageState == Enabled) {
      setImageTriggerState(0,move->trigger[0]);
      setImageTriggerState(1,move->trigger[1]);
   }

   this->setPosition(Point3F(this->getPosition().x, this->getPosition().y, this->getPosition().z - .05));

   if (isMounted()) {
      MatrixF mat;
      mMount.object->getMountTransform( mMount.node, mMount.xfm, &mat );
      Parent::setTransform(mat);
      Parent::setRenderTransform(mat);
   }
}

void EffectType::interpolateTick(F32 delta)
{
   if (isMounted()) {
      MatrixF mat;
      mMount.object->getRenderMountTransform( delta, mMount.node, mMount.xfm, &mat );
      Parent::setRenderTransform(mat);
   }
}

void EffectType::setTransform(const MatrixF& mat)
{
   Parent::setTransform(mat);
   setMaskBits(PositionMask);
}

void EffectType::onUnmount(ShapeBase*,S32)
{
   // Make sure the client get's the final server pos.
   setMaskBits(PositionMask);
}


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

U32 EffectType::packUpdate(NetConnection *connection, U32 mask, BitStream *bstream)
{
   U32 retMask = Parent::packUpdate(connection,mask,bstream);
   if (bstream->writeFlag(mask & PositionMask | ExtendedInfoMask))
   {

      // Write the transform (do _not_ use writeAffineTransform.  Since this is a static
      //  object, the transform must be RIGHT THE *&)*$&^ ON or it will goof up the
      //  synchronization between the client and the server.
      mathWrite(*bstream,mObjToWorld);
      mathWrite(*bstream, mObjScale);
   }

   // powered?
   bstream->writeFlag(mPowered);

   if (mLightPlugin) 
   {
      retMask |= mLightPlugin->packUpdate(this, ExtendedInfoMask, connection, mask, bstream);
   }

   return retMask;
}

void EffectType::unpackUpdate(NetConnection *connection, BitStream *bstream)
{
   Parent::unpackUpdate(connection,bstream);
   if (bstream->readFlag())
   {
      MatrixF mat;
      mathRead(*bstream,&mat);
      Parent::setTransform(mat);
      Parent::setRenderTransform(mat);

      VectorF scale;
      mathRead(*bstream, &scale);
      setScale(scale);
   }

   // powered?
   mPowered = bstream->readFlag();

   if (mLightPlugin)
   {
      mLightPlugin->unpackUpdate(this, connection, bstream);
   }
}

EffectType::EffectType() : ShapeBase(){
	mTypeMask |= StaticShapeObjectType | StaticObjectType;
	mDataBlock = 0;
	nodeData = dynamic_cast<ParticleEmitterNode*>(Sim::findObject("FireEffectNode"));
	this->mountObject(nodeData, 0);
}

EffectType::~EffectType(){
	
}

Extended ParticleEmitterNode class:
IMPLEMENT_CO_NETOBJECT_V1(SpellEffect);

void SpellEffect::processTick(const Move *move){
	processMove();
	Parent::processTick(move);
}

void SpellEffect::processMove(){
	this->setPosition(Point3F(this->getPosition().x, this->getPosition().y, this->getPosition().z + 0.5));
}

void SpellEffect::cast(Point3F castPoint){
	initializing = true;
}

SpellEffect::SpellEffect() : ParticleEmitterNode(){
	initializing = false;
}

SpellEffect::~SpellEffect(){
}

About the author

I'm a computer science major at Stony Brook University, and have been working with the Torque Game Engine for a number of years.

Page«First 1 2 Next»
#21
09/01/2012 (5:40 pm)
I get

container = 0x00189f18

for the value at the break point.

Here's the function.

void SpellEffect::setContainer(EffectType** container){    
	this->myContainer = *container;
}

And this is the error:

Unhandled exception at 0x109072d4 (Project M_DEBUG.dll) in Project M_DEBUG.exe: 0xC0000005: Access violation writing location 0x00000ac0.
#22
09/01/2012 (7:24 pm)
I figured out my problem here. I had forgotten to update the script code XP.

I'm having a new issue with this now. The game will crash in the processMove method now. It says there is an access violation with a memory address of 0xcdcdcdcd. I see that for some reason, my setContainer method is being called twice, but neither time is the data sent 0xcdcdcdcd.

Any ideas as to what's going on here?
#23
09/01/2012 (8:47 pm)
The error is probably because setcontainer sets mycontainer to a pointer to a pointer so you would have to access it as:
*this->myContainer
It is being called twice because it is run on both server and client.

Edit:
Quote:0xCDCDCDCD: Used by Microsoft's C++ debugging runtime library to mark uninitialised heap memory
Thats where the CDCDCDCD address value comes from.
#24
09/02/2012 (8:43 am)
I tried changing it to *this, but I still have the same issue.

When I go through the debug, its rather interesting.

First setContainer is called twice, the first time it passes through myContainer is set to cdcdcdcd. The second time, its something else. Then, when it tries to process move, myContainer, is set back to cdcdcdcd.

Neither of the setContainer methods are leaving myContainer with the value of cdcdcdcd. I can't figure out why its being changed later on. Its almost like its being set somewhere else...but there's only one method to set the container value.

Edit:
It looks like the first object created is fine, but once that second one is created the problems start happening.

Edit (again):
I got it to stop crashing, but the fix doesn't work. The second created object, is never actually being updated. I'm guessing that's the client one since the boxes are moving without the visual effects?
#25
09/02/2012 (9:53 am)
Yes *this was rambling anyway missed that you actually already sent the pointer-to-pointer and 'depointerized' it. So it was correct with your original solution.
The reason that one is working and another one isn't might be because that setContainer is only run on either server or client.
Try setting a breakpoint in setContainer and check if it is run on both the server and the client.
#26
09/02/2012 (12:14 pm)
Ok, so I think I'm getting close to getting this working. The server version seems to be working. I think what the issue might is me trying to find the object that I created in the script. I think it's only finding the object on one side.

Is there a way to create my SpellEffect class manually on both the server and client side in C++? Or is there someway to set up my scripts to tell it which one to grab for which side?

Edit: I got it working! The issue was hidden in that each wasn't finding the correct sides object. Thanks so much for all your help on this matter!
#27
09/02/2012 (12:47 pm)
Glad you got it working! Good luck with your game :)
Page«First 1 2 Next»