Author Topic: [Tutorial] Making Special Effects: a complete approach  (Read 14545 times)

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.pdf

I 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.**

   };
« Last Edit: October 25, 2009, 09:25:16 AM by Muffinmix »

Now we are going to move away from the Fireball and towards a torque supplied weapon, the Crossbow, simply because my fireball's explosion did not have any Debris to it, or subexplosions. We will look at the explosion in detail



datablock ExplosionData(CrossbowExplosion)
{
   lifeTimeMS = 1200;                              //How long the Explosion and all it's effects (except explosion shape) last. Remember that "lifetime" in the emitter datablock I talked about earlier? Well if it's larger then the lifeTimeMS for explosions, it will still end when the explosion's lifetime ends. Particles with long lifetimes will stick around after the explosion dies however.

   explosionShape = ./something.dts;     //Not included in the crossbow script but worth mentioning. This allows you to have your explosion create a 3D mesh (with animation if you want) when it's created. The best example is the Rocket launcher's Explosion Sphere shape.

   // Volume particles
   particleEmitter = CrossbowExplosionFireEmitter;  //This here's a bonus feature that allows you to spawn a number of particles within a certain radius (randomized to boot) upon explosion creation. You can only use one emitter per explosion for this function.

   particleDensity = 75;                                             //How many particles are created, right now we have 75 created on explosion.

   particleRadius = 2;                                                //The radius of the new emitter sphere. Unlike the one in emitter datablocks, this one will emit from anywhere inside the sphere as well as on the surface. This is pretty much your only ticket to getting an emitter sphere to randomize the offset range without some advanced changes.

   // Point emission
   emitter[0] = CrossbowExplosionSmokeEmitter;
   emitter[1] = CrossbowExplosionSparkEmitter;     //These guys are all your explosion emitters that will be created on explosion, and stick around until either their own lifetimeMS runs out or the explosion's lifetimeMS runs out. You can have up to 4, so 0 to 3

   // Sub explosion objects
   subExplosion[0] = CrossbowSubExplosion1;
   subExplosion[1] = CrossbowSubExplosion2;        //Subexplosions, these are separate explosions that will spawn off of your main explosion. Their own effects last until their effect lifetime runs out, or their lifetimes run out, OR the main mother explosion's lifetime runs out. You can also delay them, and I'll put a small example of a subexplosion and it's important components next. A sub explosion is a regular explosion datablock! It's nothing different. You can have up to 5 PER explosion, and subexplosions can also have subexplosions themselves, ranging from 0 to 4. In theory you can create an infinite number of explosions from a single one, looping the mother explosion will not work however as the original mother will still stop everything once it's lifetime runs out.
   
   // Camera Shaking
   shakeCamera = false;                                           //Wanna piss off your buddies? Set this to true, then give everything ridiculous values. Don't worry, everyone leaving your server at once is pretty common these days.

   camShakeFreq = "10.0 11.0 10.0";                       //This is the amount of Shakes in each direction the cam shake will make per seconds

   camShakeAmp = "1.0 1.0 1.0";                             //The amplitude, that is, how strong in each direction the shakes will be. X (side to side), Y (Up and down) and Z (Forward and Backward) are the directions.

   camShakeDuration = 0.5;                                     //The duration of the shaking in seconds

   camShakeRadius = 10.0;                                      //The radius of the shaking

   camShakeFallOff = 1.0;                                         //A value that affects by how big a magnitude the camShakeAmp will decrease over distance.

   // Exploding debris
   debris = CrossbowExplosionDebris1;                   //Now we're getting into debris! So far Badspot's Tank is the only thing to use it's own unique debris, those straight lines of fire you see when it's projectiles explode. You can think of debris as a small extension to the Emitter Datablock, because it allows you to do just a bit more but they don't have any actual physical impact on gameplay (except maybe lagging if you go overboard, see Physics Rain resource and example in 4th post). It's just pretty stuff.

   debrisThetaMin = 0;
   debrisThetaMax = 0.1;                                          //Remember the Emitter sphere? Well now we have a Debris sphere! These two guys work much the same way as with emitters.

   debrisPhiMin = 0;
   debrisPhiMax = 0.1;                                              //Again Phi like in the emitters but, hold on, what's with this PhiMin/Max instead of phiRefVel for rotation? Well that's right, this here let's you set a portion around the sphere to emit, from 0 to 360. The thing is they forgot to set an Origin value so you can decide where this zone is on your sphere. So, it's pretty much stuck in the +Y direction with no real means of orientation. In this case, it looks like whoever made this wanted the debris to shoot upwards anyways, so even if it was 0 to 360 it wouldn't make much difference.

   debrisNum = 1;                                                     //The number of debris created.

   debrisNumVariance = 0;                                        //Randomization of the debris.

   debrisVelocity = 1;                                                //The velocity of emitted debris.

   debrisVelocityVariance = 0.5;                                //Again, randomization of velocity.
   
   faceViewer     = false;                                            //not in the crossbow script, but this basically determines whether or not emitted particles use any fancy Orientation you might have set up for them using orientParticles in emitter datablocks. It would be more useful if there were more options to actually orient them but still worth mentioning.

   explosionScale = "1 1 1";                                      //Honestly, i don't know what this does. It doesn't seem to affect the emitters in any way, maybe it'f used for radiusDamage? exlplosionShape? I don't know.

   damageRadius = 7;                                               //Determines how large a radius damage will be spread out, this is different from the directDamage in the projectile datablock to come.

   radiusDamage = 150;                                           //Determines the amount of damage dealt at point-blank range from the explosion center. It degrades over the radius.

   impulseRadius = 6;                                                //The value determining impulse range

   impulseForce = 4000;                                            //The amount of force at point-blank range. It plays around in the 1k values.

   playerBurnTime = 5000;                                       //How long players burn.

   // Dynamic light
  lightStartRadius = 6;
   lightEndRadius = 3;                                               //I don't like explosion lights simply because they are not subtle. It's either inwards to outwards, in which case the light simply Vanishes magically unless EndColour is 0 0 0, leaving a bad taste in your mouth. The time from Start to Finish depends on the lifetime of the explosion. I just wish there were as many options for this light effect as there is for fxLight datablocks, but I digress. The light effect's overall lifetime is the same as the explosion's lifetimeMS. **It is noted that subExplosions may also use Dynamic Light effects. See subExplosions "Fireball" resource and example for more on this**

   lightStartColor = "0.5 0.5 0.5";
   lightEndColor = "0 0 0";                                         //The colours of the light, in RGB in the beginning. In this case it's white, slightly bright to completely dark.

   lightHasCorona = true;                                          //This is a strange line, and I don't quite know what it's for. Usually a corona is a decal that's shown along with an fxLight object, but there's no line for any decal. On a side note, Corona decals are "soft" particles, which means they don't cut through other objects to become partially clipped. This is how ALL particles should be ideally, mind you.
};






