UE2 - UT2kX Smoke Trail Emitter Help

  • Two Factor Authentication is now available on BeyondUnreal Forums. To configure it, visit your Profile and look for the "Two Step Verification" option on the left side. We can send codes via email (may be slower) or you can set up any TOTP Authenticator app on your phone (Authy, Google Authenticator, etc) to deliver codes. It is highly recommended that you configure this to keep your account safe.

Skell*

New Member
May 19, 2014
6
0
1
I've gotten pretty familiar with Beam, Spark, Sprite and Mesh Emitter stuff... but the TrailEmitter really makes no sense to me. I can't quite get a grasp on how to make it work properly

The effect I'm trying to create is a kind of cigarette smoke trail. I assume TrailEmitter is right emitter for trying to achieve this kind of effect, but if people know of a way to emulate this using another kind of emitter, I'd be cool with using that.
 

Hellkeeper

Soulless Automaton
Feb 16, 2014
146
4
18
France
hellkeeper.net
You can easily use a sprite emitter with a lot of small particles. This is, in my experience, easier and creates less bugs.

The trail emitter work a bit differently from others. First, you can't see it working unless it's moving. It creates a trail from its starting point to its ending point using crossed sheets, and the length of the trail at any given time is dependent on the distance, the speed etc. It doesn't look as good and is a bit more complex than the sprite emitter, and in your case, I belive it's less adequate than a normal sprite emitter.
 

Wormbo

Administrator
Staff member
Jun 4, 2001
5,913
36
48
Germany
www.koehler-homepage.de
To test TrailEmitters in the editor I give them an initial velocity and set their TrailLocation to PTTL_AttachedToParticle. When you use them in the game, set the initial velocity to zero again and change the TrailType to PTTL_FollowEmitter. The Emitter actor should be attached to the actor you want to emit the trail.

However, for a smoke trail, you should use a sprite emitter. If the projectile goes a about the same speed all the time, just pick a spawn rate that fits your needs. Remember to increase the maximum number of particles, otherwise particles will be recycled before they can fade out properly.

BTW: For a high-arching projectile I found time-based spawning not quite good enough. The following Emitter subclass mimics the UT3 feature that you can also specify that particles are spawned a certain distance apart:
Code:
/******************************************************************************
ProjectileTrailEmitter

Copyright (c) 2009, Wormbo
******************************************************************************/

class ProjectileTrailEmitter extends Emitter notplaceable;


//=============================================================================
// Properties
//=============================================================================

struct TVelocitySpawnInfo {
	var int EmitterIndex;
	var float ParticlesPerUU;
	var float _Remainder;
};

var() array<TVelocitySpawnInfo> VelocitySpawnInfo;

var float SpawnTime;


simulated function PostBeginPlay()
{
	SpawnTime = Level.TimeSeconds;
	SetBase(Owner);
}


simulated function Tick(float DeltaTime)
{
	local int i;
	local float LocDiff, NumParticles;
	
	if (VelocitySpawnInfo.Length > 0 && OldLocation != Location) {
		LocDiff = VSize(Location - OldLocation);
		do {
			if (Level.TimeSeconds - SpawnTime > Emitters[VelocitySpawnInfo[i].EmitterIndex].InitialDelayRange.Min) {
				NumParticles = VelocitySpawnInfo[i]._Remainder + LocDiff * VelocitySpawnInfo[i].ParticlesPerUU;
				VelocitySpawnInfo[i]._Remainder = NumParticles - int(NumParticles);
				Emitters[VelocitySpawnInfo[i].EmitterIndex].SpawnParticle(int(NumParticles));
			}
		} until (++i == VelocitySpawnInfo.Length);
	}
}

simulated function Kill()
{
	// prevent spawning additional 
	VelocitySpawnInfo.Length = 0;
	Super.Kill();
}


//=============================================================================
// Default values
//=============================================================================

defaultproperties
{
	bNoDelete = False
	bHardAttach = True
}

