UnrealShare
Class ObjectPath

source: e:\games\UnrealTournament\UnrealShare\Classes\ObjectPath.uc
Core.Object
   |
   +--Engine.Actor
      |
      +--Engine.Keypoint
         |
         +--UnrealShare.ObjectPath
Direct Known Subclasses:None

class ObjectPath
extends Engine.Keypoint

//============================================================================= // ObjectPath. //=============================================================================
Variables
 Actor PathActor
           what should be moved
 name PathActorTag
           The Tag of the actor which should be moved
 PathPoint Path[35]
           maximum 35 nodes in the path, hence 33 real positions
 rotator RAdjust
           Adjust the rotation of the object
 bool bAlterPitch
           should the pitch of the actor be modified during movement
 bool bAlterRoll
           should the roll ...
 bool bAlterYaw
           should the yaw ...
 bool bPlayedOnce
           Really don't play it again since it's already finished last time
 bool bTriggeredOnce
           Don't repeat the path if it's already played through
 int curNode
           Which node are we at?
 vector lastPosition
           Where the actor was in the most recent frame
 rotator lastRotation
           The orientation of the actor in the most recent frame
 int numPathNodes
           how many elements in the path array
 float uValue
           Offset in the segment


Function Summary
 void BeginPlay()
 void PostBeginPlay()
 void Tick(float DeltaTime)
 void Trigger(Actor Other, Pawn EventInstigator)



Source Code