Now we take a quick peek at how SubExplosions are structured before moving on to Debris:

datablock ExplosionData(CrossbowSubWaterExplosion1)
{
   delayMS   = 100;                                 //A new line here, this determines how long after the mother explosion this explosion takes place.

   offset    = 1.2;                                     //How far away from the mother explosion this explosion will appear.

   playSpeed = 1.5;                                //How fast the explosion plays, that is, how fast everything happens.

   emitter[0] = CrossbowExplosionBubbleEmitte r;
   emitter[1] = CrossbowExplosionWaterSparkEm itter;     //Like in Explosions, sub explosions can have up to 4 emitters
   
   sizes[0] = "0.75 0.75 0.75";
   sizes[1] = "1.0 1.0 1.0";
   sizes[2] = "0.5 0.5 0.5";
   times[0] = 0.0;
   times[1] = 0.5;
   times[2] = 1.0;                                    //I don't use these lines too often because if I will have to alter the colour/size/stuff of emitted particles I might as well simply make a new datablock. In the end you still use up extra datablocks for the changes, and to top it off it you're using the same emitter for a good amount of explosions and you need to actually change some of the physics stuff in that emitter then everything else will also change. Like I said, unless this was fixed, and unless you need to, don't use these. This needs to be tested further, more on Explosion provided Interpolations later. **So far these interpolations do not seem to work. I am running further tests to conclude this.**
};