Usage might look something like this:
Code:
/******************************************************************************
UT3HellfireSPMAShellTrail

Copyright (c) 2009, Wormbo
******************************************************************************/

class UT3HellfireSPMAShellTrail extends ProjectileTrailEmitter;


//=============================================================================
// Imports
//=============================================================================

#exec texture import file=Textures\SPMASmoke.dds


//=============================================================================
// Default values
//=============================================================================

defaultproperties
{
	Begin Object Class=SpriteEmitter Name=SmokeTrail
		RespawnDeadParticles=False
		SpinParticles=True
		AutomaticInitialSpawning=False
		UseRandomSubdivision=True
		UseColorScale=True
		ColorScale(0)=(Color=(G=160,R=255))
		ColorScale(1)=(RelativeTime=0.01,Color=(B=128,G=255,R=255,A=255))
		ColorScale(2)=(RelativeTime=0.2,Color=(B=255,G=255,R=255,A=255))
		ColorScale(3)=(RelativeTime=1.0,Color=(B=255,G=255,R=255))
		MaxParticles=500
		SpinsPerSecondRange=(X=(Min=-0.1,Max=0.1))
		StartSpinRange=(X=(Min=-50.0,Max=50.0))
		StartSizeRange=(X=(Min=30.0,Max=40.0))
		ParticlesPerSecond=0.0
		InitialParticlesPerSecond=0.0
		DrawStyle=PTDS_AlphaBlend
		Texture=Texture'SPMASmoke'
		SecondsBeforeInactive=0.0
		LifetimeRange=(Min=2.0,Max=2.5)
		InitialDelayRange=(Min=0.1,Max=0.1)
		StartVelocityRange=(X=(Min=-5.0,Max=5.0),Y=(Min=-5.0,Max=5.0),Z=(Min=-5.0,Max=5.0))
		Acceleration=(Z=20.0)
		Opacity=0.75
	End Object
	Emitters(0)=SpriteEmitter'SmokeTrail'
	
	VelocitySpawnInfo(0) = (EmitterIndex=0,ParticlesPerUU=0.04)
}
 

Skell*

New Member
May 19, 2014
6
0
1

Yeah, I started working with sprite emitters when I noticed it was only spawning when moving around. I realized that there was another problem with using emitters - mainly how weapons spawn them.


I realized I wasn't quite clear about my intentions with this code. It's not for projectiles but instead for a smoking barrel after a gun shoots. Currently how the weapon does the effect is this:

Emitter:

Code:
class CyberEmitterHuskSmokeTrail extends Emitter;

simulated function Trigger(Actor Other, Pawn EventInstigator)
{
    Emitters[0].SpawnParticle(30);
}

defaultproperties
{
     Begin Object Class=SpriteEmitter Name=SpriteEmitter0
         Acceleration=(Z=2.00)
         Opacity=0.40
         FadeOut=True
         FadeOutStartTime=3.00
         MaxParticles=120
         SpinParticles=True
         AutomaticInitialSpawning=False
         SpinsPerSecondRange=(X=(Min=0.02,Max=0.02))
         StartSpinRange=(X=(Min=-1.00,Max=1.00))
         UseRotationFrom=PTRS_Actor
         StartLocationShape=PTLS_Sphere
         StartSizeRange=(X=(Min=1.50,Max=2.50),Y=(Min=1.50,Max=2.50),Z=(Min=1.50,Max=2.50))
         UseAbsoluteTimeForSizeScale=True
         UseRegularSizeScale=False
         DrawStyle=PTDS_Brighten
         Texture=Texture'KF_FX_Char_T.Reggie_Smoke'
         LifetimeRange=(Min=4.00,Max=4.00)
         StartVelocityRange=(Z=(Min=4.00,Max=4.00))
     End Object
     Emitters(0)=SpriteEmitter'CyberWeaponsV3.CyberEmitterHuskSmokeTrail.SpriteEmitter0'

     bNoDelete=False
     Style=STY_Additive
     bHardAttach=True
     bDirectional=True
}

