Core.Object | +--Engine.Actor | +--Engine.Keypoint | +--UnrealShare.ObjectPath
Actor
PathActor
name
PathActorTag
PathPoint
Path[35]
rotator
RAdjust
bool
bAlterPitch
bAlterRoll
bAlterYaw
bPlayedOnce
bTriggeredOnce
int
curNode
vector
lastPosition
lastRotation
numPathNodes
float
uValue
void
BeginPlay()
PostBeginPlay()
Tick(float DeltaTime)
Trigger(Actor Other, Pawn EventInstigator)
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 }