See subExplosions "Fireball" resource and example for much more information on subExplosions




Well, that covers explosions for the most part. Next we take a look at Debris and finish up with Projectiles. Apart from gun shell debris, you can also use debris in explosions to make pretty effects.

datablock DebrisData(CrossbowExplosionDebris3)
{
   shapeFile = "./shapes/arrow.dts";                        //This is the 3D Shape of the debris, you can either have this or a 2D texture (seen below). Here I tried putting in the arrow to see if it worked, and it does by the way.

   //texture = "base/data/particles/cloud";               //The 2D texture option, basically it's a single decal, not allot of options. Render2D needs to be set to True for this to work.

   render2D = False;                                                 //Let's you use a texture for the debris instead of a shape.

   emitters[0] = "CrossbowDebrisTrail1Emitter";
   emitters[1] = "CrossbowDebrisTrail3Emitter";      //Two trail emitters?? Yes, debris are allowed up to two trail emitters, while projectiles can only have 1. Amazing isn't it?

   explosion = CrossbowDebriloveplosion;                 //This is the explosion that the debris will create when it dies on Collision if ExplodeonMaxBounce is set to True. THIS CURRENTLY DOES NOT WORK IN BLOCKLAND
   
   elasticity = 0.99;                                                    //Elasticity of the projectile, how much of the velocity it retains when it hits something if it has bounces left. In this case, it retains 99% of it's velocity, quite bouncy.

   friction = 0.0;                                                         //Essentially deducts from Elasticity, not sure why this is included when you could simply lower the value of the elasticity.

   numBounces = 3;                                                   //The number of bounces the debris will have.

   bounceVariance = 0;                                              //Another one of those randomizers, gives your numBounces a range of +/- this value.

   explodeOnMaxBounce = True;                               //Determines whether debris will create an explosion on it's last bounce. DEBRIS EXPLOSIONS DO NOT CURRENTLY WORK IN BLOCKLAND

   staticOnMaxBounce = False;                                  //Will the debris stop mid-air on it's last bounce?

   snapOnMaxBounce = False;                                   //staticOnMaxBounce needs to be true for this, and it basically asks whether you want your debris to stick on stuff. I don't think it works in BL yet.

   minSpinSpeed = -500;
   maxSpinSpeed = 500;                                            //Spinning, except for debris! This is another range such as before in the particle Datablock, negative is Clockwise and positive is CounterClockwise

   lifetime = 8;                                                            //Lifetime of the debris, in seconds

   lifetimeVariance = 2;                                              //Another randomizer, gives your lifetime a range of 6 to 10 seconds in this case

   velocity = 60;                                                         //Speed of the debris, keep in mind 100 is pretty fast.

   velocityVariance = 0.5;                                          //Another randomizer

   fade = false;                                                          //Determines whether the texture or shape of the debris will start fading away in it's last second of lifetime.

   useRadiusMass = true;                                          //Allows you to set a kind of debris collision mesh for your debris.

   baseRadius = 1.0;                                                 //This is the radius of your debris' collision mesh if useRadiusMass is set to true. The set minimum is 0.35, any value lower then that will be effectively rounded to 0.35.

   gravModifier = 2;                                                   //Gravity for your debris. It cannot be negative, that would be too cool :(

   terminalVelocity = 60;                                           //The maximum velocity your debris will be allowed to have.

   ignoreWater = true;                                             //There is a whole branch of water effects (water explosions, water physics, etc) but I won't be covering them too much for now until I get more testing done. In the meantime there's a few of them in the reference in my first post, you could check them out.
};

See "Physics Rain" debris example and resource


