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:
Extended ParticleEmitterNode class:
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.
#2
Something else I noticed, was that if I try to move the moving objects in the object editor, the visual effect will snap to the correct location in the instant I try to move it. Its almost as though their movement has some special extra code in it, but I can't find what it is.
08/29/2012 (7:28 am)
Thanks for the response Lukas. I gave it a shot, but the problem remains. A lot of the code in both classes is taken from other examples in the engine. That's why I was using the Parent::setTransform there. Something else I noticed, was that if I try to move the moving objects in the object editor, the visual effect will snap to the correct location in the instant I try to move it. Its almost as though their movement has some special extra code in it, but I can't find what it is.
#3
I would have thought that this would have printed out the same two names. However, the print statement in SpellEffect crashes the game, whereas the EffectType's nodeData's name prints out with no issues. Could it be possible that, somehow, the wrong object pointer is being collected in the constructor?
Any help would be appreciated.
08/29/2012 (9:29 am)
I noticed something interesting today. I tried adding print statements to onTick methods of both classes. The EffectType prints out nodeData's name, and SpellEffect's print out their own name. The nodeData that is used in the EffectType's class is a SpellEffect. I would have thought that this would have printed out the same two names. However, the print statement in SpellEffect crashes the game, whereas the EffectType's nodeData's name prints out with no issues. Could it be possible that, somehow, the wrong object pointer is being collected in the constructor?
Any help would be appreciated.
#4
But it seems like your transform is packaged properly.
But try to explain your system to me.
You got an effecttype object that you mount to a slot on an object, that effecttype object spawns a spelleffect object that emit particles, is that right?
Are you sure the spelleffect is moved with the effecttype?
08/29/2012 (10:15 am)
When something works only when you move the emitter in the editor it is (from experience) almost always because some value isnt networked properly.But it seems like your transform is packaged properly.
But try to explain your system to me.
You got an effecttype object that you mount to a slot on an object, that effecttype object spawns a spelleffect object that emit particles, is that right?
Are you sure the spelleffect is moved with the effecttype?
#5
As for whether or not its moved. The way I've been checking it is going into the object editor and selecting the object. I can see both the boxes indicating the EffectType and the SpellEffect moving downwards.
I didn't think the network code was at fault here, but this does seem like a problem that could be caused by that. Any ideas for how I could test your theory? Thanks.
08/29/2012 (10:43 am)
The way its setup, I create the SpellEffect in the script, and then find it in the constructor of EffectType and assign it to nodeData. The EffectType itself, isn't actually mounted. The SpellEffect is supposed to be mounted to the EffectType which will do the movements. The end goal is to be able to have the EffectType act as an invisible collision object while dragging along a visual effect.As for whether or not its moved. The way I've been checking it is going into the object editor and selecting the object. I can see both the boxes indicating the EffectType and the SpellEffect moving downwards.
I didn't think the network code was at fault here, but this does seem like a problem that could be caused by that. Any ideas for how I could test your theory? Thanks.
#6
I thought that was where you mounted it.
Owell,
This piece of code:
And this piece of code:
nodeData holds the visual effect right? The SpellEffect.
Then you should in EffectType::ProcessTick() do something along this:
08/29/2012 (1:00 pm)
So this piece of code:if (isMounted()) {
MatrixF mat;
mMount.object->getMountTransform( mMount.node, mMount.xfm, &mat );
Parent::setTransform(mat);
Parent::setRenderTransform(mat);Is not in use? It is just a copy paste artefact?I thought that was where you mounted it.
Owell,
This piece of code:
this->setPosition(Point3F(this->getPosition().x, this->getPosition().y, this->getPosition().z - .05));Makes effecttype move downwards as it is supposed to.
And this piece of code:
this->setPosition(Point3F(this->getPosition().x, this->getPosition().y, this->getPosition().z + 0.5));Makes the visual effect move upwards. Just as you said it did, so it seems like the code is working properly.
nodeData holds the visual effect right? The SpellEffect.
Then you should in EffectType::ProcessTick() do something along this:
nodeData->setTransform(getTransform());
#7
You should set RenderTransform aswell to avoid jitter.
08/29/2012 (1:03 pm)
Edit: (I don't bother fixing all the > etc errors right now so editing it like this.You should set RenderTransform aswell to avoid jitter.
nodeData->setRenderTransform(getRenderTransform());
#8
I tried adding in those two addition lines in the processTick method. Unfortunately there was no change.
Just to be clear, my issue is that, a standard ParticleEmitterNode does not follow the position of my EffectType class visually. I can't figure out why the internal setPosition call in the SpellEffect works to move the visual effect, while calling setPosition from an outside class (like I do in EffectType) doesn't.
Anyway, I tried your fix with both the SpellEffect and a standard ParticleEmitterNode. Still neither of the visual effects follow the actual position of the node. The issue still remains.
Any other ideas? This is such a weird problem.
08/29/2012 (1:32 pm)
Yes, that other code is just pasted in from another class I used to copy from. I tried adding in those two addition lines in the processTick method. Unfortunately there was no change.
Just to be clear, my issue is that, a standard ParticleEmitterNode does not follow the position of my EffectType class visually. I can't figure out why the internal setPosition call in the SpellEffect works to move the visual effect, while calling setPosition from an outside class (like I do in EffectType) doesn't.
Anyway, I tried your fix with both the SpellEffect and a standard ParticleEmitterNode. Still neither of the visual effects follow the actual position of the node. The issue still remains.
Any other ideas? This is such a weird problem.
#9
The EffectEmitter class should have a setContainer() function ofc. Something like this:
08/30/2012 (3:05 am)
Well if it's own setposition is working, you could add a EffectEmitter class and in it's ProcessTick do a:setTransform(EffectContainer->getTransform());And the EffectContainer should hold the EffectType that defines the position of the node.
The EffectEmitter class should have a setContainer() function ofc. Something like this:
IMPLEMENT_CO_NETOBJECT_V1(SpellEffect);
void EffectEmitter::processTick(const Move *move){
processMove();
Parent::processTick(move);
}
void EffectEmitter::processMove(){
// Do some check to see if the EffectContainer pointer is still valid
if(EffectContainer)
{
this->setTransform(EffectContainer.getTransform());
}
}
void EffectEmitter::cast(Point3F castPoint){
initializing = true;
}
// container should be a pointer.
void EffectEmitter::setContainer(EffectType *container){
EffectContainer = container;
}
EffectEmitter::EffectEmitter() : ParticleEmitterNode(){
initializing = false;
}
EffectEmitter::~EffectEmitter(){
}
#10
Although, it seems weird that you have to do it like this. One thing:
Maybe you are only doing the setPosition() client side and therefore it isn't getting networked properly.
Or more likely, is nodeData a pointer? If not it is referencing to a copy of the node instead of the actual node (afaik)
08/30/2012 (3:12 am)
^ Yes that was copy pasted from spelleffectAlthough, it seems weird that you have to do it like this. One thing:
Maybe you are only doing the setPosition() client side and therefore it isn't getting networked properly.
Or more likely, is nodeData a pointer? If not it is referencing to a copy of the node instead of the actual node (afaik)
#11
Spell Effect Include Statements:
EffectType Include Statements:
Can you help me out here? Thanks.
08/31/2012 (10:00 am)
Ok, so I'm trying to get your fix working but I'm having issues with header files. It seems they have created an infinite recursive loop between the two headers. I tried using #ifndef and #ifdef to fix it but I can't figure it out.Spell Effect Include Statements:
#include <string> #include "console/simObject.h" #include "T3D/fx/particleEmitterNode.h" #include "T3D/shapeBase.h" #include "T3D/EffectType.h" using namespace std; class SpellEffect : public ParticleEmitterNode
EffectType Include Statements:
#include <string> #include "console/simObject.h" #include "T3D/shapeBase.h" #include "T3D/staticShape.h" #include "T3D/SpellEffect.h" using namespace std; struct EffectTypeData: public ShapeBaseData
Can you help me out here? Thanks.
#12
Are you sure you have written:
Also did you check if nodeData is a pointer?
09/01/2012 (9:40 am)
Well obviously, EffectType is including SpellEffect and vice versa.Are you sure you have written:
#ifndef _THEHEADERNAME_H #define _THEHEADERNAME_H //The header #endifIn all your header files?
Also did you check if nodeData is a pointer?
#13
I got it to compile by combining the classes and using a forward declaration line. NodeData is a pointer. Here is the new combined header class. Right now I'm trying to figure out a problem with an access violation when I try to set the container with the setContainer method.
09/01/2012 (9:43 am)
Hi Lukas,I got it to compile by combining the classes and using a forward declaration line. NodeData is a pointer. Here is the new combined header class. Right now I'm trying to figure out a problem with an access violation when I try to set the container with the setContainer method.
#include <string>
#include "console/simObject.h"
#include "T3D/shapeBase.h"
#include "T3D/staticShape.h"
#include "T3D/fx/particleEmitterNode.h"
using namespace std;
struct EffectTypeData: public ShapeBaseData {
typedef ShapeBaseData Parent;
public:
EffectTypeData();
bool noIndividualDamage;
S32 dynamicTypeField;
bool isShielded;
F32 energyPerDamagePoint;
//
DECLARE_CONOBJECT(EffectTypeData);
static void initPersistFields();
virtual void packData(BitStream* stream);
virtual void unpackData(BitStream* stream);
};
class SpellEffect;
class EffectType : public ShapeBase
{
typedef ShapeBase Parent;
DECLARE_CONOBJECT(EffectType);
bool mPowered;
void onUnmount(ShapeBase* obj,S32 node);
protected:
enum MaskBits {
PositionMask = Parent::NextFreeMask,
NextFreeMask = Parent::NextFreeMask << 1
};
public:
EffectTypeData* mDataBlock;
SpellEffect* nodeData;
bool onAdd();
void onRemove();
bool onNewDataBlock(GameBaseData *dptr, bool reload);
void processTick(const Move *move);
void interpolateTick(F32 delta);
void setTransform(const MatrixF &mat);
U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream);
void unpackUpdate(NetConnection *conn, BitStream *stream);
void setPowered(bool power) {mPowered = power;}
bool isPowered() {return(mPowered);}
static void initPersistFields();
void onCollision(SceneObject *object, const VectorF &vec);
EffectType();
~EffectType();
};
class SpellEffect : public ParticleEmitterNode
{
//void onHit(ShapeBase);
typedef ParticleEmitterNode Parent;
DECLARE_CONOBJECT(SpellEffect);
bool initializing;
ShapeBase colObj;
EffectType* myContainer;
void processTick(const Move* move);
void cast(Point3F castPoint);
void setContainer(EffectType* container);
void processMove();
SpellEffect();
~SpellEffect();
};
#14
Do you send it like:
SpellEffect.setContainer(&container);
AFAIK if you forget the & then it will give you an error.
09/01/2012 (10:18 am)
How do you send the EffectType?Do you send it like:
SpellEffect.setContainer(&container);
AFAIK if you forget the & then it will give you an error.
#15
in the onAdd function in EffectType. nodeData has been changed to a SpellEffect.
Changing this to &this makes it so it won't compile.
This is the set Container method
Where myContainer is an EffectType*.
09/01/2012 (10:30 am)
I usenodeData->setContainer(this);
in the onAdd function in EffectType. nodeData has been changed to a SpellEffect.
Changing this to &this makes it so it won't compile.
This is the set Container method
void SpellEffect::setContainer(EffectType *container){
this->myContainer = container;
}Where myContainer is an EffectType*.
#16
Hmm I can't really see whats wrong then.
You can make it a pointer to a pointer, but not sure what that would help.
Will do some research and return to you on this.
Edit:
You can try passing it as a reference
src
09/01/2012 (11:16 am)
You are right, this is a pointer to the instance, not the instance itself my bad.Hmm I can't really see whats wrong then.
You can make it a pointer to a pointer, but not sure what that would help.
Will do some research and return to you on this.
Edit:
You can try passing it as a reference
src
//function prototype
void func(int*& rpInt);
int main()
{
int nvar=2;
int* pvar=&nvar;
func(pvar);
....
return 0;
}
void func(int*& rpInt)
{
//Modify what rpInt and pvar is pointing to, to g_One
rpInt=&g_One;
//You can also allocate memory, depending on your requirements
rpInt=new int;
//Modify the variable rpInt points to
*rpInt=3;
}
#17
I tried using the passing it has a reference, but I still get an access violation. Also, I can't apply * or & to the this keyword.
I see in your prototype, you have int* pvar = &nvar;. But I can't actually set that with the this keyword (at least not that I know of).
Am I missing something here? Or is this function designed to go into the EffectType class?
09/01/2012 (12:12 pm)
Hmmm. Ok, I'm a little lost here.I tried using the passing it has a reference, but I still get an access violation. Also, I can't apply * or & to the this keyword.
I see in your prototype, you have int* pvar = &nvar;. But I can't actually set that with the this keyword (at least not that I know of).
Am I missing something here? Or is this function designed to go into the EffectType class?
#18
I have no idea why it behaves as it does.. But best guess is to go back to the simples solution, passing this as a pointer and setting myContainer to that pointer.
What was the error message when your code looked like this:
09/01/2012 (12:28 pm)
It was just a long shot.I have no idea why it behaves as it does.. But best guess is to go back to the simples solution, passing this as a pointer and setting myContainer to that pointer.
What was the error message when your code looked like this:
// container should be a pointer.
void EffectEmitter::setContainer(EffectType *container){
EffectContainer = container;
}
nodeData->setContainer(this);
#19
09/01/2012 (12:31 pm)
It was an access violation error. When I debug it, it happens right in the middle of the setContainer method.
#20
09/01/2012 (1:26 pm)
Well access violation usually means that you are using a bad pointer. Can you set a breakpoint and check what the value of container is in the setContainer function?
Torque Owner Lukas Joergensen
WinterLeaf Entertainment