Substance Painter
In this start to finish texturing project within Substance Painter we cover all the techniques you need to texture the robot character.
# 1 19-05-2012 , 01:01 AM
Subscriber
Join Date: Feb 2005
Location: Winter Park, FL
Posts: 62

particle direction with animated characters

Ever wanted to make a particle go in the direction your character is pointing and don't want to animate the emitter directly? Yeah, didn't think so..

In two of my small projects I wanted to create some muzzle flashes for guns and i hate hand animating everything so i wanted a solution to do this automatically for me.

What I did was I parented a directional emitter to a specific joint, namely the hand of the character and made it directional. You will have to do just a little tweaking to get the emitter to point straight but once its done that's all for that.

So once you've done, setup your emitter rate. Your emitter rate is how many particles per second you want to emit, so 30 will give you 1 particle per 1 frame. So given that, let's say that your weapon of choice does 600 rounds per minute. 600 rounds per minute / 60 minutes = 10 rounds per second / 30 frames per second = 1 particle every 3 frames.. see how that works out? So 10 is what you would put in for your emitter rate. This will serve as your bullet or round. You want to set your speed up correctly because this is what we'll use to setup and create our muzzle flash. Now even though your scene is set to cm, its actually...meters.

Now you can hit play, but regardless of what it looks like, we're not done because those particles aren't going to go in the direction you want to yet. Expressions is how we will achieve the second part, and this is the meat and potatoes of this whole plate of food...

This first set of MEL will grab the direction and velocity of your bullet and create your muzzle flash
This goes into the creation expression of the bullet emitter, we are creating our muzzle flash from this
Code:
//get the location of the barrel emitter with xform
//using tx will not work since you parent the emitter
//to the weapon, parenting the emitter to the weapon
//makes all translate in localspace 0

float $eLoc[3] = `xform -ws -q -t LH_LB_Emitter`;
float $eLocX = $eLoc[0];
float $eLocY = $eLoc[1];
float $eLocZ = $eLoc[2];

//assign the emit command to a string
string $emitCmd1 = ("emit -o LH_LB_MuzzleFlash \n");
string $emitCmd2 = ("emit -o LH_LB_MuzzleFlash \n");

//get a random count of how many particles for each command
//you want to emit
int $emitCount1 = rand(1500, 2500);

//the start position for each particle is at the
//location of the emitter in worldspace
for($i = 0; $i < $emitCount1; $i++)
{
	$emitCmd1 += "     -pos " + $eLocX + " " + $eLocY + " " + $eLocZ + "\n";
	$emitCmd2 += "     -pos " + $eLocX + " " + $eLocY + " " + $eLocZ + "\n";
}

//apply attribute velocity
$emitCmd1 += "     -at velocity\n";
$emitCmd2 += "     -at velocity\n";

//give each particle their initial velocity
//the sphrand function is used to give each a
//general velocity, to get a bigger muzzle flash
//adjust the rand(0, ##) higher 0 to whatever
//You will need to adjust the divmod on the sparkShape
//to get the desired muzzle flash effect, both go
//hand in hand

for($i = 0; $i < $emitCount1; $i++)
{
	vector $randVel1 = sphrand(rand(0, 50));
	vector $randVel2 = sphrand(rand(0, 15));
	float $ranVelX = $randVel1.x;
	float $ranVelY = $randVel1.y;
	float $ranVelZ = $randVel1.z;

	$emitCmd1 += "     	       -vv " + $ranVelX + " " + $ranVelY + " " + $ranVelZ + "\n";
	$emitCmd2 += "     	       -vv " + $randVel2.x + " " + $randVel2.y + " " + clamp(0, 30, $ranVelZ) + "\n";
}

//set a random lifespan for each particle
//adjust according to get the desired effect
$emitCmd1 += "     -at lifespanPP\n";
$emitCmd2 += "     -at lifespanPP\n";
for($i = 0; $i < $emitCount1; $i++)
{
	float $randls = rand(0, .025);

	$emitCmd1 += "     	       -fv " + $randls + "\n";
	$emitCmd2 += "     	       -fv " + $randls + "\n";
}
eval($emitCmd1);
eval($emitCmd2);
Notice in the above code, I have clamped the Z value in velocity. You can clamp one of the other 2 velocity values if you change the direction set on the emitter. Its going to look wierd but if you try it out it will make sense. To me, the YZ plane is the wall, but when I tried to clamp the Y in this instance, I didn't get the result I was looking for (amazingly) it took a little playing around to figure out which one to clamp.