Many functions repeat themselves, using similar measures for everything (distances, velocity, degrees, etc.). Once you understand the basic Particle and Emitter datablocks the rest comes very easy.
« Last Edit: June 25, 2009, 11:21:01 AM by Muffinmix »

Now we move on to Projectiles.

(not seen are the DamageTypes, I figure you know what those are for)

datablock ProjectileData(CrossbowProjectile)
{
   projectileShapeName = "./shapes/arrow.dts";         //The shape of the projectile. I might add that a projectile mesh does not require any nodes at all, and that the exact Middle of the grid is where the physics happens. You can have your mesh positioned some squares away from the middle (in MS3D anyways) and it will will be the middle point that's the business end of everything.

   directDamage        = 30;                                             //The damage this projectile does to whatever it hits, including bouncing off of it

   directDamageType = $DamageType::Fireball2Direct;
   radiusDamageType = $DamageType::Fireball2Radius;  //Links to the damageTypes

   impactImpulse      = 1000;                                       //This bit decides how far back you are pushed if the projectile hits you. Can be negative for pulling force.

   verticalImpulse      = 1000;                                       //This one is a bit weirder, it basically pushes you Up by it's value if the projectile hits you (or pulls you down if se to negative).

   explosion           = CrossbowExplosion;                      //The explosion that's created when the projectile dies on collision.

   particleEmitter     = CrossbowEmitter;                        //The trail emitter of the projectile

   waterExplosion      = CrossbowWaterExplosion;        //The under-water explosion of the projectile

   particleWaterEmitter= CrossbowBoltBubbleEmitter;   //A trail for underwater? Yup, and it works quite well, I haven't tested to see whether it worked with Water bricks but it should.

   splash              = CrossbowSplash;                            //The splash datablock that plays when the projectile enters water. Like I said, never used splash datablocks before so I'm not too sure how it works. It looks fancy though and i wonder if it could be implemented into other effects.

   muzzleVelocity      = 100;                                           //The speed of the projectile. 100 is pretty fast. (There is a set maximum as of V9)

   velInheritFactor    = 0.3;                                            //How much of the velocity of the guy firing the projectile is kept with this projectile, along with Direction. 1.0 means 100% retained, 2.0 means it will gain 200% of the velocity (double), etc. This adds onto the original velocity.

   armingDelay         = 0;                                               //How long before the projectile is "armed", that is, prone to exploding. Set this higher then 0 to get projectiles to bounce if it's ballistic, needs to be lower then the Lifetime. It's in Milliseconds, so 1000 means 1 second before it can explode. Note: It will still do DirectDamage if it bounces off of you or hits you.

   lifetime            = 2000;                                              //How long this projectile has to live

   fadeDelay           = 10;                                              //Like in debris, this will determine at what time the projectile will start fading out slowly. Not sure about the specifics, but in our case I think the projectile starts fading out after 10 milliseconds of being spawned.

   bounceElasticity    = 0;                                             //How much of the velocity of the projectile is retained if it bounces off something. It goes from 0 to 1.0, where 1.0 is 100% velocity retained.

   bounceFriction      = 0;                                             //Deducts from Bounce Elasticity, kind of useless unless I'm just not getting it.

   isBallistic         = false;                                             //Needs to be True for your projectile to be affected by gravityMod

   gravityMod = 0.80;                                                  //Determines at what acceleration your projectile falls towards the ground. isBallistic is false right now so even if this guy as a value it won't do anything.

   Explodeondeath = true;                                          //This makes your projectile simply explode when it's lifetime runs out, even if it did NOT come into contact with anything beforehand. So you can have mid-air explosions for instance.

   hasLight    = true;                                                   //Determines if this projectile has light

   lightRadius = 4;                                                       //The radius of the light

   lightColor  = "0.5 0.5 0.25";                                    //RGB values for the light's colour

   hasWaterLight     = true;                                        //Light underwater

   waterLightColor   = "0 0.5 0.5";                             //RGB colours of the light underwater, it uses lightRadius for it's radius.

   decal[0] = ./bullethole.png;                                    //Example here, yes i know crossbow bolts aren't arrows, and that arrows aren't bullets, etc. Basically how it works is when you shoot a wall or terrain, it will smudge it with a decal. You can have up to 6 decals, 0 to 5, and whenever the projectile hits terrain one of those decals will pop up. This does not work on bricks, or anything for that matter except for terrain. It also does not work on some types of terrain objects.

   lightHasCorona = true;                                          //Again, not sure what this guys does as far as the specifics go.
};

