Код:
 Appendix A Helicopter Script
 
 /*  csHelicopter2 written by N. E. Topham 10 Jun 15
 * Based on work by:
 * Andrew Gotow: http://andrewgotow.com/unity3D-helicopter-tutorial/
 * joeybubble1: http://answers.unity3D.com/questions/447776/helicopter-physics-c.html;
 * GAPH: https://www.assetstore.unity3D.com/en/#!/content/27229
 *
 * The script produced a flight model for a helicopter that manoeuvres
 * in a similar manner to a drone.  It auto-hovers and maintains
 * altitude.  The main motor and sub motor game objects, should be
 * mapped to the rotating blade assembly, and ‘rb’ should link to the
 * main rigid body.  Adjusting the climbrate changes the vertical
 * speeds.
 */
 
 using UnityEngine;
 using UnityEngine.UI;
 using System.Collections;
 
 public class csHellicopter2 : MonoBehaviour {
 
 public KeyCode UpKey;
 public KeyCode DownKey;
 public KeyCode FrontSpin;
 public KeyCode BackWardSpin;
 public KeyCode LeftTurn;
 public KeyCode RightTurn;
 public KeyCode LeftSpin;
 public KeyCode RightSpin;
 public KeyCode EngineOnOffKey;
 
 //Helicopter Big Propeller. Use Y Rotate Value.
 public GameObject[] MainMotor; 
 //Helicopter Small Propeller. User X RotateValue.    
 public GameObject[] SubMotor; 
 //Helicopter Engine Sound.
 public AudioSource _AudioSource; 
 
 public float MainMotorRotateVelocity;
 public float SubMotorRotateVelocity;
 
 //Rigidbody variable for controlling drag, high drag needed to retard motion, low drag needed to prevent 'floating' when engine is off.
 public Rigidbody rb;        
     
 /*  Not currently used
 * //Text for de-bugging.
 * public GUIElement pitchInputValue;
 * public GUIElement VertV;
 * public GUIElement PitchAngle;
 */
     
 float VerticalVelocity = 0.0f; 
 float MainMotorRotation = 0.0f;
 float SubMotorRotation = 0.0f;
     
 public float ClimbRate;
 float AltitudeInput;
 
 
 //Pitch Values
 float Pitch;
 float PitchInput;
 float forwardSpeed;
 
 
 // Yaw Variables
 float Yaw = 150;
 float LeftRightTurn;
 
 // Roll Variables
 float Roll;
 float LeftRightSpin;
 float sideSpeed;
 
 bool EngineOn = false;
 float EngineValue = 0.0f;
 
 //Runs when script first called
 void start(){
 rb = GetComponent<Rigidbody> ();
     
 }
 
 //Runs every physics step, always a fixed interval, best used for rigidbody physics updates.
 void FixedUpdate(){
     
 //MotorVelocityContorl(); // Check Helicoptor AltitudeInput State
 
 if (EngineOn) {
 //Applies force on the current vertical axis of the Helicopter
 GetComponent<Rigidbody>().AddRelativeForce (Vector3.up * ClimbRate * VerticalVelocity);             
         GetComponent<Rigidbody>().AddRelativeForce (Vector3.right * sideSpeed);
         // additional force to counter altitude loss – note can result in climbing if not at full roll or when used with pitch
 GetComponent<Rigidbody>().AddForce (Vector3.up *Mathf.Abs (sideSpeed*0.33f)); 
         GetComponent<Rigidbody>().AddRelativeForce (Vector3.forward * forwardSpeed);
         GetComponent<Rigidbody>().AddForce (Vector3.up *Mathf.Abs (forwardSpeed*0.33f));
         // High drag to damp motion
 rb.drag = 2;
 
         //Sets Max Altitude
 if (transform.position.y > 500) 
 //At max altitude reverses keyboard input
 GetComponent<Rigidbody>().AddRelativeForce (-Vector3.up * ClimbRate * VerticalVelocity);       
 
             AltitudeInput = KeyValue (DownKey, UpKey);  
 
          PitchInput = KeyValue (BackWardSpin, FrontSpin);
         LeftRightTurn = KeyValue (LeftTurn, RightTurn);
         LeftRightSpin = KeyValue (LeftSpin, RightSpin);
            
         //Pitch Value
         if (PitchInput != 0) {
             Pitch += PitchInput * Time.fixedDeltaTime * 60f;
 //Limits the Pitch to a value of 20. 
 Pitch = Mathf.Clamp (Pitch, -20f, 20f); 
             //additional forwards force to improve lateral movement, adjusting the integer increases the speed.
 forwardSpeed = PitchInput * 7; 
 //Constant initially 50 adjusted to 7 after rescaling SE.
 } else {
     Pitch = Mathf.Lerp (Pitch, 0, Time.fixedDeltaTime * 3.0f);
             // reset forward speed value
 forwardSpeed = 0.0f; 
         }
 
         //Yaw Value
         //Yaw is unlimited allowing 360deg rotation
         Yaw += LeftRightTurn * Time.fixedDeltaTime * 60f;        
 
         //Roll Value
         if (LeftRightSpin != 0) {
             Roll += -LeftRightSpin * Time.fixedDeltaTime * 60f;
             //Limits the roll to a value of 1.2
 Roll = Mathf.Clamp (Roll, -20f, 20f);
 //additional sideways force to improve lateral movement 
             sideSpeed = LeftRightSpin * 7; 
 //Constant initially 50 adjusted to 7 after rescaling SE.
                          
         } else {
             //Smooth the Roll back to 0
 Roll = Mathf.Lerp (Roll, 0, Time.fixedDeltaTime * 3.0f);    
         //Reset the sideSpeed value - note, this line may not be required if the sideSpeed were taken outside of the if statement
 sideSpeed = 0.0f;    
         }
 
 //Transforms from current orientation to desired new rotation, indicated by Pitch Yaw and Roll Values with a 1.5 Second smoothing factor
 transform.rotation = Quaternion.Slerp (transform.rotation, Quaternion.Euler (Pitch, Yaw, Roll), Time.fixedDeltaTime * 1.5f);  
             
 
 /* Not Used in main system
         //Debugging outputs 
         PitchAngle.GetComponent<GUIText>().text = "Pitch Angle = " + transform.rotation;
         VertV.GetComponent<GUIText>().text = "Vertical Velocity = " + VerticalVelocity;
         pitchInputValue.GetComponent<GUIText>().text = "Pitch Input =" + PitchInput;
         */
 
     else {
         rb.drag=0;
     }
 }
     
 // Update fires every Frame, time step dependent upon framerate
 void Update (){
 SoundControl(); // Check Helicopter Engine Sound
 MotorRotateControl(); // Check Motor Rotate State
 MotorVelocityContorl(); // Check Helicopter AltitudeInput State
 EngineControl(); // Check Engine Turn On/Off
 }
 
 //Key input script, originally used to smooth inputs, no longer strictly required, but retained to allow future adjustments, maybe required for gesture controls.
 float KeyValue(KeyCode A, KeyCode B){
     
 float Value = 0.0f;
 
 if (Input.GetKey (A))
 Value = -1.0f;
 
     else if (Input.GetKey (B))
 Value = 1.0f;
 
     else
         Value = 0;//As soon as keys are released returns a 0 value
 
 return Value;
 }
 
 void MotorVelocityContorl(){
 
 if (EngineOn){
 //for maintain altitude.
 float Hovering = (Mathf.Abs(GetComponent<Rigidbody>().mass * Physics.gravity.y) / ClimbRate); 
 
 if (AltitudeInput != 0.0f)
 //if Input Up/Down Axes, increase VerticalVelocity for Increase altitude.
                 VerticalVelocity += AltitudeInput * 0.1f; 
             else
             //if no input on Vertical Axes revert to Hovering            VerticalVelocity = Hovering; 
 
 
 /* Original code included a smoothing function to gradually shift to hovering, when combined with the physics inertial effects this resulted in over-smoothing.  
 *  To resolve this the code line below was replaced with 'Hovering;'
 * 
 * Mathf.Lerp(VerticalVelocity, Hovering, Time.deltaTime); 
 * 
 * This resulted in improved vertical handling.
 */
 
 }
 CheckVerticalVelocity();
 }
 
 //Prevents verticalVelocitiy exceeding 1
 void CheckVerticalVelocity(){
 if (VerticalVelocity > 1.0f)
 VerticalVelocity = 1.0f;
 else if (VerticalVelocity < -0.5f)
 //When set to zero no reduction in decent speed.
 VerticalVelocity = -0.5f;  
 }
 
 //Spins rotors with smoothed start and stop animation
 void MotorRotateControl(){
 if (MainMotor.Length > 0){
 for (int i = 0; i < MainMotor.Length; i++)
 MainMotor[i].transform.Rotate(0, MainMotorRotation, 0);
 }
 
 if (SubMotor.Length > 0){
 for (int i = 0; i < SubMotor.Length; i++)
 SubMotor[i].transform.Rotate(SubMotorRotation, 0, 0);
 }
 
 MainMotorRotation = MainMotorRotateVelocity * EngineValue * Time.deltaTime;
 SubMotorRotation = SubMotorRotateVelocity * EngineValue * Time.deltaTime;
 }
 
 //Engine Control script, turns the engines on and off.
 void EngineControl(){
 if (Input.GetKeyDown(EngineOnOffKey)){
         if (EngineOn)
 EngineOn = false;
 else
 EngineOn = true;
 }
 
 if (EngineOn == true){
 if (EngineValue < 1)
 EngineValue += Time.deltaTime;
 if (EngineValue >= 1)
 EngineValue = 1;
 }
 
 else if (EngineOn == false){
 if (EngineValue > 0)
 EngineValue -= Time.deltaTime / 2;
 if (EngineValue <= 0)
 EngineValue = 0;
 }
 }
 
 
 //Sound Control script, varies pitch based on engine speed
 void SoundControl(){
 if (EngineValue > 0){
 _AudioSource.mute = false;
 _AudioSource.pitch = EngineValue;
 }
 else
 _AudioSource.mute = true;
 }
 }