Tired of looking at the same old Rocket Launcher emitters go off for your grenade launcher? Want to make a cool smoke grenade, but you just can't get around how the emitters work? Well then, This is the guide for you and all your emitter needs and questions.
Purpose of this guide: Well I was tired all right? Tired of looking at the same old stuff plastered on a different model. Everyone seems to be simply copy-pasting the rocket-launcher and gun scripts, changing the names to work with a different model and then posting it as a finished add-on. It's not that those special effects aren't good or anything, it's just you'd figure people would want to make cool weapons and utilities a bit more unique! It's just special effects but if you're making something simple then that's all it will have going for it aside from function.
This will also double as an extra reference guide to help answer the tiny questions some more seasoned scripters might have about these things. There is a Torque Functions guide that tries to explain some of this stuff but fails in many areas, this tutorial will compliment this guide:
http://www-rohan.sdsu.edu/~stewart/GPGT/Appendix%20A%20-%20Quick%20References.pdfI will be covering 5 datablocks in particular: Particles, Emitters, Explosions, Debris, and Projectiles. I will not be covering any vehicles/characterPlayers seeing as I have never really looked at them in detail.
DATABLOCK USAGE:
Even if you "recycle" datablocks, to my past V9 knowledge the script will still load an extra datablock anyways for every extra "same" datablock that is attached to some other datablock. I doubt you will need to use all 2000+ datablocks anyways, I mean come on guys!This was changed or I was wrong. Either way re-using the same datablocks seems to cut down on datablock usage. There are also extra interpolation values you can set for your explosions which, from what I've seen so far, do not increase datablock usage if you re-use datablocks for them. I will be doing further testing on the explosion datablock interpolations to check out how exactly they affect the explosion emitters. (Thanks for bringing this issue up Space Guy)
First, we'll look at how it all gets started with the humble Particle Datablock, an example from a Fireball projectile I made. I will give an explanation of what each line does:
datablock ParticleData(Fireball2TrailParticle)
{
dragCoefficient = 3; //This slows down particles over time. It will be deducted from constantAcceleration. It does NOT affect gravityCoeefficient at all!
gravityCoefficient = -0.0; //A positive value will cause particles to accelerate towards the ground. Likewise, a Negative value causes them to rise.
inheritedVelFactor = 0.15; //How much of the emitting object's Velocity (speed and direction) is given to the particle when it is emitted
constantAcceleration = 0.0; //The opposite of dragCoefficient, it will cause the particle to accelerate over time.
lifetimeMS = 1500; //How long the particle lasts, in Milliseconds. This one will last 1.5 seconds
lifetimeVarianceMS = 800; //This line randomizes the lifetime a bit. In this case it treats the lifetime with +/- 800 milliseconds. So the particle will last from 0.7 seconds to 2.3 seconds.
textureName = "base/data/particles/cloud"; //This is the line that determines which decal is used for the particle. In this case, I picked one of Badspot's originals and probably my favorite, the Cloud. You can also make your own decals by placing them in your packaged .zip folder and then typing in "./decalNameHere" as the value, where decalNameHere is the name of your decal obviously.
spinSpeed = 10.0; //This value causes your particle to spin. A value of 1000 will have it make a Full Revolution every second. **A Negative value gives Clockwise rotation, and a Positive value gives Anticlockwise rotation**.
spinRandomMin = -150.0; //This is the Minimum spin, another randomizing factor. It will deduct a range value to the spinSpeed. So in this case, it's 10-150, giving you -140. So now the spinSpeed will range from -140 to 10. It has to be lower then spinRandomMax otherwise you will get some errors.
spinRandomMax = 150.0; //This is another randomizing factor that adds a range value to your particle's spinSpeed. So now, without accounting for spinRandomMin, our particle will spin at a speed anywhere from 10 to 160 when it's emitted. Adding spinRandomMax, the particle will spin from a Clockwise -140 to anticlockwise 160.
colors[0] = "1.0 0.9 0.8 0.6"; colors[1] = "1.0 0.2 0.0 0.5"; colors[2] = "0.9 0.2 0.0 0.3"; colors[3] = "0.4 0.0 0.0 0.0"; //Your color keys! These mix in with your "sizes" and "times" keys. The maximum amount of keys is 4, so 0 to 3. The scheme for colors goes Red, Green, Blue, and Transparency. The allowed value ranges are between 0 and 1, or 1 and 255. As you can see, my first key here is made to simulate a bright white slightly orange starting color. If all your values are, say, 0.5 for RGB, then you get a grey color. If they're all 1.0, you get a very bright white color, in this case I set the bar at 0.8, then give a hint of orange by mixing red with a little green. Unfortunately, this does not mean putting all values to 0.01 will give you a dark black, instead it will give you a near invisible grey (even with trans set to 1.0). To get a Black color, we use invAlpha below. The color transition from step 0 to step 1 is gradual, which is great because it causes particles to change very smoothly. Furthermore, the speed at which the particle changes color is determined by the times[] steps.
sizes[0] = 2.25; sizes[1] = 3.55; sizes[2] = 2.35; sizes[3] = 1.05; //Size steps, how big your particle is and when. In this case, a value of 1.50 is roughly the size of a 1x1x3 brick. Sizes also change gradually from step to step so it gives you a smooth transition throughout. Of course, how long the rendition lasts is again attributed to your times[] steps, more on those below.
times[0] = 0.0; times[1] = 0.15; times[2] = 0.4; times[3] = 1.0; //These keys determine how long it takes for colour and size steps to jump to the next step. How they work is simple, your first times[] HAS to be 0.0, and your last HAS to be 1.0. At 0.0, the particle's lifetime count is at exactly 0, this is the moment it's emitted. At 1.0, it's the moment when the particle's lifetime runs out. Let's simplify our life by saying our particle's lifetime is 10 seconds, and has a range of 0. The rendition from 0 to 1, that is 0.0 to 0.15, it takes 15% of the particles lifetime (1.5 seconds) to happen. From 1 to 2, which are 0.15 to 0.4, it will take 2.5 seconds, and so on. These guys are extremely important, and I recommend you play around with them and see what happens. You can make very pretty particles if you can master these steps.
useInvAlpha = false; //What it does is it boldens your colors, which is both a blessing and a curse. If you set it to "true" and set your RBG values all to 0.01, you get black particles! You can also make some very nice greys, but the problem is that it messes with your particle's Transparency. Say you want to make some cool fire effects, leading to a black smoke. You need to set your RGB to say, 1.0 0.6 0.0 0.6 to get an Orange-like color as well as semi-transparency. The problem is, because Red is 1.0, the minimum transparency is 1.0 for that color key. So you get this weird thick orange gob particle instead of a pretty transparent orange that overlaps nicely with other particles to make neato fire. The trans "minimum" will be set to your key's highest value.
**This feature also causes some major sorting issues if particles from 2 different emitters or 2 entirely different types of particles overlap (one needs to have invAlpha set to true). The result is a very fast blinking between particles. You may see this in effect if you stack 2 bricks and give the first brick Fire A and the other brick, a black Smoke emitter. It is an engine issue.**
};
Not included are the Animation lines, animateTexture = true or false; and animTexName[0] == textureName.fileType; to [49], because animated particles don't work properly yet. (They only appear animated in single player, or for a host. They stick with the first animation decal if you join a game that has them.). **Animated particles are officially broken, so don't worry about them.**
That's allot, I know, but it becomes pretty easy to get around after a while. Next object of importance, the Emitter:
datablock ParticleEmitterData(Fireball2TrailEmitter)
{
lifetimeMS = 100; //This bit is used in conjunction with the Explosion datablock. It determines how long the emitter lasts before it ceases to emit particles. If you are making, say, a brick emitter, this line is useless because the brick will continue to emit forever regardless. It is also in milliseconds, so in our case the emitter will only live for 0.1 seconds. Useful for stopping special effects in Sub Explosions and in image states.
ejectionPeriodMS = 3; //How much time is spent between each particle emitted. In our case, the emitter spurts out a particle every 0.003 seconds, that's fast! Unfortunately, you can't go any lower then 1, but you will very rarely need to. Another thing, if you put a decimal value it will simply round it off. This shouldn't be an issue to you, but it means that making variable hacks (in combination with other things) to achieve really unique effects is impossible.
periodVarianceMS = 1; //Our first randomizing line for emitters. This guy will add/substract from the ejectionPeriodMS to form a time range in which the particles are emitted. I might add it needs to be lower or equal to your ejectionPeriodMS
ejectionVelocity = 10.0; //The starting velocity at which the emitter will emit particles. 100.0 is pretty damn fast to give you an idea. It can also be set to a negative value for reversed velocity.
velocityVariance = 0.0; //This adds a +/- range for the ejectionVelocity.
ejectionOffset = 0.5; //Now we're getting into all the stuff about what i like to call the Emitter Sphere. Imagine your emitter is a phere, and particles are emitted from the surface of that sphere. This will determine how big said sphere is, so how far away from the middle point (or source, or node, or whatever it's called) all your particles are emitted. There is no randomizing range for this one unfortunately, so you can't make the surface of your emitting sphere "thicker" to allow particles to be emitted at different distances from your middle point. I might add every Unit in offset = 2 brick distances (see example in 4th post). In this case, the particles are emitted 1 brick away from the mid point.
thetaMin = 0; thetaMax = 90; //These two guys determine what circular surface of your sphere will actually be emitting, in degrees. The range can be from 0 to 180, and Min has to be lower then Max otherwise you get some errors. 0 is along the +Z axis, and 180 is along the -Z axis. In our case, 0 to 90, we have half a sphere pointing upwards. Putting something like 89 to 90, we get an equatorial ring. 89.9 to 90, we get an even thinner ring. And making that range greater gives you a thicker ring. You can play around with these angles and the Offset to get the right effects. For a better look at angles and the emitter sphere, see 4th post.
phiReferenceVel = 0; //These lines have baffled people. Well i know what they do, and I'm going to tell you guys so you can make "Spinning" emitters. Why would you need them? Well for great smoke trails for one, and whatever other funkiness you could think up. This line right here will determine how many Degrees the emitter will turn. Your emitter sphere is made up of two major degree units: The Theta degrees (Up to Down, 0 to 180 degrees) and the Phi units (Front to Front again, full revolution of 360 degrees). If you set this to 360, for example, and your Phivariance below is 1 instead of 360 (more on that in a bit), your emitter sphere will be emitting on the Theta range you set up, only that range will be condensed to a thin portion of the sphere. Furthermore, that portion will be revolving around the middle point of the emitter, making a full revolution (360) every 1 second. The value you set here will basically determine how many degrees of rotation are covered every 1 second, and the starting point of this thin portion is always on the +Y axis (shooting a projectile with this enabled, it will start facing Downwards). In our case, it's 0. It's not even moving, and if phiVariance were 1 it would be shooting out in the +Y direction constantly. You can also make some small variable hacks here but I won't go into details, that alone would take an entire post to explain.
phiVariance = 360; //This here determines over what degree range around the mid point particles will be emitted. Simply put, it determines how thick the "portion of the sphere" that emits particles is. Right now it's 360, which means particles will be spread out over the entire equator when they are emitted. Essentially, this means no matter where the portion of the sphere is, either due to rotation or being still, it will still be the entirety of the sphere that emits particles. It accepts values from 1 to 360. I don't think 0 works. There will be more examples portraying it's uses later.
overrideAdvance = false; //This line allows you to apply new colors[]. sizes[], and times[] keys to particles. It's useful if you want to use the same basic particles you used for something else, and it might cut down on datablocks (untested)
useEmitterColors = false; //If set to true, all emitted particles will have the same RGB values as their emitter object if it has any specific color arrays to it (in our case, the brick), and it will also
blend the transparency values with your particles. It's nice if you're making brick emitters, amazing even. Not sure if it applies to other objects, like playerCharacter colors. If it does then god damn.
orientParticles = false; //This is another amazing line that allows you to make "straight", almost mesh-like particles. What it does is, if set to true, will lock your emitted particles' X and Y axises, leaving only the Z axis for free rotation. In other words, it turns your particles into Z Billboards. This makes it harder for them to rotate in order to "look at you". They will still rotate on that Z axis though, which is why we don't have holograms, but this bit of code still allows you to make things like Light Rays, the only problem being the particle decals will be gigantic and mostly empty in order to limit that rotation.
**As of V11, this feature is broken. It has been highlighted however, and will very likely be restored in V13.** The orientParticles function works perfectly again.
particles = "Fireball2TrailParticle"; //You particle datablock
string. I said string, because you can actually have different particles emitted from a single emitter. How? Simply add a space, then the next particle datablock name, like so: = "Fireball2TrailParticle Fireball2FartParticle";. I don't think there is a limit to how many different kinds you can fit in there, but basically whenever the emitter hits an emitting time, it will choose one particle from the list, and emit it. If you have two, each has 50% chance, with three each has 33% chance and so on. This is MONSTROUSLY useful especially for Projectile Trails simply because you can only have one emitter per projectile. Doing the same thing with the projectile's trailEmitter string doesn't work. **There seems to be a minimal ejectionPeriodMS for this string feature, under which the particles do that whole "Switching" bug (all particles switch from one datablock to another, causing some problems). Fortunately the minimal ejectionPeriod is quite small, I pinpointed it to be around 10, so you may not have to worry about it.**
};