I hope this will help you guys, and I hope to edit these posts as I get new information. I will also add a small tutorial on how to quickly change any of these variables to help you fine-tune your effects better.
« Last Edit: June 25, 2009, 11:27:41 AM by Muffinmix »

A small guide to fast and effective fine tuning.


You want a quick and easy way of making changes right? This is as simple as it gets.

First, we're going to make a shameful, disgraceful copy of the .zip packaged folder of something that already exists that is "like" what you want to make (Weapons with large explosions, Rocket_Launcher, for guns, the Gun, for brick emitters, Emitters_basic, etc.). You will then paste that copy elsewhere, maybe on your desktop for now. Keep your add-ons folder open. Next, do the following:

- Change the name of the .zip folder
- Extract the Server.cs, Description, and Weapon.cs (if there is one, if there's only server and description then that's fine).
- Replace all the names for your conventions and so you don't overwrite anything as per usual
- Place those files into the .zip folder, place the folder into your add-ons, run Blockland and test it to see if it works. Tackle any major bugs posted in the console.

Alright, we haven't done anything yet, next is the fun part

- Keep a copy of the server.cs or weapon.cs on your desktop, open it and edit whatever lines to get an Approximate effect
- Save, then place the file into your .zip folder in the Add-ons folder
- Run blockland, test it out, observe what needs changing.
- Then, modify the script some more to Fine Tune the variables to get just that right effect, save, put it in your .zip, restart blockland, test, rinse and repeat.

Console editing used to lead to quite a bit of horror stories in the particle emitter department, but Badspot seems to have fixed a whole bunch of variable problems that were present in V8 so now you should expect your emitter to look "exactly the same" when you start a new game as when you were done editing it. Whichever method you want to use is up to you.

Of course, in the end before release make sure there are NO problems at all. What you see when an add-on loads successfully is Loading Add-on, Executing Server.cs, Executing Add-onName.cs, then the number of datablocks successfully executed. Anything else like a huge loving "WARNING" obviously means something is wrong. Furthermore, I would recommend what everyone else recommends and that's getting your own models and such for a good finished product.

If you're new at it I would recommend you start with brick Emitters, simply because it's easy to see what each modifications do with them, while in weapons all the effects die out too fast to be observed very well.

I will modify this last post with some images, especially to demonstrate what the so-called Emitter Sphere looks like.



Extra tips and pictures!

It's a bit unfair that you get a giant wall of text and no actual picture of what's going on here so I put together a few samples to show you guys how much distance is covered by Offset and what exactly the Emitter sphere looks like.

First off, I made a few basic dot emitters to get a better idea of the distance covered by offset, in brick amounts. And, fortunately, every single Unit in Offset amounts to 2 brick distances. So, an offset of 1.0 means your particles will spawn 2 bricks away from the mid-point of the emitter, 2.0 in offset doubles that amount. Here's an example:



Blue is 1.0, green is 2.0, wine red is 3.0 and so on. You can see how exact it is, so now you don't have to worry too much about all of this being an inexact process.


Next, I have an Angle sample, giving you a small perspective of what the emitter sphere looks like. Well, it's an oval in this case, that's just because I can't stack emitters on a single brick, that would be pretty cool though. The offset is about 5.5 for each emitter.



Starting from the top with all emitter bricks pointing the emitters Up:
White
thetaMin = 0.0;
thetaMax = 0.1;

Blue/Purple
thetaMin = 22.5;
thetaMax = 22.6;

