Magic Time User

v1.0

Magic Time User is a MonoBehaviour which implements IUseMagicTime. Objects which will utilize the Magic Time system should inherit from MagicTimeUser, so they automatically work.

You can create your own implementation logic either by creating your own IUseMagicTime class, or creating a new class which derives from MagicTimeUser and overrides the methods you'd like to customize.

"Local" LocalTimeScale

Each MagicTimeUser object will have its own LocalTimeScale automatically. This is unique and specific to this object, and its value will be combined with any other Local Time Scales that this object subscribes to.

Main Methods and Properties

These are the main methods and properties you'll likely interact with most often.

Time Properties

DeltaTime, FixedDeltaTime, and UnscaledDeltaTime will replace Time.deltaTime, Time.fixedDeltaTime, and Time.unscaledDeltaTime in your code. Anywhere that you would have used Time.deltaTime, you should use DeltaTime instead.

TimeScale is the actual scale currently set based on all of the subscribed LocalTimeScale objects, compared to normal speed (Time.timeScale = 1f).

InverseTimeScale is the value you would need to multiple TimeScale by in order to get Time.timeScale. This is useful for multiplying things that you don't want to be slowed down, when they otherwise would. (See the "animation" example in the Magic Time User Scripting page.)

// From a class that is IUseMagicTime or inherits from MagicTimeUser
float speedThisFrame = speed * DeltaTime
float fixedSpeedThisFrame = fixedSpeed * FixedDeltaTime;
float speedThisFrameVersion2 = speed * UnscaledDeltaTime;
Debug.Log($"TimeScale for this object is currently {TimeScale}"); // a float!

// Accounting for TimeScale changes in the Animation speed using InverseTimeScale
//...
var metersPerSecond = distance / deltaTimeToUse;
var animationSpeed = 0f;
if (metersPerSecond > 0 && Owner.TimeScale > 0)
    animationSpeed = metersPerSecond * Owner.InverseTimeScale;
animator.SetFloat(locomotionString, animationSpeed, animatorDampTime, Time.deltaTime);
// The above example ensures the "locomotion" speed remains steady, even as the
// object slows down. This gives it the "running in slow motion" effect.
//...

// Subscribe to a specific LocalTimeScale
LocalTimeScale otherTimeScale;
Debug.Log($"TimeScale before adding {otherTimeScale.name}: {magicTimeUser.TimeScale}");
magicTimeUser.SubscribeToLocalTimeScale(otherTimeScale);
Debug.Log($"TimeScale after adding {otherTimeScale.name}: {magicTimeUser.TimeScale}");

// Unsubscribing from a specific LocalTimeScale
LocalTimeScale otherTimeScale;
Debug.Log($"TimeScale before removing {otherTimeScale.name}: {magicTimeUser.TimeScale}");
magicTimeUser.UnsubscribeFromLocalTimeScale(otherTimeScale);
Debug.Log($"TimeScale after removing {otherTimeScale.name}: {magicTimeUser.TimeScale}");

// Interacting with the LocalTimeScale on the object from another class
if (otherObject is IUseMagicTime magicTimeUser)
{
    LocalTimeScale otherTimeScale = magicTimeUser.LocalTimeScale;
    float otherObjectsTimeScale = otherTimeScale.LocalTimeScaleValue;
}

// Getting the gameObject from IUseMagicTime
protected virtual void OnCollisionEnter(Collider other)
{
    if (other.isTrigger) return;
            
    var magicTimeUser = other.GetComponentInParent<IUseMagicTime>();
    if (magicTimeUser != null)
    {
        GameObject otherObject = magicTimeUser.GameObject;
    }
}

Check the Magic Time User Scripting page for more scripting documentation.

Inspector Options

Once added to your class, some fields will be exposed in the Inspector.

Initial Time Scales

These are LocalTimeScale Scriptable Objects which you want to subscribe to at runtime. These will connect to the instances of those objects via the Magic Time Manager in your scene.

Transition Settings

Whenever the combined TimeScale value changes, the value of TimeScale will transition to the new value.

Transition Duration

This is how long the transition lasts. Set this value to 0f to skip transitioning altogether.

Transition Curve

This is the curve used for the transition, if you'd like to customize the experience.

Default Time Scale

This is the default time scale value for this objects LocalTimeScale object.

Customization Example

Often you may want your object to subscribe to some LocalTimeScales, but not others, depending on the type of object it is in your game. In my own project, I have "Enemy" and "Player" Local Time Scales, and characters of those types will subscribe to one or the other.

Here I have exposed two additional lists, one for the "Player" and one for the "Enemy". In my code, I ensure the character only subscribes to the correct list of LocalTimeScales.

// Note that this overrides the coroutine on MagicTimeUser!
public override IEnumerator SubscribeToInitialTimeScales()
{
    // Wait for the base class coroutine to finish [Don't forget this step!]
    yield return base.SubscribeToInitialTimeScales();
    
    // Subscribe to the additional time scales based on the IsPlayer value
    foreach (var timeScale in IsPlayer ? playerTimeScales : enemyTimeScales)
    {
        var localTimeScale = MagicTimeManager.Instance.TimeScale(timeScale.name);
        SubscribeToLocalTimeScale(localTimeScale);
    }
}

Last updated