RbBicycleController.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. using System;
  2. using UnityEngine;
  3. using Wheels;
  4. namespace Controller.Bicycle
  5. {
  6. [Serializable]
  7. public struct SlopeAdjustmentOptions
  8. {
  9. [Range(0, 3)] public float speedFactorUphill;
  10. [Range(0, 3)] public float speedFactorDownhill;
  11. public float timeToMaxPhysicsIntensity;
  12. }
  13. public class RbBicycleController : BicycleControllerBaseBehaviour, IBicycleController
  14. {
  15. #region Variables
  16. private const float THRESHOLD_GRADIENT_ZERO = .05f;
  17. private Transform rbTransform;
  18. private float currentSteerAngle;
  19. private float currentLeaningAngle;
  20. private float currentSpeedSensed;
  21. private float currentSpeedAdjusted;
  22. private float speedAtLastSlopeChange = -1f;
  23. private float lastSlopeChangeTimetamp = -1f;
  24. [Header("Slope Impact")] public bool adjustSpeedToSlope = true;
  25. public LerpSlopeCollider slopeCollider;
  26. public SlopeAdjustmentOptions slopeAdjustmentOptions;
  27. public Vector3 Forward => rbTransform.forward;
  28. public Vector3 Right => -rbTransform.right;
  29. public Vector3 Up => rbTransform.up;
  30. public BicycleControllerMode ControllerMode
  31. {
  32. get => controllerMode;
  33. set => controllerMode = value;
  34. }
  35. public float CurrentSpeed
  36. {
  37. get => adjustSpeedToSlope ? currentSpeedAdjusted : currentSpeedSensed;
  38. set => currentSpeedSensed = Mathf.Clamp(value, 0, maxSpeed);
  39. }
  40. public float CurrentSpeedKph => CurrentSpeed * 3.6f;
  41. public float CurrentSteerAngle
  42. {
  43. get => currentSteerAngle;
  44. set => currentSteerAngle = Mathf.Clamp(value, -maxSteeringAngle, maxSteeringAngle);
  45. }
  46. public float CurrentLeaningAngle
  47. {
  48. get => currentLeaningAngle;
  49. set
  50. {
  51. //don't lean while standing / walking to bike
  52. if (rigidBody.velocity.magnitude < .5f) return;
  53. currentLeaningAngle = Mathf.Clamp(value, -maxLeaningAngle, maxLeaningAngle);
  54. }
  55. }
  56. public Vector3 RigidBodyVelocity => rigidBody.velocity;
  57. #endregion
  58. private void Awake()
  59. {
  60. rbTransform = rigidBody.transform;
  61. rigidBody.freezeRotation = true;
  62. rigidBody.centerOfMass = centerOfMass.position;
  63. }
  64. private void Start()
  65. {
  66. slopeCollider.OnSlopeChanged += delegate(float timestamp)
  67. {
  68. lastSlopeChangeTimetamp = timestamp;
  69. speedAtLastSlopeChange = CurrentSpeed;
  70. };
  71. }
  72. private void FixedUpdate()
  73. {
  74. //rigidBody.isKinematic = currentSpeed <= THRESHOLD_STANDING;
  75. ApplyVelocity();
  76. ApplySteerAngleAndRotation();
  77. }
  78. //TODO: maybe add some kind of rolling physics after downhill
  79. private void ApplyVelocity()
  80. {
  81. var targetVelocity = CalculateTargetVelocity();
  82. var velocityChange = targetVelocity - rigidBody.velocity;
  83. velocityChange.y = 0;
  84. rigidBody.AddForce(velocityChange, ForceMode.VelocityChange);
  85. }
  86. private Vector3 CalculateTargetVelocity()
  87. {
  88. AdjustSpeedToGradientIfNeeded();
  89. var tv = new Vector3(0, 0, currentSpeedAdjusted);
  90. tv = rbTransform.TransformDirection(tv);
  91. return tv;
  92. }
  93. private void AdjustSpeedToGradientIfNeeded()
  94. {
  95. var bikeAngle = rbTransform.localRotation.eulerAngles.x;
  96. if (!adjustSpeedToSlope || Mathf.Abs(bikeAngle) <= THRESHOLD_GRADIENT_ZERO)
  97. {
  98. currentSpeedAdjusted = currentSpeedSensed;
  99. return;
  100. }
  101. if (bikeAngle > 180)
  102. {
  103. bikeAngle -= 360f;
  104. }
  105. else if (bikeAngle < -180)
  106. {
  107. bikeAngle = 360f - bikeAngle;
  108. }
  109. var gradientDeg = -bikeAngle;
  110. var gradient = Mathf.Tan(gradientDeg * Mathf.Deg2Rad);
  111. Debug.Log($"current speed sensed: {currentSpeedSensed}");
  112. var adjusted = BicyclePhysics.SpeedAtGradientForSpeedAtFlat(currentSpeedSensed, rigidBody.mass,
  113. gradient) * slopeAdjustmentOptions.speedFactorUphill;
  114. var t = lastSlopeChangeTimetamp < 0
  115. ? 1f
  116. : Mathf.Clamp((Time.fixedTime - lastSlopeChangeTimetamp) /
  117. slopeAdjustmentOptions.timeToMaxPhysicsIntensity, 0, 1);
  118. currentSpeedAdjusted = Mathf.Lerp(speedAtLastSlopeChange < 0 ? currentSpeedSensed : speedAtLastSlopeChange,
  119. adjusted, t);
  120. }
  121. private void ApplySteerAngleAndRotation()
  122. {
  123. //don't lean and rotate when veeeeeery sloooow/standing. Otherwise bike will rotate already
  124. if (CurrentSpeed < 0.3f) //ca 1 km/h
  125. {
  126. CurrentSteerAngle = 0;
  127. CurrentLeaningAngle = 0;
  128. }
  129. var sumMode = controllerMode.weightLeaning + controllerMode.weightSteering;
  130. var calculatedSteerAngle = (controllerMode.weightSteering * CurrentSteerAngle +
  131. controllerMode.weightLeaning + currentLeaningAngle) /
  132. sumMode; //TODO: maybe define what leaning angle means as steering angle;
  133. var r = rbTransform.localRotation.eulerAngles;
  134. float rectifiedZ;
  135. if (r.z > 180f)
  136. {
  137. rectifiedZ = -360 + r.z;
  138. }
  139. else if (r.z < -180f)
  140. {
  141. rectifiedZ = 360 + r.z;
  142. }
  143. else
  144. {
  145. rectifiedZ = r.z;
  146. }
  147. var leanDif = -CurrentLeaningAngle - rectifiedZ;
  148. rbTransform.localRotation =
  149. Quaternion.Euler(r + new Vector3(0, calculatedSteerAngle, leanDif) * Time.fixedDeltaTime);
  150. }
  151. }
  152. }