And it's used as the SmokeEmitter in my WeaponFire extension class. I'm pretty sure that's a class that exists in UT2004.
 

Wormbo

Administrator
Staff member
Jun 4, 2001
5,913
36
48
Germany
www.koehler-homepage.de
Oh sorry, I guess I shouldn't just have skimmed your post.

For a cigarette smoke effect you might want to try the BeamEmitter with dynamic noise. It won't be pretty, but may be the closest thing you will be able to achieve with the tools as they come out of the box.
But as I pointed out with my special emitter for constant distance spawning, you can mess with the stuff if you are willing to write custom code. Particularly for the beam emitter I also created custom code that bends it into a parabola shape to visualize the trajectory of the projectile that smoke emitter code from above was used for.

Here's an updated version of that trajectory beam emitter:
Code:
/**
WVPersesMAS.PersesArtilleryTrajectory

Creation date: 2013-12-16 09:26
Last change: $Id$
Copyright (c) 2013, Wormbo
*/

class PersesArtilleryTrajectory extends Emitter;


var vector GoodColor, BadColor;


/**
Spawn the initial trajectory beam particle.
*/
function PostBeginPlay()
{
	Emitters[0].SpawnParticle(1);
	Emitters[1].SpawnParticle(1);
}


/**
Update the trajectory arc according to the given parameters.
*/
function UpdateTrajectory(bool bVisible, optional vector StartLocation, optional vector StartVelocity, optional float Gravity, optional float MinZ, optional bool bCanHitTarget)
{
	local float tMax, tDelta, t;
	local BeamEmitter Arc;
	local int i;
	
	Emitters[0].Disabled = !bVisible;
	
	// check if we need the bounding box hack
	if (bVisible && Normal(StartVelocity).Z > 0)
	{
		// need to calculate apex point (actually only apex height)
		Emitters[1].Disabled = False;
		Emitters[1].Particles[0].Location = StartLocation;
		Emitters[1].Particles[0].Location.Z -= 0.5 * Square(StartVelocity.Z) / Gravity;
	}
	else
	{
		// horizontal or downward initial velocity, so no hack required
		Emitters[1].Disabled = True;
	}
	
	if (!bVisible)
		return;
	
	if (bCanHitTarget)
		Emitters[0].Particles[0].ColorMultiplier = GoodColor;
	else
		Emitters[0].Particles[0].ColorMultiplier = BadColor;
	
	Arc = BeamEmitter(Emitters[0]);
	
	tMax = (StartVelocity.Z + Sqrt(Square(StartVelocity.Z) + 2 * Gravity * (StartLocation.Z - MinZ))) / Gravity;
	tDelta = tMax / (Arc.HighFrequencyPoints - 1);
	
	Arc.HFPoints[0].Location = StartLocation;
	for (i = 1; i < Arc.HighFrequencyPoints; ++i)
	{
		t += tDelta;
		Arc.HFPoints[i].Location = StartLocation + StartVelocity * t - vect(0,0,0.5) * Gravity * Square(t);
	}
}


//=============================================================================
// Default values
//=============================================================================

