Sniper's Paradise!
Trails
This tutorial outlines a generic class to create a trailing effect, for example a smoke trail from a rocket or a cool motion effect. Oh and thanks to TNSe for pointing out some silly mistakes I made :|
Theory The trail will consist of a number of Actors (we shall call them Trailer), so we shall store them in a linked list with the first Trailer in the list controlling the rest of the trail. This is somewhat similar to the way the PBolt beam in UT works, except with our trail the first item in the linked list isn't of a different class to the rest; there's no need when a few extra lines of code will allow us to determine which object is first. To do this, and to limit the maximum number of Trailers in a trail, we keep a counter in each Trailer. This counter gets incremented when a new Trailer is added to the linked list. The approach taken to trails in UT (for example in the rockets) is to continously spawn short lived Actors which gives the effect of a trail. The approach we're taking is quite different, as indicated earlier we're using a fixed number of Actors held in a linked list.
Because new Actors aren't continously being spawned at the location of whatever the trail is attached to we need to devise a means of moving the individual Trailers in such a fashion as to create a trail effect. We simply move each Trailer to the location the one in preceeding it (in the linked list) occupied and move the first Trailer to the same location as the Actor the trail is set to follow. This is really quite simple to do, if we set the Owner of each Trailer to be the Trailer preceeding it and in the case of the first Trailer set the Owner to the Actor that the trail should follow. So now we can not only traverse down the linked list, but each item in the list has a means of referring to the item preceeding it. Now every time we update the trail each Trailer will set its location to be that of its Owner. This can be bundled into a function which initially is called on the first Trailer, this function will then call itself on the next item in the linked list (and so on), then set the current Trailer's location to that of its Owner and finally if it's the last item in the linked list and the trail contains less Trailers than we want, spawn a new Trailer. All that remains to get the whole thing working is to set up Tick() to periodically call the trail update function on the first Trailer in the linked list. Implementation So we've gone through a basic explaination of how the trail system works, so here's the code for the generic Trailer class.
Note that it expands considerably on the basic concept, adding a number of useful features such as the ability to delay trail updating and offset the trail from the Actor it follows.
// Generic trailer effect class Trailer extends Effects;
// Position in trail & maximum number of trails var int Pos, MaxTrails; // Next part of the trail, Owner is used to keep track of previous part of trail var Trailer Next; // Time delay to lag the trail updating by var float lagBy; var float cntr; // What to spawn as trails var class<Trailer>TrailerClass; // Location on a cylinder around the object being trailed that trail should appear from // X = radius, Y = elevation, Z = angle, e.g. 10UUs directly behind at same level would be vector(10,0,180) var vector TrailOffset;
replication { unreliable if (ROLE == ROLE_Authority) Pos; }
// Make sure the whole trail gets destroyed at once simulated function Destroyed() { Super.Destroyed();
if (Next != None) Next.Destroy(); }
// Update the trailer simulated function Update() { local vector Offset; local float Alpha, X, Y;
// Call Update() on next trail if (Next != None) Next.Update();
// Move to where the previous part of the trail was (or player location for that matter) if (Pos == 0) { // Convert Owner horizontal rotation into radians Alpha = Owner.Rotation.Yaw / 10430.2192; // Rotate it through TrailOffset.Z Alpha += (TrailOffset.Z / 180 * Pi); if (Alpha > 2 * Pi) Alpha -= 2 * Pi; // Calculate cylinder co'ordinates X = TrailOffset.X * Cos(Alpha); Y = TrailOffset.X * Sin(Alpha); Offset.X = X; Offset.Y = Y; Offset.Z = TrailOffset.Y; SetLocation(Owner.Location + Offset); } else SetLocation(Owner.Location); SetRotation(Owner.Rotation);
// Spawn additional trails as necessary if (Pos < MaxTrails && Next == None) { Next = Spawn(TrailerClass, self,, Location, Rotation); Next.Pos = Pos + 1; } }
// Handle updating of trail location simulated function Tick(float DeltaTime) { // Destroy if there's no owner if (Owner == None) Destroy();
// First trail should tell next trail to update & so on if (Pos > 0) return;
cntr += DeltaTime; // Lag location update by lagBy ticks if (cntr >= lagBy) { cntr = 0.0; Update(); } }
defaultproperties { cntr=0.0 Pos=0 lagBy=0.2 MaxTrails=4 Physics=PHYS_None bNetTemporary=False RemoteRole=ROLE_SimulatedProxy TrailerClass=class'Trailer' TrailOffset=vector(X=0,Y=0,Z=0) }
And now a demonstration of how to derive a new class from Trailer to implement whatever type of trail we desire. In this particular case a cool motion trail.
// Motion trails for something with a mesh class MeshTrailer extends Trailer;
#exec OBJ LOAD FILE=..\Textures\AlfaFX.utx
simulated function PostBeginPlay() { // Set mesh & anims Mesh = Owner.Mesh; AnimFrame = Owner.AnimFrame; AnimSequence = Owner.AnimSequence; AnimRate = Owner.AnimRate; TweenRate = Owner.TweenRate; AnimMinRate = Owner.AnimMinRate; AnimLast = Owner.AnimLast; bAnimLoop = Owner.bAnimLoop; bAnimFinished = Owner.bAnimFinished;
Super.PostBeginPlay(); }
// Update the trailer simulated function Update() { local vector Offset; local float Alpha, X, Y;
// Update animation AnimFrame = Owner.AnimFrame; AnimSequence = Owner.AnimSequence; AnimRate = Owner.AnimRate; TweenRate = Owner.TweenRate; AnimMinRate = Owner.AnimMinRate; AnimLast = Owner.AnimLast; bAnimLoop = Owner.bAnimLoop; bAnimFinished = Owner.bAnimFinished;
// Call Update() on next trail if (Next != None) Next.Update();
// Move to where the previous part of the trail was (or player location for that matter) if (Pos == 0) { // Convert Owner horizontal rotation into radians Alpha = Owner.Rotation.Yaw / 10430.2192; // Rotate it through TrailOffset.Z Alpha += (TrailOffset.Z / 180 * Pi); if (Alpha > 2 * Pi) Alpha -= 2 * Pi; // Calculate cylinder co'ordinates X = TrailOffset.X * Cos(Alpha); Y = TrailOffset.X * Sin(Alpha); Offset.X = X; Offset.Y = Y; Offset.Z = TrailOffset.Y; SetLocation(Owner.Location + Offset); } else SetLocation(Owner.Location); SetRotation(Owner.Rotation);
// Spawn additional trails as necessary if (Pos < MaxTrails && Next == None) { Next = Spawn(TrailerClass, self,, Location, Rotation); Next.Pos = Pos + 1; } }
defaultproperties { DrawType=DT_Mesh Style=STY_Translucent lagBy=0.1 MaxTrails=4 TrailerClass=class'MeshTrailer' Skin=FireTexture'AlfaFX.Lion3' }
All logos and trademarks are properties of their respective owners.
Unreal™ is a registered trademark of Epic Games Inc.
Privacy Policy
Website by Softly
Powered by RUSH