Green
thetaMin = 45.0;
thetaMax = 45.1;

Yellow
thetaMin = 67.5;
thetaMax = 67.6;

Red
thetaMin = 89.9;
thetaMax = 90.1;

And so on. Next yellow is ~112.5, next Green is ~135, Next Blue is ~157, and finally the last white dot down there is 180.

Fine that,s cool, now let's try something for the heck of it. We'll use all the same emitters, but make a small change to the range between thetaMin and thetaMax. Let's make it a range of 10, so for example the middle Red ring will be:

thetaMin = 85;
thetaMax = 95;

The top Green will be

thetaMin = 40;
thetaMax = 50;

And so on for the rest. This is what we get:





Oooh that's pretty, and it demonstrates the sheer bucketloads of particles emitters dish out at a 1ms ejectionPeriod. The particles last 3 seconds btw, so for each emitter we have 3000 dots out at any given moment, which brings me to my next topic.



Particle lag!! Oh god

2 things cause lag with particles

- Particle Size
- The amount

Badspot has a fix in options that will reduce the amount of particles created by emitters (particle quality) for the client, but it still won't save you from giant particles. A good test to see whether or not your emitter is just too much, simply spawn 10-15 bricks with the emitter. All of them need to be in your sight (particles don't load if you aren't "looking" at the emitter source). If your experience lags with only 10 bricks, then you will have to do something about it, either reduce the size or reduce the amount, or both.

For size it's simple, over 30.0 is too big no matter what you're doing. 20.0 is ok, just make sure there aren't more then 20 at any given time. 10.0 is fine in tame amounts, and anything under is usually fine.

For particle Amount, 2 things to keep in mind: ejectionPeriod in the Emitter datablock, and lifeTime in the Particle datablock. Multiply one by the other to get how many particles are out at any given time. More then 1000 out at any time is ridiculous at any size larger then 1.0, even then it's a stretch.



Resources and other experiments

Here are some other topics I started as side tutorials and experiments.

"Physics Rain" debris example and resource for a working physics debris resource
subExplosions "Fireball" resource and example for much more information on subExplosions and a working projectile Fireball with subExplosions effects as resource.
« Last Edit: June 25, 2009, 11:31:49 AM by Muffinmix »

Huuurrrr
* Packer head explodes

This'll be a good reference for when I forget around with the olde stats.

3 Long posts with a bunch of effect stuff = mah head go bewm :S

This seems helpful nonetheless, good job Muffinmix

Holy stuff, good job.  I started reading then I just scrolled down and was like, "Wtf."  Will be useful if I decide to do anything.

Aye, I hope you guys find all this useful whenever you need it.


I agree, this should be stickied.
Also, I'd like to add that when coloring particles, you can use the RGB followed by the transparency, just like when making a colorset.


I agree, this should be stickied.
Also, I'd like to add that when coloring particles, you can use the RGB followed by the transparency, just like when making a colorset.

Yup, and 1-255 values also work. Forgot to add that in.

I added some pictures to show you guys what the so-called "emitter sphere" looks like, as well as how much distance is covered in Offset values. I'd love to show how the phi variables work too but that stuff's more animated, kind of hard to demonstrate with a still picture.

It looks more like an egg than a sphere, anyway, that looks so awesome, it's like a hologram, which reminds me... Wedge said quite some time ago that he'd make holograms, but then his files got lost or something, i wonder if he'll ever try to re-do it :D

It looks more like an egg than a sphere, anyway, that looks so awesome, it's like a hologram, which reminds me... Wedge said quite some time ago that he'd make holograms, but then his files got lost or something, i wonder if he'll ever try to re-do it :D

We'd need to go outside the realm of Datablocks to get that done. You can't make completely non-Billboard particles otherwise. Billboards are particles that will always turn to face you, you can come close with orientParticles = true;, but that doesn't lock the Z axis so the particles will still rotate on a single axis to look at you.

It's dumb I know, but you simply can't get non-billboard particles for some reason.

Sticky.
I shall do this one day.