defaultproperties
{
	GoodColor = (X=0.0,Y=1.0,Z=0.0)
	BadColor = (X=1.0,Y=0.0,Z=0.0)
	
	// the trajectory arc
	Begin Object Class=BeamEmitter Name=Trajectory
		MaxParticles              = 1
		AutomaticInitialSpawning  = False
		LifetimeRange             = (Min=999999.0,Max=999999.0)
		StartSizeRange            = (X=(Min=5.0,Max=5.0))
		HighFrequencyPoints       = 50
		CoordinateSystem          = PTCS_Absolute
		DetermineEndPointBy       = PTEP_Offset
		Texture                   = Texture'EpicParticles.Beams.DanGradient'
		AlphaTest                 = False // DanGradient has an alpha value of 0 and would otherwise be invisible
		Opacity                   = 0.75
	End Object
	Emitters(0) = Trajectory
	
	// invisible particle placed at the trajectory apex to extend the rendering bounding box
	Begin Object Class=SpriteEmitter Name=BoundingBoxHack
		MaxParticles              = 1
		AutomaticInitialSpawning  = False
		LifetimeRange             = (Min=999999.0,Max=999999.0)
		StartSizeRange            = (X=(Min=0.0,Max=0.0)) // so it's not actually visible
		CoordinateSystem          = PTCS_Absolute
	End Object
	Emitters(1) = BoundingBoxHack
	
	bNoDelete = False
}
If you are willing to fight against potential native code logic that may be in your way, you could probably get a TrailEmitter or BeamEmitter to create that rising wiggly movement you'd expect from the iconic kind of cigarette/gun smoke. In the case of a BeamEmitter that native code either messed with the LFPoints or didn't use them at all for rendering, so the entire bending logic needed to be implemented based on the HFPoints list. I didn't attempt to mess with a TrailEmitter this way, but the variables and structs are defined in UnrealScript, and they aren't const either.
 
Last edited:

Skell*

New Member
May 19, 2014
6
0
1
No, no. I wasn't really being explicit in my first post, my bad haha

Although I already did my projectile's emitters, I'll definitely hang on to this code if I ever try and make a real fancy projectile. The sprite emitter is probably the best way to go for this as both of you have... but I'm now having trouble actually getting the emitter attached to the weapon. I'm probably going to have to redo how the smoke emitter is done inside of the fire class anyways.

Stuff like this:
Code:
if (SmokeEmitter != None && SmokeEmitter.Base != Weapon)
{
        SmokeEmitter.SetLocation( Weapon.GetEffectStart() );
        Canvas.DrawActor( SmokeEmitter, false, false, Weapon.DisplayFOV );
}
makes me think I shouldn't be even using SmokeEmitter/SmokeEmitterClass stuff at all. I might have to write up some custom code that just spawns a xEmitter that attaches to the tip of the gun (still using a sprite particle emitter).

EDIT:

Looking through xEmitters, something like this SpeedTrail might be the easy way out of this:

Code:
//=============================================================================
// speedtrail
// effect for fast things
// by David Hensley
//=============================================================================

class SpeedTrail extends xemitter;

defaultproperties
{
     mParticleType=PT_Stream
     mStartParticles=0
     mLifeRange(0)=0.650000
     mLifeRange(1)=0.650000
     mRegenRange(0)=10.000000
     mRegenRange(1)=10.000000
     mDirDev=(X=0.500000,Y=0.500000,Z=0.500000)
     mPosDev=(X=2.000000,Y=2.000000,Z=2.000000)
     mSpeedRange(0)=-20.000000
     mSpeedRange(1)=-20.000000
     mAirResistance=0.000000
     mSizeRange(0)=12.000000
     mSizeRange(1)=12.000000
     mGrowthRate=-12.000000
     mAttenKa=0.000000
     mNumTileColumns=0
     mNumTileRows=0
     LifeSpan=1.000000
     Skins(0)=Texture'kf_fx_trip_t.Misc.speedtrail_T'
     Style=STY_Additive
}
 
Last edited:

Skell*

New Member
May 19, 2014
6
0
1
I think this will do for what I have in mind - just a little more fine tuning and it should be good to go. It uses the same spawning stuff as muzzle flashes with the spawning and then attaching to bone.

It does a fade in as well as a mRegen set to false to have particles dissipate before LifeSpan makes it get destroyed. I might make a 3rd person version just for quality sake.

Code:
class CyberEmitterHuskSmoke extends xEmitter;

var float CurrentAlpha;
var bool bFadingIn;

