|
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 :|
TheoryThe 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.
ImplementationSo 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' }
|
|