Changing particle movement code?
by Tyler Slabinski · in Torque Game Builder · 08/14/2010 (3:47 am) · 33 replies
I've been using a formula in TorqueScript to create a gravitational 'force' towards an object. This 'force' currently only works on objects in the scene, but I want to expand it a bit more.
I am not good at changing the engine code, which is what I am trying to learn at the moment. I am trying to make certain particles (if they have an option turned on) to be attracted to the objects just like normal. I know C++, but I don't know how the particles work in the source code. I know I am being vague right now, but I am trying to think of a way to explain what I need. This is my script for the gravity:
I am not good at changing the engine code, which is what I am trying to learn at the moment. I am trying to make certain particles (if they have an option turned on) to be attracted to the objects just like normal. I know C++, but I don't know how the particles work in the source code. I know I am being vague right now, but I am trying to think of a way to explain what I need. This is my script for the gravity:
new SimSet(StarList);
//For each star in the scene, add it to the SimSet created above.
function starClass::onLevelLoaded(%this, %scenegraph)
{
StarList.add(%this);
}
//Update a callback every frame for the planet (%this)
function planetClass::onLevelLoaded(%this, %scenegraph)
{
%this.enableUpdateCallBack();
}
function planetClass::onUpdate(%this)
{
%cStars = StarList.getCount();
for( %i = 0; %i < %cStars; %i++ )
{
%star = StarList.getObject(%i);
%pastX = %this.getLinearVelocityX();
%pastY = %this.getLinearVelocityY();
%distance = t2dVectorDistance(%star.getPosition(), %this.getPosition());
%x = %star.getPositionX() - %this.getPositionX();
%y = %star.getPositionY() - %this.getPositionY();
%sum = mAbs(%x) + mAbs(%y);
%powerX = castGravityX(%sum, %x, %distance);
%powerY = castGravityY(%sum, %y, %distance);
%this.setLinearVelocity(%powerX + %pastX, %powerY + %pastY);
}
}
//Get the gravitational force in the X direction.
function castGravityX(%sum, %x, %distance)
{
%x = %x / %sum;
return (%x / %distance) * 300;
}
//Get the gravitation force in the Y direction.
function castGravityY(%sum, %y, %distance)
{
%y = %y / %sum;
return (%y / %distance) * 300;
}About the author
Working on prototype.
#2
I would need to add a flag for the emitter, to toggle whether the particle can be attracted or not. Right?
I am guessing I would do that in the .h file? Just guessing here, I would put it under the private section as something like mGrav:
I am lost after this. I understand what I need to do, but I don't really know how to implement it.
08/14/2010 (4:29 pm)
Okay, I am guessing by the comments in that function, I should be looking at this:// **********************************************************************************************************************
// Calculate New Velocity...
// **********************************************************************************************************************
// Only Calculate Velocity if not a Single Particle.
if ( !mSingleParticle )
{
// Calculate Random Motion (if we've got any).
if ( mNotZero( pParticleNode->mRenderRandomMotion ) )
{
// Fetch Random Motion.
F32 randomMotion = pParticleNode->mRenderRandomMotion * 0.5f;
// Add Time-Integrated Random-Motion into Velocity.
pParticleNode->mVelocity += elapsedTime * t2dVector( mGetT2DRandomF(-randomMotion, randomMotion), mGetT2DRandomF(-randomMotion, randomMotion) );
}
// Add Time-Integrated Fixed-Force into Velocity ( if we've got any ).
if ( mNotZero( pParticleNode->mRenderFixedForce ) )
pParticleNode->mVelocity += (mFixedForceDirection * pParticleNode->mRenderFixedForce * elapsedTime);
// Suppress Movement?
if ( !pParticleNode->mSuppressMovement )
{
// No, so adjust Particle Position.
pParticleNode->mPosition += (pParticleNode->mVelocity * pParticleNode->mRenderSpeed * elapsedTime);
}
}I would need to add a flag for the emitter, to toggle whether the particle can be attracted or not. Right?
I am guessing I would do that in the .h file? Just guessing here, I would put it under the private section as something like mGrav:
private:
t2dParticleEffect* pParentEffectObject;
StringTableEntry mEmitterName;
t2dVector mGlobalClipBoundary[4];
F32 mTimeSinceLastGeneration;
bool mPauseEmitter;
bool mVisible;
bool mGrav;I am lost after this. I understand what I need to do, but I don't really know how to implement it.
#3
08/14/2010 (4:38 pm)
My weekend is kind-of shot, but I'll snag a moment to give you some code that'll get you giong sometime in the next day or two. Later!
#4
08/14/2010 (8:53 pm)
Thanks! I am trying to learn how the engine works a bit more, but it will help alot.
#5
I'm also going to assume that you want this to work at the particle emitter level, not the particle effect level. You could easily add the bodies to the t2dParticleEffect instead and have all of the t2dParticleEmitters look at that list instead.
In addition to your new mGrav variable, you'll want to add a list of bodies to the t2dParticleEmitter.h file:
Down under the "Set Properties" comment, add the following:
In t2dParticleEmtiter.cc, look for the constructor and initialize the gravity variable:
Then you'll want to add the following somewhere in the same file:
and the associated console methods:
Down in t2dParticleEmitter::integrateParticle, go to the "Calculate New Velocity" section and add the following inside the !mSingleParticle if-statement:
In your TorqueScript, you'll need to write code like the following:
NOTICE TO EVERYBODY: Tyler's gravity code isn't for everybody. Also, the speed on the particle effect is 0.05! It's so tiny because the gravity model doesn't really scale with time. I'll probably play with a modified gravity model and will post it here later.
I'm really excited right now because this is the first time I've done a complex change to the engine that actually only took 10 minutes. I'm always, "I can do that in 10 minutes" and then I'm done an hour or two later. Plus, it's surprisingly very fast.
You can see the higher resolution version on YouTube.
You'll notice that I've not written save code, the ability to get values, etc. It shouldn't be too hard to add these if you need them. An exercise for the reader. *HEHE!*
08/15/2010 (4:54 am)
Since you're going to have to write TorqueScript to get this all to work, I'm not going to focus too much on getting this to work in the level editor.I'm also going to assume that you want this to work at the particle emitter level, not the particle effect level. You could easily add the bodies to the t2dParticleEffect instead and have all of the t2dParticleEmitters look at that list instead.
In addition to your new mGrav variable, you'll want to add a list of bodies to the t2dParticleEmitter.h file:
bool mGrav; Vector<t2dVector> mGravBodies;
Down under the "Set Properties" comment, add the following:
void setGravity( bool status ); void clearBodies(); void addBody( const t2dVector& pos );
In t2dParticleEmtiter.cc, look for the constructor and initialize the gravity variable:
t2dParticleEmitter::t2dParticleEmitter() : ...
mVisible( true ),
mGrav( false ),
...Then you'll want to add the following somewhere in the same file:
void t2dParticleEmitter::setGravity( bool status )
{
mGrav = status;
}
void t2dParticleEmitter::clearBodies()
{
mGravBodies.clear();
}
void t2dParticleEmitter::addBody( const t2dVector& pos )
{
mGravBodies.push_back( pos );
}and the associated console methods:
ConsoleMethod( t2dParticleEmitter, setGravity, void, 3, 3, "(status)" )
{
object->setGravity( dAtob( argv[2] ) );
}
ConsoleMethod( t2dParticleEmitter, clearBodies, void, 2, 2, "()" )
{
object->clearBodies();
}
ConsoleMethod( t2dParticleEmitter, addBody, void, 3, 3, "(<x y>)" )
{
t2dVector pos;
dSscanf( argv[2], "%g %g", &pos.mX, &pos.mY );
object->addBody( pos );
}Down in t2dParticleEmitter::integrateParticle, go to the "Calculate New Velocity" section and add the following inside the !mSingleParticle if-statement:
if( mGrav )
{
// Based upon your current gravity model.
for( S32 i = 0; i < mGravBodies.size(); ++i )
{
const t2dVector& star = mGravBodies[i];
t2dVector dir = star - pParticleNode->mPosition;
F32 distance = dir.len();
if( distance == 0 ) continue;
F32 sum = mAbs( dir.mX ) + mAbs( dir.mY );
t2dVector deltaVel = dir / sum / distance * 300;
pParticleNode->mVelocity += deltaVel;
}
}In your TorqueScript, you'll need to write code like the following:
%pe = new t2dParticleEffect()
{
scenegraph = <YourSceneGraph>;
effectFile = "game/data/particles/whateverFile.eff";
position = "0 0";
size = "15 15";
Layer = "0";
};
%pee = %pe.getEmitterObject( 0 );
%pee.setGravity( true );
%pee.addBody( "0 -25" );
%pee.addBody( "25 0" );
%pe.playEffect();NOTICE TO EVERYBODY: Tyler's gravity code isn't for everybody. Also, the speed on the particle effect is 0.05! It's so tiny because the gravity model doesn't really scale with time. I'll probably play with a modified gravity model and will post it here later.
I'm really excited right now because this is the first time I've done a complex change to the engine that actually only took 10 minutes. I'm always, "I can do that in 10 minutes" and then I'm done an hour or two later. Plus, it's surprisingly very fast.
You can see the higher resolution version on YouTube.
You'll notice that I've not written save code, the ability to get values, etc. It shouldn't be too hard to add these if you need them. An exercise for the reader. *HEHE!*
#6
And yea, the particles do go pretty quickly! I was only planning on using a small few particles (30-40 at a time), so that is really cool.
I am using the iT2D engine, so I will see how it runs in that (I think it will be the same), but I will need to wait until the morning.
08/15/2010 (6:00 am)
Holy crap! I didn't expect you to have done it that quickly... Or simply...And yea, the particles do go pretty quickly! I was only planning on using a small few particles (30-40 at a time), so that is really cool.
I am using the iT2D engine, so I will see how it runs in that (I think it will be the same), but I will need to wait until the morning.
#7
08/15/2010 (7:51 am)
I don't know anything about the iT2D baseline. I sure hope that this code will work for you!
#8
08/15/2010 (4:23 pm)
Wow... awesome!
#9
It's like it's ignoring the changes I've made to the header file...
08/15/2010 (8:00 pm)
Well I can't seem to get this to work with TGB, I keep getting 18 compiling errors:t2dParticleEmitter.cc:102: error: class 't2dParticleEmitter' does not have any field named 'mGrav' t2dParticleEmitter.cc: At global scope: t2dParticleEmitter.cc:214: error: no 'void t2dParticleEmitter::setGravity(bool)' member function declared in class 't2dParticleEmitter' t2dParticleEmitter.cc:216: error: 'mGrav' was not declared in this scope t2dParticleEmitter.cc: At global scope: t2dParticleEmitter.cc:218: error: no 'void t2dParticleEmitter::clearBodies()' member function declared in class 't2dParticleEmitter' t2dParticleEmitter.cc:220: error: 'mGravBodies' was not declared in this scope t2dParticleEmitter.cc: At global scope: t2dParticleEmitter.cc:222: error: no 'void t2dParticleEmitter::addBody(const t2dVector&)' member function declared in class 't2dParticleEmitter' t2dParticleEmitter.cc:224: error: 'mGravBodies' was not declared in this scope t2dParticleEmitter.cc:2665: error: 'mGrav' was not declared in this scope t2dParticleEmitter.cc:2668: error: 'mGravBodies' was not declared in this scope t2dParticleEmitter.cc: In constructor 't2dParticleEmitter::t2dParticleEmitter()': t2dParticleEmitter.cc:102: error: class 't2dParticleEmitter' does not have any field named 'mGrav' t2dParticleEmitter.cc: At global scope: t2dParticleEmitter.cc:214: error: no 'void t2dParticleEmitter::setGravity(bool)' member function declared in class 't2dParticleEmitter' t2dParticleEmitter.cc:216: error: 'mGrav' was not declared in this scope t2dParticleEmitter.cc:218: error: no 'void t2dParticleEmitter::clearBodies()' member function declared in class 't2dParticleEmitter' t2dParticleEmitter.cc: In member function 'void t2dParticleEmitter::clearBodies()': t2dParticleEmitter.cc:220: error: 'mGravBodies' was not declared in this scope t2dParticleEmitter.cc:222: error: no 'void t2dParticleEmitter::addBody(const t2dVector&)' member function declared in class 't2dParticleEmitter' t2dParticleEmitter.cc: In member function 'void t2dParticleEmitter::addBody(const t2dVector&)': t2dParticleEmitter.cc:224: error: 'mGravBodies' was not declared in this scope t2dParticleEmitter.cc: In member function 'void t2dParticleEmitter::integrateParticle(t2dParticleEmitter::tParticleNode*, F32, F32)': t2dParticleEmitter.cc:2665: error: 'mGrav' was not declared in this scope t2dParticleEmitter.cc:2668: error: 'mGravBodies' was not declared in this scope
It's like it's ignoring the changes I've made to the header file...
#10
08/16/2010 (4:27 am)
Make sure you added the new .h changes to be inside the class. I think I put mine right below the mVisible variable. Also make sure you added it to the t2dParticleEmitter and not some other .h file (like the t2dParticleEffect).
#11
08/16/2010 (3:19 pm)
Nevermind, I got it fixed. It was a compiler problem, and I was apparently not overwriting the previous .h file when I was trying to save it.
#12
08/16/2010 (4:36 pm)
Good to hear!
#13
I created a particle effect in the editor, and then I gave it a class name. I then created an onLevelLoaded() function that defined it as %pe, and then pretty much put in the rest of the TS you posted.
Gravity.cs
TestingArena.t2d
No errors in the console, no compiling errors, and nothing that I can see wrong with it.
08/18/2010 (2:22 am)
I can't seem to get the TorqueScript to work right. I can't enable it in the editor, since I can't bring the editor up in the console (which means I can't get the number reference like you did in the video), and I can't seem to get it to load anything ingame.I created a particle effect in the editor, and then I gave it a class name. I then created an onLevelLoaded() function that defined it as %pe, and then pretty much put in the rest of the TS you posted.
Gravity.cs
function PlanetParticle::onLevelLoaded(%this, %scenegraph)
{
%pe = %this;
%pee = %pe.getEmitterObject(0);
%pee.setGravity( true );
%pee.addBody( "0 -25" );
%pee.addBody( "25 0" );
%pe.playEffect();
}TestingArena.t2d
new t2dParticleEffect()
{
effectFile = "~/data/particles/GravityTest.eff.eff";
useEffectCollisions = "0";
effectMode = "INFINITE";
effectTime = "0";
canSaveDynamicFields = "1";
class = "PlanetsParticles";
Position = "31.000 -84.000";
size = "128.000 128.000";
Layer = "31";
CollisionMaxIterations = "1";
mountID = "5";
};No errors in the console, no compiling errors, and nothing that I can see wrong with it.
#14
08/18/2010 (6:01 am)
I think you found it. You gave the particle effect the class name "PlanetsParticles", but your onLevelLoaded is scoped to "PlanetParticle". Hopefully it's as simple as that!
#15
But I think I know what the problem is...
EDIT: Yep, I just learned that I need to set my target in Xcode... Now it's working (albeit too powerfully). I just need to lower the number.
Thanks so much!
08/18/2010 (2:45 pm)
You know, that's the sixth time in this one project that I made that mistake. But now I am getting some errors saying "Unknown command: setGravity()".But I think I know what the problem is...
EDIT: Yep, I just learned that I need to set my target in Xcode... Now it's working (albeit too powerfully). I just need to lower the number.
Thanks so much!
#16
Change the mGravBodies definition to
Modify the addBody code to
Finally, change the movement code to
The gravity force is typically moved along the line between the two bodies proportional to the product of the masses over the radius squared. ( G = k*m1*m2/r^2 ). Tyler's code had it proportional to a fixed mass over the Manhattan distance. ( G = 300 / ManhattanDistance ). This new code makes it proportional to the mass of the object over the radius to the 1.1 power. ( G = m1 / r^1.1 ). Also, with this new code, you can have repulsers, simply by giving a body a negative mass!
08/18/2010 (5:08 pm)
I did make some recent changes for myself.Change the mGravBodies definition to
Vector<Point3F> mGravBodies;
Modify the addBody code to
void t2dParticleEmitter::addBody( const Point3F& posAndMass )
{
mGravBodies.push_back( posAndMass );
}
ConsoleMethod( t2dParticleEmitter, addBody, void, 4, 4, "(<x y>, <mass>)" )
{
Point3F pos;
dSscanf( argv[2], "%g %g", &pos.x, &pos.y );
dSscanf( argv[3], "%g", &pos.z );
object->addBody( pos );
}Finally, change the movement code to
if( mGrav )
{
for( S32 i = 0; i < mGravBodies.size(); ++i )
{
const Point3F& star = mGravBodies[i];
t2dVector starPos( star.x, star.y );
F32 starMass = star.z;
t2dVector dir = starPos - pParticleNode->mPosition;
F32 distance = dir.len();
if( distance == 0 ) continue;
if( distance < 1 ) distance = 1;
t2dVector deltaVel = dir / mPow(distance, 2.1f) * starMass;
pParticleNode->mVelocity += deltaVel * elapsedTime;
}
}The gravity force is typically moved along the line between the two bodies proportional to the product of the masses over the radius squared. ( G = k*m1*m2/r^2 ). Tyler's code had it proportional to a fixed mass over the Manhattan distance. ( G = 300 / ManhattanDistance ). This new code makes it proportional to the mass of the object over the radius to the 1.1 power. ( G = m1 / r^1.1 ). Also, with this new code, you can have repulsers, simply by giving a body a negative mass!
#17
Also, is there anything in TorqueScript that needs to be changed? It doesn't look like it, but it seems like the particles are being effected ALOT more slowly then before in my screen.
08/18/2010 (11:30 pm)
Also, if anyone's going to use the additional code above, then remember to change the prototype function in the header file to this:void addBody( const Point3F& pos );
Also, is there anything in TorqueScript that needs to be changed? It doesn't look like it, but it seems like the particles are being effected ALOT more slowly then before in my screen.
#18
I've just been playing with the masses to affect the speed of particles. You can also adjust the speed graph of the particles.
08/18/2010 (11:54 pm)
Doh! Thanks for catching that!I've just been playing with the masses to affect the speed of particles. You can also adjust the speed graph of the particles.
#19
Gravity.cs
But I keep getting the 'wrong number of arguments' error:
I tried replacing the loop with this for testing:
But I get the same error. It doesn't like it when I try to put mass in there.
08/19/2010 (2:54 am)
For some reason the mass won't change. I tried putting this into my script:Gravity.cs
function PlanetsParticles::onLevelLoaded(%this, %scenegraph)
{
%emitter = %this.getEmitterObject(0);
%emitter.setGravity( true );
%cStars = StarList.getCount();
for( %i = 0; %i < %cStars; %i++ )
{
%star = StarList.getObject(%i);
%emitter.addBody(%star.getPosition(), %star.getMass());
}
%this.playEffect();
}But I keep getting the 'wrong number of arguments' error:
t2dParticleEmitter::addBody - wrong number of arguments. usage: (<x y>)
I tried replacing the loop with this for testing:
%emitter.addBody("0 0", 2500);But I get the same error. It doesn't like it when I try to put mass in there.
#20
ConsoleMethod( t2dParticleEmitter, addBody, void, 4, 4, "(<x y>, <mass>)" )
Later!
08/19/2010 (6:02 am)
It looks like you missed the ConsoleMethod signature change:ConsoleMethod( t2dParticleEmitter, addBody, void, 4, 4, "(<x y>, <mass>)" )
Later!
Associate William Lee Sims
Machine Code Games
One easy idea that I might do is pass the gravitational bodies to the t2dParticleEffect and store them in a list there. I'd them modify the t2dParticleEmitter to modify the velocity based upon those bodies.
Just so you know, this may really slow down the particle effects in your game. You might want to keep the number of particles way down (experiementation should help you find this number).
If you need some more guidance on what to add and where, just let me know and I can give you some code to try.
Later!