AeroplaneController.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. using System;
  2. using UnityEngine;
  3. namespace UnityStandardAssets.Vehicles.Aeroplane
  4. {
  5. [RequireComponent(typeof (Rigidbody))]
  6. public class AeroplaneController : MonoBehaviour
  7. {
  8. [SerializeField] private float m_MaxEnginePower = 40f; // The maximum output of the engine.
  9. [SerializeField] private float m_Lift = 0.002f; // The amount of lift generated by the aeroplane moving forwards.
  10. [SerializeField] private float m_ZeroLiftSpeed = 300; // The speed at which lift is no longer applied.
  11. [SerializeField] private float m_RollEffect = 1f; // The strength of effect for roll input.
  12. [SerializeField] private float m_PitchEffect = 1f; // The strength of effect for pitch input.
  13. [SerializeField] private float m_YawEffect = 0.2f; // The strength of effect for yaw input.
  14. [SerializeField] private float m_BankedTurnEffect = 0.5f; // The amount of turn from doing a banked turn.
  15. [SerializeField] private float m_AerodynamicEffect = 0.02f; // How much aerodynamics affect the speed of the aeroplane.
  16. [SerializeField] private float m_AutoTurnPitch = 0.5f; // How much the aeroplane automatically pitches when in a banked turn.
  17. [SerializeField] private float m_AutoRollLevel = 0.2f; // How much the aeroplane tries to level when not rolling.
  18. [SerializeField] private float m_AutoPitchLevel = 0.2f; // How much the aeroplane tries to level when not pitching.
  19. [SerializeField] private float m_AirBrakesEffect = 3f; // How much the air brakes effect the drag.
  20. [SerializeField] private float m_ThrottleChangeSpeed = 0.3f; // The speed with which the throttle changes.
  21. [SerializeField] private float m_DragIncreaseFactor = 0.001f; // how much drag should increase with speed.
  22. public float Altitude { get; private set; } // The aeroplane's height above the ground.
  23. public float Throttle { get; private set; } // The amount of throttle being used.
  24. public bool AirBrakes { get; private set; } // Whether or not the air brakes are being applied.
  25. public float ForwardSpeed { get; private set; } // How fast the aeroplane is traveling in it's forward direction.
  26. public float EnginePower { get; private set; } // How much power the engine is being given.
  27. public float MaxEnginePower{ get { return m_MaxEnginePower; }} // The maximum output of the engine.
  28. public float RollAngle { get; private set; }
  29. public float PitchAngle { get; private set; }
  30. public float RollInput { get; private set; }
  31. public float PitchInput { get; private set; }
  32. public float YawInput { get; private set; }
  33. public float ThrottleInput { get; private set; }
  34. private float m_OriginalDrag; // The drag when the scene starts.
  35. private float m_OriginalAngularDrag; // The angular drag when the scene starts.
  36. private float m_AeroFactor;
  37. private bool m_Immobilized = false; // used for making the plane uncontrollable, i.e. if it has been hit or crashed.
  38. private float m_BankedTurnAmount;
  39. private Rigidbody m_Rigidbody;
  40. WheelCollider[] m_WheelColliders;
  41. private void Start()
  42. {
  43. m_Rigidbody = GetComponent<Rigidbody>();
  44. // Store original drag settings, these are modified during flight.
  45. m_OriginalDrag = m_Rigidbody.drag;
  46. m_OriginalAngularDrag = m_Rigidbody.angularDrag;
  47. for (int i = 0; i < transform.childCount; i++ )
  48. {
  49. foreach (var componentsInChild in transform.GetChild(i).GetComponentsInChildren<WheelCollider>())
  50. {
  51. componentsInChild.motorTorque = 0.18f;
  52. }
  53. }
  54. }
  55. public void Move(float rollInput, float pitchInput, float yawInput, float throttleInput, bool airBrakes)
  56. {
  57. // transfer input parameters into properties.s
  58. RollInput = rollInput;
  59. PitchInput = pitchInput;
  60. YawInput = yawInput;
  61. ThrottleInput = throttleInput;
  62. AirBrakes = airBrakes;
  63. ClampInputs();
  64. CalculateRollAndPitchAngles();
  65. AutoLevel();
  66. CalculateForwardSpeed();
  67. ControlThrottle();
  68. CalculateDrag();
  69. CaluclateAerodynamicEffect();
  70. CalculateLinearForces();
  71. CalculateTorque();
  72. CalculateAltitude();
  73. }
  74. private void ClampInputs()
  75. {
  76. // clamp the inputs to -1 to 1 range
  77. RollInput = Mathf.Clamp(RollInput, -1, 1);
  78. PitchInput = Mathf.Clamp(PitchInput, -1, 1);
  79. YawInput = Mathf.Clamp(YawInput, -1, 1);
  80. ThrottleInput = Mathf.Clamp(ThrottleInput, -1, 1);
  81. }
  82. private void CalculateRollAndPitchAngles()
  83. {
  84. // Calculate roll & pitch angles
  85. // Calculate the flat forward direction (with no y component).
  86. var flatForward = transform.forward;
  87. flatForward.y = 0;
  88. // If the flat forward vector is non-zero (which would only happen if the plane was pointing exactly straight upwards)
  89. if (flatForward.sqrMagnitude > 0)
  90. {
  91. flatForward.Normalize();
  92. // calculate current pitch angle
  93. var localFlatForward = transform.InverseTransformDirection(flatForward);
  94. PitchAngle = Mathf.Atan2(localFlatForward.y, localFlatForward.z);
  95. // calculate current roll angle
  96. var flatRight = Vector3.Cross(Vector3.up, flatForward);
  97. var localFlatRight = transform.InverseTransformDirection(flatRight);
  98. RollAngle = Mathf.Atan2(localFlatRight.y, localFlatRight.x);
  99. }
  100. }
  101. private void AutoLevel()
  102. {
  103. // The banked turn amount (between -1 and 1) is the sine of the roll angle.
  104. // this is an amount applied to elevator input if the user is only using the banking controls,
  105. // because that's what people expect to happen in games!
  106. m_BankedTurnAmount = Mathf.Sin(RollAngle);
  107. // auto level roll, if there's no roll input:
  108. if (RollInput == 0f)
  109. {
  110. RollInput = -RollAngle*m_AutoRollLevel;
  111. }
  112. // auto correct pitch, if no pitch input (but also apply the banked turn amount)
  113. if (PitchInput == 0f)
  114. {
  115. PitchInput = -PitchAngle*m_AutoPitchLevel;
  116. PitchInput -= Mathf.Abs(m_BankedTurnAmount*m_BankedTurnAmount*m_AutoTurnPitch);
  117. }
  118. }
  119. private void CalculateForwardSpeed()
  120. {
  121. // Forward speed is the speed in the planes's forward direction (not the same as its velocity, eg if falling in a stall)
  122. var localVelocity = transform.InverseTransformDirection(m_Rigidbody.velocity);
  123. ForwardSpeed = Mathf.Max(0, localVelocity.z);
  124. }
  125. private void ControlThrottle()
  126. {
  127. // override throttle if immobilized
  128. if (m_Immobilized)
  129. {
  130. ThrottleInput = -0.5f;
  131. }
  132. // Adjust throttle based on throttle input (or immobilized state)
  133. Throttle = Mathf.Clamp01(Throttle + ThrottleInput*Time.deltaTime*m_ThrottleChangeSpeed);
  134. // current engine power is just:
  135. EnginePower = Throttle*m_MaxEnginePower;
  136. }
  137. private void CalculateDrag()
  138. {
  139. // increase the drag based on speed, since a constant drag doesn't seem "Real" (tm) enough
  140. float extraDrag = m_Rigidbody.velocity.magnitude*m_DragIncreaseFactor;
  141. // Air brakes work by directly modifying drag. This part is actually pretty realistic!
  142. m_Rigidbody.drag = (AirBrakes ? (m_OriginalDrag + extraDrag)*m_AirBrakesEffect : m_OriginalDrag + extraDrag);
  143. // Forward speed affects angular drag - at high forward speed, it's much harder for the plane to spin
  144. m_Rigidbody.angularDrag = m_OriginalAngularDrag*ForwardSpeed;
  145. }
  146. private void CaluclateAerodynamicEffect()
  147. {
  148. // "Aerodynamic" calculations. This is a very simple approximation of the effect that a plane
  149. // will naturally try to align itself in the direction that it's facing when moving at speed.
  150. // Without this, the plane would behave a bit like the asteroids spaceship!
  151. if (m_Rigidbody.velocity.magnitude > 0)
  152. {
  153. // compare the direction we're pointing with the direction we're moving:
  154. m_AeroFactor = Vector3.Dot(transform.forward, m_Rigidbody.velocity.normalized);
  155. // multipled by itself results in a desirable rolloff curve of the effect
  156. m_AeroFactor *= m_AeroFactor;
  157. // Finally we calculate a new velocity by bending the current velocity direction towards
  158. // the the direction the plane is facing, by an amount based on this aeroFactor
  159. var newVelocity = Vector3.Lerp(m_Rigidbody.velocity, transform.forward*ForwardSpeed,
  160. m_AeroFactor*ForwardSpeed*m_AerodynamicEffect*Time.deltaTime);
  161. m_Rigidbody.velocity = newVelocity;
  162. // also rotate the plane towards the direction of movement - this should be a very small effect, but means the plane ends up
  163. // pointing downwards in a stall
  164. m_Rigidbody.rotation = Quaternion.Slerp(m_Rigidbody.rotation,
  165. Quaternion.LookRotation(m_Rigidbody.velocity, transform.up),
  166. m_AerodynamicEffect*Time.deltaTime);
  167. }
  168. }
  169. private void CalculateLinearForces()
  170. {
  171. // Now calculate forces acting on the aeroplane:
  172. // we accumulate forces into this variable:
  173. var forces = Vector3.zero;
  174. // Add the engine power in the forward direction
  175. forces += EnginePower*transform.forward;
  176. // The direction that the lift force is applied is at right angles to the plane's velocity (usually, this is 'up'!)
  177. var liftDirection = Vector3.Cross(m_Rigidbody.velocity, transform.right).normalized;
  178. // The amount of lift drops off as the plane increases speed - in reality this occurs as the pilot retracts the flaps
  179. // shortly after takeoff, giving the plane less drag, but less lift. Because we don't simulate flaps, this is
  180. // a simple way of doing it automatically:
  181. var zeroLiftFactor = Mathf.InverseLerp(m_ZeroLiftSpeed, 0, ForwardSpeed);
  182. // Calculate and add the lift power
  183. var liftPower = ForwardSpeed*ForwardSpeed*m_Lift*zeroLiftFactor*m_AeroFactor;
  184. forces += liftPower*liftDirection;
  185. // Apply the calculated forces to the the Rigidbody
  186. m_Rigidbody.AddForce(forces);
  187. }
  188. private void CalculateTorque()
  189. {
  190. // We accumulate torque forces into this variable:
  191. var torque = Vector3.zero;
  192. // Add torque for the pitch based on the pitch input.
  193. torque += PitchInput*m_PitchEffect*transform.right;
  194. // Add torque for the yaw based on the yaw input.
  195. torque += YawInput*m_YawEffect*transform.up;
  196. // Add torque for the roll based on the roll input.
  197. torque += -RollInput*m_RollEffect*transform.forward;
  198. // Add torque for banked turning.
  199. torque += m_BankedTurnAmount*m_BankedTurnEffect*transform.up;
  200. // The total torque is multiplied by the forward speed, so the controls have more effect at high speed,
  201. // and little effect at low speed, or when not moving in the direction of the nose of the plane
  202. // (i.e. falling while stalled)
  203. m_Rigidbody.AddTorque(torque*ForwardSpeed*m_AeroFactor);
  204. }
  205. private void CalculateAltitude()
  206. {
  207. // Altitude calculations - we raycast downwards from the aeroplane
  208. // starting a safe distance below the plane to avoid colliding with any of the plane's own colliders
  209. var ray = new Ray(transform.position - Vector3.up*10, -Vector3.up);
  210. RaycastHit hit;
  211. Altitude = Physics.Raycast(ray, out hit) ? hit.distance + 10 : transform.position.y;
  212. }
  213. // Immobilize can be called from other objects, for example if this plane is hit by a weapon and should become uncontrollable
  214. public void Immobilize()
  215. {
  216. m_Immobilized = true;
  217. }
  218. // Reset is called via the ObjectResetter script, if present.
  219. public void Reset()
  220. {
  221. m_Immobilized = false;
  222. }
  223. }
  224. }