⏰Time Manager

Used to freeze/unfreeze time, do slow-motion effect

You can call TimeManager.cs anywere even if it's not replaced in scene.

namespace using Game.Core;

API

// you can subscribe to this event which will invoke every 1 real second tick
// to calculate game timers and etc
public static UnityEvent OnRealSecondTick;

public float DefaultTimeScale => defaultTimeScale;
public float DefaultFixedDeltaTime => defaultFixedDeltaTime;

// Resets time settings to default
public void ResetTimeToDefault() {
    Time.timeScale = defaultTimeScale;
    Time.fixedDeltaTime = defaultFixedDeltaTime;
}

// Freezes time by scale
public void FreezeTime();

// Unfreezes time by scale
// parameter "useDefault"=true means unfreeze and keep default timeScale
// if "useDefault"=true then last timeScale that was before time freeze will be
public void UnfreezeTime(bool useDefault = false);

// do slow-motion
public void DoSlowMotion(SlowMotionData data);

// changes time scale and stops slow-motion if was
public void SetTimeScale(float scale);

public static DateTime UnixTimeStampMillisecondsToUTCDateTime(double unixTimeStamp);
public static DateTime UnixTimeStampSecondsToUTCDateTime(double unixTimeStamp);

// formats seconds to "00:00" format (hours:minutes or minutes:seconds)
public static string FormatTime00x00(int seconds);

// run timer on TimeManager for specific seconds with tickCallback
public Coroutine RunTimerInt(float forSeconds, bool unscaled, Action<float> tickCallback);
// stop active timer on TimeManager by coroutine
public void StopTimer(ref Coroutine coroutine);

Example how use TimeManager timer

// this is UI timer object, that updates text on RunTimer call till it's complete
public class TimerUI : MonoBehaviour {
    [HideInInspector] public UnityEvent OnComplete = new();

    [SerializeField] private Text _timerText;

    public bool IsRunning => _timerCoroutine != null;

    private Coroutine _timerCoroutine;
    private TimeManager _timeManager;
    private float _timerSecondsTotal;

    private void Awake() {
        if (_timeManager == null) Init();
    }

    public void Init() {
        _timeManager = TimeManager.Instance;
    }

    private void OnDestroy() {
        if (_timerCoroutine != null && TimeManager.IsExist) {
            StopTimer();
        }
    }

    public void RunTimer(float forSeconds, bool unscaled = true) {
        if (_timeManager == null) Init();
        _timeManager.StopTimer(ref _timerCoroutine);
        SetSeconds(forSeconds);
        _timerSecondsTotal = forSeconds;
        _timerCoroutine = _timeManager.RunTimerInt(forSeconds, unscaled: unscaled, SetSeconds);
    }
    
    public void StopTimer() {
        _timeManager.StopTimer(ref _timerCoroutine);
    }

    public void SetSeconds(float seconds) {
        _timerText.text = TimeManager.FormatTime00x00((int)seconds);
        if (seconds == 0) {
            _timerCoroutine = null;
            OnComplete.Invoke();
        }
    }
}

// and usage of it:
// subscribe once in Awake or something
_timerUI.OnComplete.AddListener(Lose); 
// run it
_timerUI.RunTimer(forSeconds: 30f);
// stop timer if some conditions met
_timerUI.StopTimer();
// you can remove if you want to reuse it
// somewhere else with OnComplete new logic
_timerUI.OnComplete.RemoveListener(Lose);

Example how use TimeManager static real second tick event

// this is example as timer, but it's not accurate
// timer error can be 1 second since OnRealSecondTick can be invoked too early
// but you can freely use it for some ingame notification or reward and etc systems!

int timeLeft;

public void OnGameStart() {
    timeLeft = 10;
    // subscribe to event
    TimeManager.OnRealSecondTick.AddListener(UpdateTimer);
}

private void UpdateTimer() {
    timerLeft--;
    if (timerLeft < 0) {
        OnTimeOver();
    }
}

private void OnTimeOver() {
    TimeManager.OnRealSecondTick.RemoveListener(UpdateTimer);
    // do some actions
}

Example how to do slow motion

// SO in the end of class means that this is ScriptableObject
[CreateAssetMenu(fileName = "EnemySettings", 
                 menuName = "Configs/EnemySettings")]
public class EnemySettingsSO : ScriptableObject() {
    // you can setup slow motion data in the inspector of SO asset
    public SlowMotionData SlowMotionOnKill;
}

public class Enemy : Monobehaviour {
    // link here your SO asset that you will create in Assets\_Main\Configs\
    [SerializeField] private EnemySettingsSO _settings;
    
    // assume that it will be called when enemy health empty
    private void OnDeath() {
        TimeManager.Instance.DoSlowMotion(_settings.SlowMotionOnKill);
    }
}

Last updated