For testing, lower the emit counter at the top to 150, 200
you'll also notice I have 2 emitCmd variables.. 1 is for the base of the muzzle flash and the other is for closer to the emitter, which is the clamped value.

So simply put, the above states, when we create our bullet particle, create the muzzle flash...

Once this has been created, now we need to shape our particles correctly...
This goes in the creation expression of the 2nd set of particles
Code:
vector $bulletVel = LH_LB_ParticlesShape.velocity;
vector $muzFlVel = LH_LB_MuzzleFlashShape.velocity;
int $partID = LH_LB_MuzzleFlashShape.particleId;
float $divMod;

float $angleBullet = angle($bulletVel, $muzFlVel);

//the divmod adjust the distance at which particles will travel in the direction of the bullets.
//higher numbers will make the muzzle flash shorter in distance.

if($partID%5 == 0)
{
	$divMod = rand(40, 80);
}
else
{
	$divMod = rand(20, 40);
}

//bullet velocity is multiplied by angelbullet/divmod so the muzzle flash doesn't travel the
//distance of the primary particle

float $newVelX = (($bulletVel.x * ($angleBullet / $divMod)) + $muzFlVel.x);
float $newVelY = (($bulletVel.y * ($angleBullet / $divMod)) + $muzFlVel.y);
float $newVelZ = (($bulletVel.z * ($angleBullet / $divMod)) + $muzFlVel.z);

//apply the new velocity direction to the muzzleflash

LH_LB_MuzzleFlashShape.velocity = <<$newVelX, $newVelY, $newVelZ>>;
the above code basically "Shapes" the muzzle flash for you. Find me the angle, and shape it...

If you want to go a step further and create particles for the smoke...
in your muzzle flash particle's Runtime before dynamics expression..
Code:
vector $MuzPos = LH_LB_MuzzleFlashShape.worldPosition;
int $partID = LH_LB_MuzzleFlashShape.particleId;

if($partID%5 == 0)
{
	//assign the emit command to a string
	string $emitCmd1 = ("emit -o MuzzleSmoke \n");
	
	//get a random count of how many particles for each command
	//you want to emit
	int $emitCount1 = rand(0, 2);
	
	//the start position for each particle is at the
	//location of the emitter in worldspace
	for($i = 0; $i < $emitCount1; $i++)
	{
		$emitCmd1 += "     -pos " + $MuzPos.x + " " + $MuzPos.y + " " + $MuzPos.z + "\n";
	}
	
	//apply attribute velocity
	$emitCmd1 += "     -at velocity\n";
	
	for($i = 0; $i < $emitCount1; $i++)
	{
		vector $randVel1 = sphrand(rand(1, 5));
		float $ranVelX = $randVel1.x;
		float $ranVelY = $randVel1.y;
		float $ranVelZ = $randVel1.z;
	
		$emitCmd1 += "     	       -vv " + $ranVelX + " " + $ranVelY + " " + $ranVelZ + "\n";
	}
	
	$emitCmd1 += "     -at lifespanPP\n";
	for($i = 0; $i < $emitCount1; $i++)
	{
		float $randls = rand(0, .75);
	
		$emitCmd1 += "     	       -fv " + $randls + "\n";
	}
	eval($emitCmd1);
}
Keep one thing in mind that if the scale of your characters and objects are bigger or shorter, you will have to modify all of the numbers above to fit...there's no magical number to this and I had this same expression for 2 different scenes, one scene had much larger characters. Its not easy, but the payoff is great

Hopeully this all makes sense! Let me know if you have questions!!


Last edited by Razor Blade; 19-05-2012 at 01:04 AM.
Posting Rules Forum Rules
You may not post new threads | You may not post replies | You may not post attachments | You may not edit your posts | BB code is On | Smilies are On | [IMG] code is On | HTML code is Off

Similar Threads