using UnityEngine; namespace Controller.Bicycle { public class RbBicycleController : BicycleControllerBaseBehaviour, IBicycleController { private void Awake() { rbTransform = rigidBody.transform; rigidBody.freezeRotation = true; rigidBody.centerOfMass = centerOfMass.position; } private void Start() { speedBooster = BikeSpeedBooster.Instance; } private void FixedUpdate() { //rigidBody.isKinematic = currentSpeed <= THRESHOLD_STANDING; ApplyVelocity(); ApplySteerAngleAndRotation(); } private void OnGUI() { GUI.TextField(new Rect(200, 100, 400, 50), $"Boost = {speedBooster.Boost}"); } //TODO: maybe add some kind of rolling physics after downhill private void ApplyVelocity() { var targetVelocity = CalculateTargetVelocity(); var velocityChange = targetVelocity - rigidBody.velocity; velocityChange.y = 0; rigidBody.AddForce(velocityChange, ForceMode.VelocityChange); } private Vector3 CalculateTargetVelocity() { if (adjustSpeedToSlope) { previousSoeedAdjusted = currentSpeedAdjusted; var boost = speedBooster.Boost; float nextSpeedAdjusted; if (boost > 1 && CurrentSpeedSensed < 5.55f) //5.55 m/s = 20 km/h nextSpeedAdjusted = 5.55f * boost; else nextSpeedAdjusted = CurrentSpeedSensed * speedBooster.Boost; currentSpeedAdjusted = Mathf.Max(Mathf.Max(0, previousSoeedAdjusted - Time.deltaTime * MAX_DIF_SPEED_ADJUSTED_PER_SECOND), nextSpeedAdjusted); } else { currentSpeedAdjusted = CurrentSpeedSensed; } var tv = new Vector3(0, 0, currentSpeedAdjusted); tv = rbTransform.TransformDirection(tv); return tv; } private void ApplySteerAngleAndRotation() { //don't lean and rotate when veeeeeery sloooow/standing. Otherwise bike will rotate already if (CurrentSpeed < 0.3f) //ca 1 km/h { CurrentSteerAngle = 0; CurrentLeaningAngle = 0; } var sumMode = controllerMode.weightLeaning + controllerMode.weightSteering; var calculatedSteerAngle = (controllerMode.weightSteering * CurrentSteerAngle + controllerMode.weightLeaning + currentLeaningAngle) / sumMode; //TODO: maybe define what leaning angle means as steering angle; var r = rbTransform.localRotation.eulerAngles; float rectifiedZ; if (r.z > 180f) rectifiedZ = -360 + r.z; else if (r.z < -180f) rectifiedZ = 360 + r.z; else rectifiedZ = r.z; var leanDif = -CurrentLeaningAngle - rectifiedZ; rbTransform.localRotation = Quaternion.Euler(r + new Vector3(0, calculatedSteerAngle, leanDif) * Time.fixedDeltaTime); } #region Variables private Transform rbTransform; private float currentSteerAngle; private float currentLeaningAngle; private float currentSpeedAdjusted; private float previousSoeedAdjusted; private const float MAX_DIF_SPEED_ADJUSTED_PER_SECOND = 10f; private BikeSpeedBooster speedBooster; [Tooltip("Needs a RbBicycleSlopeSpeedManager to be attached to this GameObject")] public bool adjustSpeedToSlope = true; public Vector3 Forward => rbTransform.forward; public Vector3 Right => -rbTransform.right; public Vector3 Up => rbTransform.up; public BicycleControllerMode ControllerMode { get => controllerMode; set => controllerMode = value; } public float CurrentSpeedSensed { private set; get; } public float CurrentSpeed { get => adjustSpeedToSlope ? currentSpeedAdjusted : CurrentSpeedSensed; set => CurrentSpeedSensed = Mathf.Clamp(value, 0, maxSpeed); } public float CurrentSpeedKph => CurrentSpeed * 3.6f; public float CurrentSteerAngle { get => currentSteerAngle; set => currentSteerAngle = Mathf.Clamp(value, -maxSteeringAngle, maxSteeringAngle); } public float CurrentLeaningAngle { get => currentLeaningAngle; set { //don't lean while standing / walking to bike if (rigidBody.velocity.magnitude < .5f) return; currentLeaningAngle = Mathf.Clamp(value, -maxLeaningAngle, maxLeaningAngle); } } public Vector3 RigidBodyVelocity => rigidBody.velocity; #endregion } }