using System; using System.Collections.Generic; using UnityEngine; namespace Controller.Bicycle { readonly struct Booster { public float StartTime { get; } public float EndTime { get; } public float StartValue { get; } public float EndValue { get; } public Booster(float startTime, float duration, float startValue, float endValue) { StartTime = startTime; EndTime = startTime + duration; StartValue = startValue; EndValue = endValue; } public override string ToString() { return $"Booster({StartValue}-{EndValue} between Time({StartTime}, {EndTime}))"; } } public class BikeSpeedBooster { #region singleton private static readonly Lazy lazy = new Lazy (() => new BikeSpeedBooster()); public static BikeSpeedBooster Instance => lazy.Value; private readonly List activeBooster = new List(); #endregion public const float MaxBoost = 1.4f; public const float MinBoost = 0.6f; public const float ThresholdZero = 0.05f; private const float DURATION_BOOST_STRAIGHT_UPHILL = 2.5f; private const float DURATION_BOOST_UPHILL_STRAIGHT = 1.5f; private const float DURATION_BOOST_STRAIGHT_DOWNHILL = 3.5f; private const float DURATION_BOOST_DOWNHILL_STRAIGHT = 5f; private float maxSlopeDeg = 8.7f; private float fixedBoost = 1; public float Boost { get { var boost = fixedBoost; var toDelete = new Stack(); var t = Time.time; foreach (var b in activeBooster) { if (b.EndTime < t) { toDelete.Push(b); continue; } if (b.StartTime > t) continue; var add = Mathf.Lerp(b.StartValue, b.EndValue, (Time.time - b.StartTime) / (b.EndTime - b.StartTime)); boost *= add; } foreach (var d in toDelete) { activeBooster.Remove(d); } return Mathf.Clamp(boost, MinBoost, MaxBoost); } } public void AddBoosterForFirstAndSlope(float slopeDegree) { //going from straight to downhill -> increase speed from straight to slope //-> inverse fixed boost first, then 1*fixedBoost //going from straight to uphill -> decrease speed from straight to slope //-> inverse fixed boost first, then 1*fixedBoost var booster = new Booster(Time.time, slopeDegree < 0 ? DURATION_BOOST_STRAIGHT_DOWNHILL : DURATION_BOOST_STRAIGHT_UPHILL, 1f / fixedBoost, 1f); //Debug.Log($"Adding Booster: {booster}"); activeBooster.Add(booster); } public void AddBoosterForLastAndSlope(float slopeDegree) { //going from downhill to straight -> let the bike roll -> boost starts at fixedBoost and decreases to 1 //going from uphill to straight -> don't go to 1 immediately //-> boost starts at fixedBoost and increases to 1 var booster = new Booster(Time.time, slopeDegree < 0 ? DURATION_BOOST_DOWNHILL_STRAIGHT : DURATION_BOOST_UPHILL_STRAIGHT, fixedBoost, 1f); //Debug.Log($"Adding Booster: {booster}"); activeBooster.Add(booster); } public void SetFixedBoostForSlope(float slopeDegree) { var max = slopeDegree < 0 ? -maxSlopeDeg : maxSlopeDeg; fixedBoost = Mathf.Lerp(1f, slopeDegree < 0 ? MaxBoost : MinBoost, slopeDegree / max); } public void ResetFixedBoost() { fixedBoost = 1; } } }