Custom Time Bridge / Integrations

v1.0

If your game has its own time system (e.g. a game “time manager” with variable clocks), you can integrate it with Juicy Actions by making your own bridge / resolver, similar to the Magic Time Integration.

1. Create a Clock Resolver

A resolver simply tells Juicy Actions:

"If this GameObject users my time system, give it my clock instead of the Juicy Action clock."

Here's an example. Here you'd replace IMyTimeProvider with your own interface or component.

using UnityEngine;
using MagicPigGames.ActionSystem;

namespace YourNamespace.JuicyActionsIntegration
{
    public sealed class MyTimeClockResolver : IClockResolver
    {
        public int Priority => 900; // Something high

        public IClock ResolveFor(Component target, ActionExecutor executor)
        {
            // Look for your own time provider
            var provider = target.GetComponentInParent<IMyTimeProvider>();
            if (provider != null)
                return new MyTimeObjectClock(provider);

            return null; // fallback to default
        }

        [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
        private static void Install() => ActionClock.Register(new MyTimeClockResolver());
    }
}

2. Wrap Your Time System in an IClock Adapter

You can call this something other than MyTimeObjectClock if you'd prefer, and use the same interface as you did in step 1.

public sealed class MyTimeObjectClock : IClock
{
    private readonly IMyTimeProvider _provider;
    public MyTimeObjectClock(IMyTimeProvider provider) => _provider = provider;

    // These are the values that will be brought over from your time system!
    public float Time              => _provider.CurrentTime;
    public float UnscaledTime      => _provider.UnscaledTime;
    public float DeltaTime         => _provider.Delta;
    public float UnscaledDeltaTime => _provider.UnscaledDelta;
    public float TimeScale         => _provider.TimeScale;

    public async ValueTask Delay(float seconds, CancellationToken ct)
    {
        float elapsed = 0f;
        while (elapsed < seconds)
        {
            ct.ThrowIfCancellationRequested();
            elapsed += _provider.DeltaTime;
            await Task.Yield();
        }
    }
}

This pattern is exactly what MagicTimeClockResolver and MagicTimeObjectClock do internally — it’s just type-safe and localized to your own interfaces.

Interface Expectations

Your custom time provider (the thing your game already uses) only needs to expose the same shape of data expected by the adapter:

public interface IMyTimeProvider
{
    float Time                { get; }
    float DeltaTime           { get; }
    float UnscaledTime        { get; }
    float UnscaledDeltaTime   { get; }
    float TimeScale           { get; }
}

If your system already has different names (e.g., GameTime, DeltaGameTime), you can simply map them in your MyTimeObjectClock.

Full Sample Code

This is a full code sample that you may wish to refer to, or pass into your favorite AI to help you create your bridge.

// File: YourTimeClockResolver.cs
// Namespace can match your package layout.
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

namespace MagicPigGames.ActionSystem.CustomTimeIntegration
{
    /// <summary>
    /// Provide a minimal contract your time system can implement.
    /// You can replace this with the actual interface from the other package.
    /// </summary>
    public interface IMyTimeProvider
    {
        float CurrentTime   { get; }
        float Delta         { get; }
        float UnscaledTime  { get; }
        float UnscaledDelta { get; }
        float TimeScale     { get; }
    }

    /// <summary>
    /// Adapter that turns any IMyTimeProvider into an Action System IClock.
    /// </summary>
    public sealed class MyTimeObjectClock : IClock
    {
        private readonly IMyTimeProvider _src;
        public MyTimeObjectClock(IMyTimeProvider src) => _src = src;

        public float Time               => _src?.CurrentTime   ?? ActionSystemClock.Instance.Time;
        public float UnscaledTime       => _src?.UnscaledTime  ?? ActionSystemClock.Instance.UnscaledTime;
        public float DeltaTime          => _src?.Delta         ?? ActionSystemClock.Instance.DeltaTime;
        public float UnscaledDeltaTime  => _src?.UnscaledDelta ?? ActionSystemClock.Instance.UnscaledDeltaTime;
        public float TimeScale          => _src?.TimeScale     ?? ActionSystemClock.Instance.TimeScale;

        public async ValueTask Delay(float seconds, CancellationToken ct)
        {
            // Delegate to the active ActionSystemClock for consistency
            var fallback = ActionSystemClock.Instance;
            var remaining = Mathf.Max(0f, seconds);
            while (remaining > 0f)
            {
                ct.ThrowIfCancellationRequested();
                remaining -= (_src?.Delta ?? fallback.DeltaTime);
                await Task.Yield();
            }
        }
    }

    /// <summary>
    /// IClockResolver that auto-detects IMyTimeProvider on the target or its parents.
    /// Registers itself automatically on load.
    /// </summary>
    public sealed class MyTimeClockResolver : IClockResolver
    {
        /// <summary>
        /// Priority of this resolver. Higher beats lower.
        /// Use something below Magic Time (1000) if you want Magic Time to win when both exist.
        /// </summary>
        public int Priority => 900;

        public IClock ResolveFor(Component target, ActionExecutor executor)
        {
            if (!target) return null;

            // Find a provider on target or in its parents.
            var provider = target.GetComponentInParent<IMyTimeProvider>();
            if (provider != null)
                return new MyTimeObjectClock(provider);

            return null; // fall back to default Unity clock
        }

        // Auto-register once the app domain is ready.
        [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
        private static void Install()
        {
            ActionClock.Register(new MyTimeClockResolver());
        }
    }
}

Last updated

Was this helpful?