Custom Time for Naninovel

Posted: 20 Oct 2023 09:58
by Restush

In Naninovel 1.19, there is new feature called ITime where we can use custom time rather than use built-in UnityEngine.Time.
Benefit of using custom time, we can decrease or increase time in custom minigame without affecting Naninovel objects like dialogue, transition, typing effect, tweener, etc and vice versa.

You can copy the code below or download from my gist https://gist.github.com/restush/43b800a ... 07fa7d1cd8

Here is CustomTime.cs

Code: Select all

/// <summary> Custom Unity Time based on <see cref="System.Diagnostics.Stopwatch"/> </summary>
public class CustomTime : ITime
{
   public float TimeScale { get; set; }
   public float Time { get; private set; }
   public float DeltaTime { get; private set; }
   public float UnscaledTime { get; private set; }
   public float UnscaledDeltaTime { get; private set; }
   public int FrameCount { get; private set; }

   private readonly System.Diagnostics.Stopwatch stopwatch;
   private readonly float defaultTimeScale; // default `TimeScale` that set in first instance
   private float lastTimeScale; // last `TimeScale` before changed 
   private long lastElapsedTicks; // last Stopwatch's ticks in Update method

   public CustomTime(float timeScale = 1f)
   {
       stopwatch = new System.Diagnostics.Stopwatch();
       stopwatch.Start();
       lastElapsedTicks = stopwatch.ElapsedTicks;
       TimeScale = timeScale;
       lastTimeScale = timeScale;
       defaultTimeScale = timeScale;
   }

   /// <summary>Calculate `Time` and `DeltaTime` based on current frame.</summary>
   /// <remarks>Important: Call this method in Monobehaviour.Update()</remarks>
   public void Update()
   {
       CalculateDeltaTime();
       FrameCount++;

       void CalculateDeltaTime()
       {
           long currentElapsedTicks = stopwatch.ElapsedTicks;

           UnscaledTime = stopwatch.ElapsedMilliseconds * 0.001f;
           UnscaledDeltaTime = (currentElapsedTicks - lastElapsedTicks) * (1.0f / System.Diagnostics.Stopwatch.Frequency);

           DeltaTime = UnscaledDeltaTime * TimeScale;
           Time += DeltaTime;

           lastElapsedTicks = currentElapsedTicks;
       }
   }

   /// <summary>Freeze the time.</summary>
   /// <remarks>Info: Before changed, the `TimeScale` cached to <see cref="lastTimeScale"/>.</remarks>
   public void Pause()
   {
       lastTimeScale = TimeScale;
       TimeScale = 0;
   }
   /// <summary>Resume the time.</summary>
   /// <remarks>Info: Resume the time based on lastTimeScale.</remarks>
   public void Resume()
   {
       TimeScale = lastTimeScale;
   }
   /// <summary>Set default `TimeScale` that set in first instance.</summary>
   public void SetDefaultSpeed() => TimeScale = defaultTimeScale;

   public void Speed1x() => TimeScale = 1;
   public void Speed2x() => TimeScale = 2;
   public void Speed5x() => TimeScale = 5;
   public void Speed10x() => TimeScale = 10;

}

Here is how to implement it. Make sure attach Initialize.cs class in one of GameObject in Scene then disable Initialize On Application Load in Naninovel setting.

Code: Select all

public class Initialize : MonoBehaviour
{
   private ITime time;

   private async UniTask Start()
   {
       time = new CustomTime();
       await RuntimeInitializer.InitializeAsync(time: time);
   }

   private void Update()
   {
       if (this.time is CustomTime time)
           time.Update();
   }
}

Updated 21/10/2023