Overview | Package | Class | Source | Class tree | Glossary | UnrealScript Documentation |
previous class next class | frames no frames |
00001 00002 00003 00004 00005 00006 00007 00008 00009 00010 00011 00012 00013 00014 00015 00016 00017 00018 00019 00020 00021 00022 00023 00024 00025 00026 00027 00028 00029 00030 00031 00032 00033 00034 00035 00036 00037 00038 00039 00040 00041 00042 00043 00044 00045 00046 00047 00048 00049 00050 00051 00052 00053 00054 00055 00056 00057 00058 00059 00060 00061 00062 00063 00064 00065 00066 00067 00068 00069 00070 00071 00072 00073 00074 00075 00076 00077 00078 00079 00080 00081 00082 00083 00084 00085 00086 00087 00088 00089 00090 00091 00092 00093 00094 00095 00096 00097 00098 00099 00100 00101 00102 00103 00104 00105 00106 00107 00108 00109 00110 00111 00112 00113 00114 00115 00116 00117 00118 00119 00120 00121 00122 00123 00124 00125 00126 00127 00128 00129 00130 00131 00132 00133 00134 00135 00136 00137 00138 00139 00140 00141 00142 00143 00144 00145 00146 00147 00148 00149 00150 00151 00152 00153 00154 00155 00156 00157 00158 00159 00160 00161 00162 00163 00164 00165 00166 00167 00168 00169 00170 00171 00172 00173 00174 00175 00176 00177 00178 00179 00180 00181 00182 00183 00184 00185 00186 00187 00188 00189 00190 00191 00192 00193 00194 00195 00196 00197 00198 00199 00200 00201 00202 00203 00204 00205 00206 00207 00208 00209 00210 00211 00212 00213 00214 00215 00216 00217 00218 00219 00220 00221 00222 00223 00224 00225 00226 00227 00228 00229 00230 00231 00232 00233 00234 00235 00236 00237 00238 00239 00240 00241 00242 00243 00244 00245 00246 00247 00248 00249 00250 00251 00252 00253 00254 00255 00256 00257 00258 00259 00260 00261 00262 00263 00264 00265 00266 00267 00268 00269 00270 00271 00272 00273 00274 00275 00276 00277 00278 00279 00280 00281 00282 00283 00284 00285 00286 00287 00288 00289 00290 00291 00292 00293 00294 00295 00296 00297 00298 00299 00300 00301 00302 00303 00304 00305 00306 00307 00308 00309 00310 00311 00312 00313 00314 |
class UnderwaterRobot extends NauticVehicle config(USARBot); // Structure to hold both the part index and the part's force struct PartData { var int PartNumber; var float Force; }; // Configuration variable to be set in the defaultproperties section of the vehicle's class var float waterDensity; // Programming variables (these variables are transparent to the users) var float currentVel, totalVel, currentTime, oldTime; var bool atSurface, initialized; var array<PartData> Propellers, Rudders, SternPlanes; function ProcessCarInput() { // Programming variables local int i; local float maxSpeed, maxAngle, ControllerSpeed, ControllerRudder, ControllerSternPlane, InputSpeed, InputRudder, InputSternPlane; local bool isCommandNormalized; Super.ProcessCarInput(); // Initializes various variables and fills up arrays. Note: this section is only executed once. if(!initialized) { // Here, we get the propeller(s), rudder(s), and stern plane(s) into separate dynamic arrays for(i=0; i<JointParts.length; i++) { // Get propeller(s) if(InStr(Caps(string(JointParts[i].PartName)), "PROPELLER") != -1) { Propellers.Insert(Propellers.length, 1); // Make space in the dynamic array to add a propeller Propellers[Propellers.length-1].PartNumber = i; // Store the index number of the part in the dynamic array } // Get rudder(s) if(InStr(Caps(string(JointParts[i].PartName)), "RUDDER") != -1) { Rudders.Insert(Rudders.length, 1); // Make space in the dynamic array to add a rudder Rudders[Rudders.length-1].PartNumber = i; // Store the index number of the part in the dynamic array } // Get stern plane(s) if(InStr(Caps(string(JointParts[i].PartName)), "STERNPLANE") != -1) { SternPlanes.Insert(SternPlanes.length, 1); // Make space in the dynamic array to add a stern plane SternPlanes[SternPlanes.length-1].PartNumber = i; // Store the index number of the part in the dynamic array } } // Initialize the controller's properties USARRemoteBot(Controller).Normalized = false; USARRemoteBot(Controller).Propeller = 0.0; USARRemoteBot(Controller).Rudder = 0.0; USARRemoteBot(Controller).SternPLane = 0.0; initialized = true; // Section used for debugging purposes to see if the correct joints have been saved in the dynamic arrays if(bDebug) { for(i=0; i<Propellers.Length; i++) Log("Propeller #" $ i+1 $ ": " $ Propellers[i].PartNumber); for(i=0; i<Rudders.Length; i++) Log("Rudder #" $ i+1 $ ": " $ Rudders[i].PartNumber); for(i=0; i<SternPlanes.Length; i++) Log("Stern Plane #" $ i+1 $ ": " $ SternPlanes[i].PartNumber); } } currentTime = Level.TimeSeconds; // If a DRIVE command was issued if(USARRemoteBot(Controller).bNewThrottle) { // If the underwater robot is underwater (not at the surface) if(!atSurface) { isCommandNormalized = USARRemoteBot(Controller).Normalized; // Get the normalized bool from the controller ControllerSpeed = USARRemoteBot(Controller).Propeller; // Get the propeller's spin speed value from the controller ControllerRudder = USARRemoteBot(Controller).Rudder; // Get the rudder angle from the controller ControllerSternPlane = USARRemoteBot(Controller).SternPlane; // Get the stern plane angle from the controller // Here, we deal with the propeller(s) for(i=0; i<Propellers.Length; i++) { maxSpeed = Propeller(Parts[Propellers[i].PartNumber]).maxSpinSpeed; // Get the maximum spin speed for this propeller // If a normalized drive command was received (e.g. the propeller spin speed is between -100 and 100) if (isCommandNormalized) { if(ControllerSpeed < -100) InputSpeed = -maxSpeed; // If the normalized value is less than -100, we use the negative of the propeller's maximum spin speed else if(ControllerSpeed > 100) InputSpeed = maxSpeed; // If the normalized value is more than 100, we use the propeller's maximum spin speed else InputSpeed = (ControllerSpeed/100) * maxSpeed; // If the normalized value is between -100 and 100, we use a percentage of the maximum spin speed } // If a non-normalized drive command was received (e.g. the propeller spin speed is an absolute value) else { if(ControllerSpeed < -maxSpeed) InputSpeed = -maxSpeed; // If the absolute value is less than -(maximum speed), we use the negative of the propeller's maximum spin speed else if(ControllerSpeed > maxSpeed) InputSpeed = maxSpeed; // If the absolute value is more than the maximum speed, we use the propeller's maximum spin speed else InputSpeed = ControllerSpeed; // Otherwise, we use the controller's value for the propeller's spin speed } // Physically spin the propeller (spin the part) setSpinSpeed(Propellers[i].PartNumber, Converter.SpinSpeedToUU(InputSpeed)); // Find the velocity generated by this propeller currentVel = (InputSpeed/6.283185307179586476925286766559) * (Propeller(Parts[Propellers[i].PartNumber]).Pitch); // Add the velocity generated by this propeller to the velocity generated by the other propellers if(i==0) totalVel = currentVel; else totalVel += currentVel; // Find the force generated by this propeller Propellers[i].Force = getPropellerForce(); } // Here, we deal with the rudder(s) for(i=0; i<Rudders.Length; i++) { maxAngle = ControlSurface(Parts[Rudders[i].PartNumber]).maxAngle; // Get the maximum rudder angle for this rudder // If a normalized drive command was received (e.g. the rudder angle is between -100 and 100) if (isCommandNormalized) { if(ControllerRudder < -100) InputRudder = -maxAngle; // If the normalized value is less than -100, we use the negative of the maximum rudder angle else if(ControllerRudder > 100) InputRudder = maxAngle; // If the normalized value is more than 100, we use the maximum rudder angle else InputRudder = (ControllerRudder/100) * maxAngle; // If the normalized value is between -100 and 100, we use a percentage of the maximum rudder angle } // If a non-normalized drive command was received (e.g. the rudder angle is an absolute value) else { if(ControllerRudder < -maxAngle) InputRudder = -maxAngle; // If the absolute value is less than -(maximum angle), we use the negative of the rudder's maximum angle else if(ControllerRudder > maxAngle) InputRudder = maxAngle; // If the absolute value is more than the maximum angle, we use the rudder's maximum angle else InputRudder = ControllerRudder; // Otherwise, we use the controller's value for the rudder angle } // Physically rotate the rudder (rotate the part) setAngle(Rudders[i].PartNumber, Converter.AngleToUU(InputRudder)); // Find the side force generated by the rudder Rudders[i].Force = getSideForce(ControlSurface(Parts[Rudders[i].PartNumber]).Area, InputRudder, InputSpeed); } // Here, we deal with the stern plane(s) for(i=0; i<SternPlanes.Length; i++) { maxAngle = ControlSurface(Parts[SternPlanes[i].PartNumber]).maxAngle; // Get the maximum stern plane angle for this stern plane // If a normalized drive command was received (e.g. the stern plane angle is between -100 and 100) if(isCommandNormalized) { if(ControllerSternPlane < -100) InputSternPlane = -maxAngle; // If the normalized value is less than -100, we use the negative of the maximum stern plane angle else if(ControllerSternPlane > 100) InputSternPlane = maxAngle; // If the normalized value is more than 100, we use the maximum stern plane angle else InputSternPlane = (ControllerSternPlane/100) * maxAngle; // If the normalized value is between -100 and 100, we use a percentage of the maximum stern plane angle } // If a non-normalized drive command was received (e.g. the stern plane angle is an absolute value) else { if(ControllerSternPlane < -maxAngle) InputSternPlane = -maxAngle; // If the absolute value is less than -(maximum angle), we use the negative of the stern plane's maximum angle else if(ControllerSternPlane > maxAngle) InputSternPlane = maxAngle; // If the absolute value is more than the maximum angle, we use the stern plane's maximum angle else InputSternPlane = ControllerSternPlane; // Otherwise, we use the controller's value for the stern plane angle } // Physically rotate the stern plane (rotate the part) setAngle(SternPlanes[i].PartNumber, Converter.AngleToUU(InputSternPlane)); // Find the side force generated by the stern plane SternPlanes[i].Force = getSideForce(ControlSurface(Parts[SternPlanes[i].PartNumber]).Area, InputSternPlane, InputSpeed); } } oldTime = currentTime; KWake(); // Make sure that the Karma Engine is "awake" } } //********************************************************************************************************************* // getPropellerForce Function // -------------------------- // Function that calculates and returns the force generated by the underwater robot's propeller. //********************************************************************************************************************* function float getPropellerForce() { return (1.1 * currentVel * ChassisMass) / (currentTime - oldTime); } //********************************************************************************************************************* // getSideForce Function // -------------------------- // Function that calculates and returns the side force generated by the underwater robot's control surfaces. // The control surfaces can either be rudders or stern planes. The equation used is the drag equation. //********************************************************************************************************************* function float getSideForce(float SurfaceArea, float Angle, float Speed) { local float Force; // Calculate the side force generated by the part if(Sign(Angle) == Sign(Speed)) { Force = 0.5 * waterDensity * Square(totalVel) * getDragConstant(Angle) * SurfaceArea; } else { Force = -0.5 * waterDensity * Square(totalVel) * getDragConstant(Angle) * SurfaceArea; } return Force; } //********************************************************************************************************************* // Sign Function // ------------------------ // Helper function (for the getSideForce function) that returns the sign of the number passed as a parameter. // If the variable passed as a parameter is 0, the function returns 0. // If the variable passed as a parameter is positive, the function returns 1. // If the variable passed as a parameter is negative, the function returns -1. //********************************************************************************************************************* function int Sign(float theValue) { if(theValue == 0) return 0; return (theValue / Abs(theValue)); } //********************************************************************************************************************* // getDragConstant Function // ------------------------ // Helper function (for the getSideForce function) that calculates and returns the drag coefficient given // the angle of a part (e.g given the angle of a rudder or of a stern plane). //********************************************************************************************************************* function float getDragConstant(float angle) { local float X_2,X_3; angle = Converter.AngleToDeg(Abs(angle)); X_2 = angle * angle; X_3 = X_2 * angle; return ((0.00003 * X_3) - (0.0002 * X_2) + (0.0028 * angle)); } //********************************************************************************************************************* // KApplyForce Event // ----------------- // Event called at the same frequency of the Tick function, as long as the karma engine is "awake". // The event calculates the appropriate forces and torques, based on the underwater robot's rotation, which // are then automatically applied by the Karma Engine. //********************************************************************************************************************* event KApplyForce(out vector Force, out vector Torque) { local int i; local float PropellerForce, RudderTorque, SternPlaneTorque; // Calculate the total force generated by all the propellers PropellerForce = 0; for(i=0; i<Propellers.Length; i++) { PropellerForce += Propellers[i].Force; } // Calculate the total torque generated by all the rudders (Torque = Force * Length_from_center_of_mass) RudderTorque = 0; for(i=0; i<Rudders.Length; i++) { RudderTorque += (Rudders[i].Force * Converter.LengthFromUU(Sqrt(Square(Location.X - Parts[Rudders[i].PartNumber].Location.X) + Square(Location.Y - Parts[Rudders[i].PartNumber].Location.Y) + Square(Location.Z - Parts[Rudders[i].PartNumber].Location.Z)))); } // Calculate the total torque generated by all the stern planes (Torque = Force * Length_from_center_of_mass) SternPlaneTorque = 0; for(i=0; i<SternPlanes.Length; i++) { SternPlaneTorque += (SternPlanes[i].Force * Converter.LengthFromUU(Sqrt(Square(Location.X - Parts[SternPlanes[i].PartNumber].Location.X) + Square(Location.Y - Parts[SternPlanes[i].PartNumber].Location.Y) + Square(Location.Z - Parts[SternPlanes[i].PartNumber].Location.Z)))); } // Set the Force and Torque to be applied by the Karma Engine, based on the vehicle's rotation Force = (PropellerForce * vect(1,0,0)) >> Rotation; Torque = ((Vect(0,1,0) * SternPlaneTorque) + (Vect(0,0,1) * RudderTorque)) >> Rotation; } //********************************************************************************************************************* // PhysicsVolumeChange Event // ------------------------- // Event called whenever the submarine enters a new physics volume. // The event sets the atSurface boolean variable if the underwater robot is not in a water volume. //********************************************************************************************************************* simulated event PhysicsVolumeChange( PhysicsVolume NewVolume ) { Super.PhysicsVolumeChange(NewVolume); atSurface = !NewVolume.bWaterVolume; } //********************************************************************************************************************* // DEFAULT PROPERTIES // DO NOT change these properties since they are used to initialize programming variables //********************************************************************************************************************* defaultproperties { atSurface = false initialized = false } |
Overview | Package | Class | Source | Class tree | Glossary | UnrealScript Documentation |
previous class next class | frames no frames |