00001	//=============================================================================
00002	// ObjectPath.
00003	//=============================================================================
00004	class ObjectPath extends Keypoint;
00005	
00006	// Allows an object to follow a defined path, by specifying
00007	// PathPoint nodes. M
00008	
00009	// Note: At least 4 PathPoints must exist.  The first and last
00010	//       do not need speed/deltaU settings.
00011	
00012	// Note: If there are N points to interpolate through, there must
00013	//       be in total N+2 points specified.  Point 0 and Point N+1
00014	//       are dummy control points, where Point 1-Point 0 is the 
00015	//       initial direction of motion, and Point N+1 - Point N is
00016	//       the final direction of motion.  The object will start at
00017	//       point 1 and end up at point N.
00018	
00019	// Uses the Bernstein basis functions for Bezier interpolation:
00020	//   B0(u) = (1-u)^3
00021	//   B1(u) = 3u(1-u)^2
00022	//   B2(u) = 3u^2(1-u)
00023	//   B3(u) = u^3
00024	
00025	var() name     PathActorTag; 	// The Tag of the actor which should be moved
00026	var() bool     bAlterPitch;	// should the pitch of the actor be modified during movement
00027	var() bool     bAlterYaw;		// should the yaw ...
00028	var() bool     bAlterRoll;		// should the roll ...
00029	var() rotator  RAdjust;		// Adjust the rotation of the object
00030	
00031	//var() bool  bLoopMotion;	// The last PathPoint should lead to the first
00032	
00033	var Actor   	PathActor;		// what should be moved
00034	var PathPoint Path[35];		// maximum 35 nodes in the path, hence 33 real positions 
00035	var int 		numPathNodes;		// how many elements in the path array
00036	var int 		curNode;			// Which node are we at?
00037	var float 	uValue;			// Offset in the segment
00038	var bool 	bTriggeredOnce;	// Don't repeat the path if it's already played through
00039	var bool		bPlayedOnce;		// Really don't play it again since it's already finished last time
00040	var vector 	lastPosition;		// Where the actor was in the most recent frame
00041	var rotator lastRotation;		// The orientation of the actor in the most recent frame
00042	
00043	
00044	function BeginPlay()
00045	{
00046		local int i, l;
00047		local PathPoint tempPP;
00048		
00049		// Find all relevant PathPoint elements and load them 
00050		// into the temporary array (in sorted order).  Also set up
00051		// velocities, etc..
00052		// Wait until something triggers the motion
00053		Disable('Tick');
00054		bTriggeredOnce = false;
00055		bPlayedOnce = true;
00056	
00057		// Find the object which must be moved
00058		PathActor = None;
00059		foreach AllActors (class 'Actor', PathActor)
00060		{
00061			if( PathActor.Tag == PathActorTag )
00062				// found the matching Actor
00063				break;	
00064		}
00065		if( PathActor == None ) 
00066		{
00067			log("ObjectPath: No object to be moved.  Aborting.");
00068			Destroy();
00069			return;
00070		}
00071	
00072		// Find all the Path Nodes.
00073		numPathNodes=0;
00074		foreach AllActors (class 'PathPoint', tempPP) 
00075		{
00076			// This PathPoint must have the same tag as this ObjectPath actor
00077			if (tempPP.Tag == Tag) {
00078			
00079				Path[numPathNodes] = tempPP;
00080				numPathNodes++;	
00081				// Make sure that the user didn't specify too many points
00082				if (numPathNodes > 35) {
00083					log ("ObjectPath: Maximum number of path elements exceeded.  Aborting.");
00084					log ("            Tag = " $ tempPP.Tag);
00085					Destroy();
00086					return;
00087				}		
00088			}
00089		}
00090		
00091		// Make sure that there are at least four PathPoint nodes.
00092		if (numPathNodes < 4)
00093		{
00094			log("ObjectPath: Not enough PathPoints specified.  Needed 4.  Aborting.");
00095			Destroy();
00096			return;
00097		}
00098	
00099		// Now sort the elements (using a crappy bubble sort.. hey it's < 30 elements)
00100		for (i=0; i<numPathNodes-1; i++) {
00101			for (l=i+1; l<numPathNodes; l++) {
00102				if (Path[i].sequence_Number > Path[l].sequence_Number) {
00103				
00104					// switch them
00105					tempPP  = Path[i];
00106					Path[i] = Path[l];
00107					Path[l] = tempPP;
00108				}		
00109			}
00110		}
00111	
00112		// Precalculate the direction vectors at each point, based on 
00113		// relative position and curveSpeed.
00114		// The first (1) and last (N-1) segments' velocities are
00115		// related to the (0) and (N)'th points respectively.
00116		// first segment
00117		Path[1].pVelocity = Normal(Path[1].Location - 
00118						  		  Path[0].Location) * Path[1].curvespeed;
00119		// last segment
00120		Path[numPathNodes-2].pVelocity = Normal(Path[numPathNodes-1].Location - 
00121									    	    Path[numPathNodes-2].Location) * Path[numPathNodes-2].curvespeed;
00122	
00123		// The tangent of the middle nodes is parallel to the vector
00124		// between the previous and next nodes.
00125		for (i=2; i<=numPathNodes-3; i++) 
00126		{
00127			Path[i].pVelocity = Normal(Path[i+1].Location - 
00128								      Path[i-1].Location) * Path[i].curvespeed;
00129		}
00130	
00131		Enable('Trigger');
00132	}
00133	
00134	function PostBeginPlay()
00135	{
00136		Super.PostBeginPlay();
00137	
00138		// Set the object at the initial position and orientation, so that it doesn't
00139		// suddenly jerk when triggered to move.
00140		PathActor.RemoteRole = ROLE_DumbProxy;
00141		PathActor.SetLocation( Path[1].Location );
00142	}
00143	
00144	function Trigger( actor Other, pawn EventInstigator )
00145	{
00146		// Play the motion if it hasn't occured yet
00147		if( !bTriggeredOnce )
00148		{
00149			// Set update parameters
00150			bTriggeredOnce = true;
00151			bPlayedOnce = false;
00152			curNode = 1;
00153			uValue = 0;
00154			
00155			// Put the actor at the initial position
00156			PathActor.SetLocation( Path[1].Location );
00157			//PathActor.SetRotation( RAdjust );
00158			//PathActor.SetPhysics( PHYS_None );
00159	
00160			// So that the orientation of the object can be set immediately
00161			lastPosition = Path[0].Location;
00162			lastRotation = PathActor.Rotation;
00163			
00164			// Start the motion
00165			Enable('Tick');
00166		}
00167	}
00168	
00169	function Tick( float DeltaTime )
00170	{
00171		local float 		curSpeedU;
00172		local vector		actorPosition;
00173		local rotator 	    actorRotation;
00174	
00175		if( bPlayedOnce )
00176		{
00177			Disable('Tick');
00178			return;
00179		}
00180	
00181		// Update the position of the object, based on DeltaTime and it's
00182		// index in the sequence of positions.
00183			
00184		// Interpolate the deltaU value from this to the next position,
00185		// based on the current U value in the segment.
00186		// FIXME: This is not exact by far, and would cause discontinuities
00187		//        in velocity if the arclengths of the bezier's are different.
00188		curSpeedU = (Path[curNode+1].speedU - Path[curNode].speedU)*uValue +
00189					Path[curNode].speedU;
00190	
00191		uValue += curSpeedU * DeltaTime;
00192		
00193		// Check if the difference sends the point to the next segment (or the one after that, etc..)
00194		while( uValue >= 1 && curNode < numPathNodes-2 )
00195		{
00196			curNode++;
00197			uValue -= 1;
00198		}
00199	
00200		// Check if the point is beyond, or at the end of the path
00201		if( curNode >= numPathNodes-2 )
00202		{
00203			// Set the position of the object to the position of the last node.
00204			PathActor.SetLocation( Path[numPathNodes-2].Location );
00205			PathActor.GotoState('');
00206	
00207			// Don't ever play it again!!!
00208			bPlayedOnce = true;
00209			
00210			// Finished the path, discontinue the updates
00211			Disable( 'Tick' );
00212			Disable( 'Trigger' );
00213			return;
00214		}
00215						
00216		// Calculate the position of the object based on the current node index
00217		// and U offset in the curve segment.
00218		//   B0(u) = (1-u)^3				Bernstein basis polys
00219		//   B1(u) = 3u(1-u)^2
00220		//   B2(u) = 3u^2(1-u)
00221		//   B3(u) = u^3
00222		actorPosition  = Path[curNode].Location * ((1 - uValue)**3) +
00223						(Path[curNode].Location + Path[curNode].pVelocity) *
00224						 (3*uValue* ((1-uValue)**2)) +
00225						(Path[curNode+1].Location - Path[curNode+1].pVelocity) *
00226						 (3*(uValue**2)* (1-uValue)) +
00227						Path[curNode+1].Location * (uValue**3);
00228		
00229		PathActor.Move( actorPosition - PathActor.Location );
00230			
00231		// Calculate new Pitch, Yaw, Roll values if applicable.
00232		actorRotation = rotator( actorPosition - lastPosition );
00233		actorRotation += RAdjust;
00234	
00235		if( !bAlterPitch ) actorRotation.Pitch = lastRotation.Pitch;
00236		if( !bAlterYaw   ) actorRotation.Yaw   = lastRotation.Yaw;
00237		// The roll is proportional to the change in yaw (airplane turning effect)
00238		if( !bAlterRoll  ) actorRotation.Roll  = lastRotation.Roll;
00239		              else actorRotation.Roll  = 0.4*(actorRotation.Yaw - lastRotation.Yaw);
00240	
00241		PathActor.SetRotation( actorRotation );
00242		
00243		// Save this frame's position/yaw for next frame calculations
00244		lastRotation = actorRotation;
00245		lastPosition = actorPosition;
00246	}
00247	
00248	defaultproperties
00249	{
00250	}

End Source Code