simulated function Tick(float DeltaTime)
{
     super.Tick(DeltaTime);

     CurrentAlpha += 600.000000*DeltaTime;

     if(bFadingIn){
          
          if(CurrentAlpha < 255.000000)
          {
               mColorRange[0].A = int(CurrentAlpha);
               mColorRange[1].A = int(CurrentAlpha);
          }
          else
          {
               mColorRange[0].A = 255;
               mColorRange[1].A = 255;
               mRegenRange[0]=0.000000;
               mRegenRange[1]=0.000000;
               bFadingIn = False;
          }
     }

     if(CurrentAlpha > 1000.000000)
          mRegen = False;
}

defaultproperties
{
     mParticleType=PT_Stream
     mRegen=True
     mMaxParticles=90
     mStartParticles=0
     mLifeRange(0)=1.000000
     mLifeRange(1)=1.000000
     mRegenRange(0)=40.00000  
     mRegenRange(1)=40.000000
     mDirDev=(X=0.050000,Y=0.050000,Z=0.010000)
     mPosDev=(X=0.000000,Y=0.000000,Z=0.000000)
     mColorRange(0)=(A=0)
     mColorRange(1)=(A=0)
     mSpeedRange(0)=20.000000
     mSpeedRange(1)=20.000000
     mAirResistance=0.000000
     mSizeRange(0)=8.000000
     mSizeRange(1)=8.000000
     mGrowthRate=-2.000000
     mAttenKa=0.000000
     mAttenKb=1.000000
     mNumTileColumns=0
     mNumTileRows=0
     mPosRelative=False
     LifeSpan=5.000000
     Skins(0)=Texture'kf_fx_trip_t.Misc.speedtrail_T'
     Style=STY_Translucent

     Rotation=(Pitch=16384,Yaw=0)
     bFadingIn = True
     CurrentAlpha = 0
}

I'll upload a video of it in action when I get home from work today if you guys want. :p
 

Skell*

New Member
May 19, 2014
6
0
1
I think I've found my last problem... How do you rotate this thing? I've tried setting it in default properties and using the ChangeRotation() function method but neither are fixing the rotation. I need the smoke to be moving upward, but for some reason it's just going to the right...
Code:
class CyberEmitterHuskSmoke extends xEmitter;

var float CurrentAlpha;
var int WaitCount;
var bool bFadingIn, bTimerOn, bWaitingToStop;

simulated function Tick(float DeltaTime)
{
     super.Tick(DeltaTime);

     if(!bTimerOn){
     	ChangeRotation();
        SetTimer(0.003900,True);
        bTimerOn = True;
     }
}

simulated function ChangeRotation()
 {
    local Rotator newRot;
    newRot = Rotation;
    newRot.Pitch += 16384;
    newRot.Pitch += 16384;
    SetRotation(newRot);
 }

simulated function Timer()
{
	if(bFadingIn)
	{
		if(CurrentAlpha < 255.000000)
        {
        	CurrentAlpha += 1.000000;
            mColorRange[0].A = int(CurrentAlpha);
            mColorRange[1].A = int(CurrentAlpha);
        }
        else
        {
            mColorRange[0].A = 255;
            mColorRange[1].A = 255;
            bFadingIn = False;
            SetTimer(1.0,True);
            bWaitingToStop = True;
        }

	}
	if(bWaitingToStop)
	{
		WaitCount++;
		if(WaitCount > 2)
		{
			mRegen = False;
			mRegenRange[0]=0.000000;
     		mRegenRange[1]=0.000000;
     		SetTimer(1.0,False);
     		bWaitingToStop = False;
     	}

	}
}

Edit: If it's causing any trouble, this xEmitter is being spawned and attached to a bone. I've tried more and more ways to try and change the rotation, but no dice...
 
Last edited:

Skell*

New Member
May 19, 2014
6
0
1
I ended up having it spawned via AnimNotify_Effect. It looks like it could have been a conflict between AttachToBone() and the setting of rotation. It seems to give me less control since it looks like Tick() is ignored, but I'll deal.

Here's a little preview of the barrel smoke effect. Alpha has been reduced from 255 to 128 after this video was uploaded.
https://www.youtube.com/watch?v=kDJqTCywISs