Skip to content

Repository files navigation

TimerKit is a versatile, easy-to-use timer component designed for Unity

Whether you're building a countdown for a game level, managing cooldowns, or triggering events at specific intervals, TimerKit provides a robust solution. It combines basic timing functionality with advanced features, all wrapped in an extensible and Unity-friendly design.

License: MITPullRequestsReleasesUnity

Features

  • Zero GC Allocations: Allocation-free per-frame updates for smooth performance.
  • Basic Operations: Start, stop, reset, and query the timer's state.
  • Pause & Resume: Pause the timer and pick up where you left off.
  • Fast Forward & Rewind: Skip ahead or backtrack through time.
  • Milestones: Trigger custom actions at specific time or progress points.
  • Range Milestones: Trigger events at regular intervals within a time range.
  • Serialization: Save and load timer states for persistent gameplay.
  • Unity Integration: Works seamlessly as a MonoBehaviour or standalone class.
  • Extensible Architecture: Multiple timer classes for different complexity needs.
  • Service Locator Support: Optional integration with dependency injection patterns.

Recent Changes (v0.12.0)

  • Fixed: package now compiles in projects without ServiceKit installed
  • Fixed: recurring milestones re-trigger when a timer is restarted with StartTimer() alone
  • Fixed: milestones sharing a trigger value across different time types now trigger independently
  • Changed: range milestone intervals must be greater than zero (the constructor now throws)

See CHANGELOG.md for complete version history.

Performance

TimerKit is designed for zero per-frame allocations in production scenarios:

OperationAllocations
BasicTimer.Update()0 bytes
StandardTimer.Update() (no milestones)0 bytes
StandardTimer.Update() (with milestones)0 bytes
Milestone triggering0 bytes
Range milestone processing0 bytes

Optimizations include:

  • Index-based iteration instead of foreach to avoid enumerator allocations
  • Pooled collections reused across frames
  • Early exit when no milestones are registered
  • No LINQ or temporary object creation in hot paths

This makes TimerKit suitable for performance-critical applications where GC pressure must be minimized.

Installation

Add TimerKit to your Unity project via Package Manager:

  1. Open Window > Package Manager
  2. Click + > Add package from git URL
  3. Enter:
https://www.pkglnk.dev/timerkit.git

pkglnk

Support

If you like my work then please consider showing your support by buying me a brew

Buy Me A Coffee

Timer Architecture

The package provides a flexible hierarchy of timer classes to suit different needs:

  • BasicTimer: Pure timer functionality without milestone support (~150 lines)
  • MilestoneTimer: Extends BasicTimer with milestone support
  • StandardTimer: Full-featured timer with all capabilities (recommended for new projects)
  • Timer: Unity MonoBehaviour wrapper for Unity integration
  • SimpleTimer: [DEPRECATED] Alias for StandardTimer (maintained for backward compatibility)

Interfaces

  • IReadOnlyTimer: Read-only timer properties (TimeRemaining, TimeElapsed, Progress, etc.)
  • IBasicTimer: Basic timer operations extending IReadOnlyTimer
  • ITimer: Full timer functionality with milestone management

Usage

Quick Start

Unity MonoBehaviour Timer

For Unity integration with Inspector support:

usingNonatomic.TimerKit;usingUnityEngine;publicclassCountdownExample:MonoBehaviour{privateTimer_timer;voidStart(){_timer=gameObject.AddComponent<Timer>();_timer.Duration=30f;// 30 seconds_timer.OnComplete+=()=>Debug.Log("Countdown finished!");_timer.StartTimer();}}

Standalone Timer

For pure C# usage without Unity dependencies:

usingNonatomic.TimerKit;publicclassStandaloneExample{privateStandardTimer_timer;publicvoidStartCountdown(){_timer=newStandardTimer(30f);// 30 seconds_timer.OnComplete+=()=>Console.WriteLine("Countdown finished!");_timer.StartTimer();// In your update loop:// _timer.Update(deltaTime);}}

Basic Operations

// Create a timervartimer=newStandardTimer(10f);// 10 second duration// Control the timertimer.StartTimer();// Start from full durationtimer.ResumeTimer();// Resume from current positiontimer.StopTimer();// Pause the timertimer.ResetTimer();// Reset to full duration// Time manipulationtimer.FastForward(2f);// Skip ahead 2 secondstimer.Rewind(1f);// Go back 1 second// Query timer stateboolisRunning=timer.IsRunning;floattimeLeft=timer.TimeRemaining;floatelapsed=timer.TimeElapsed;floatprogress=timer.ProgressElapsed;// 0.0 to 1.0

Events

timer.OnStart+=()=>Debug.Log("Timer started");timer.OnResume+=()=>Debug.Log("Timer resumed");timer.OnStop+=()=>Debug.Log("Timer stopped");timer.OnComplete+=()=>Debug.Log("Timer completed");timer.OnTick+=(IReadOnlyTimert)=>Debug.Log($"Time: {t.TimeRemaining}");timer.OnDurationChanged+=(floatnewDuration)=>Debug.Log($"Duration changed to: {newDuration}");

Milestones

Milestones trigger callbacks when the timer reaches specific points. You can create them using either the convenience API (passing components) or by creating milestone objects manually:

// Convenience API - pass components directly (recommended)timer.AddMilestone(TimeType.TimeRemaining,5f,()=>{Debug.Log("5 seconds left!");});// Progress-based milestonetimer.AddMilestone(TimeType.ProgressElapsed,0.75f,()=>{Debug.Log("75% complete!");});// Recurring milestone - triggers every timer roundtimer.AddMilestone(TimeType.TimeRemaining,5f,()=>{Debug.Log("5 seconds warning!");},isRecurring:true);// Manual creation (if you need to store the reference)varmilestone=newTimerMilestone(TimeType.TimeRemaining,5f,()=>{Debug.Log("5 seconds left!");});timer.AddMilestone(milestone);// Remove milestonestimer.RemoveMilestone(milestone);timer.RemoveAllMilestones();timer.RemoveMilestonesByCondition(m =>m.TriggerValue<3f);

Range Milestones

Range milestones trigger at regular intervals within a specified range. Like regular milestones, you can create them using either the convenience API or by creating instances manually:

// Convenience API - pass components directly (recommended)timer.AddRangeMilestone(TimeType.TimeRemaining,// Type of time to track10f,// Range start (10 seconds remaining)0f,// Range end (0 seconds remaining)1f,// Interval (every 1 second)()=>Debug.Log("Countdown warning!")// Callback);// Trigger every 0.5 seconds from 2-5 seconds elapsedtimer.AddRangeMilestone(TimeType.TimeElapsed,2f,// Start at 2 seconds elapsed5f,// End at 5 seconds elapsed0.5f,// Every 0.5 seconds()=>PlayTickSound()// Callback);// Recurring range milestone - triggers every timer roundtimer.AddRangeMilestone(TimeType.TimeRemaining,10f,0f,2f,()=>Debug.Log("Every 2 seconds!"),isRecurring:true);// Manual creation (if you need to store the reference)varrangeMilestone=newTimerRangeMilestone(TimeType.TimeRemaining,10f,0f,1f,()=>Debug.Log("Countdown warning!"));timer.AddRangeMilestone(rangeMilestone);

TimeType Options

  • TimeRemaining: Time left on the timer (countdown)
  • TimeElapsed: Time passed since timer started
  • ProgressElapsed: Completion progress (0.0 to 1.0)
  • ProgressRemaining: Remaining progress (1.0 to 0.0)

Advanced Examples

Game Level Timer with Warnings

publicclassLevelTimer:MonoBehaviour{privateTimer_timer;voidStart(){_timer=gameObject.AddComponent<Timer>();_timer.Duration=300f;// 5 minutes// Add warning milestones using convenience API_timer.AddMilestone(TimeType.TimeRemaining,60f,()=>ShowWarning("1 minute remaining!"));_timer.AddMilestone(TimeType.TimeRemaining,30f,()=>ShowWarning("30 seconds remaining!"));// Add countdown for last 10 seconds_timer.AddRangeMilestone(TimeType.TimeRemaining,10f,0f,1f,()=>PlayCountdownBeep());_timer.OnComplete+=()=>EndLevel();_timer.StartTimer();}}

Ability Cooldown System

publicclassAbilityCooldown:MonoBehaviour{[SerializeField]privatefloat_cooldownDuration=5f;privateTimer_cooldownTimer;voidStart(){_cooldownTimer=gameObject.AddComponent<Timer>();_cooldownTimer.Duration=_cooldownDuration;_cooldownTimer.OnComplete+=()=>OnCooldownComplete();}publicvoidUseAbility(){if(_cooldownTimer.IsRunning)return;// Still on cooldown// Execute ability logic hereDebug.Log("Ability used!");// Start cooldown_cooldownTimer.StartTimer();}privatevoidOnCooldownComplete(){Debug.Log("Ability ready!");}publicfloatGetCooldownProgress()=>_cooldownTimer.ProgressElapsed;}

Custom Time Sources

TimerKit supports external time synchronization through the ITimeSource interface. This allows you to sync timers with external systems like network time, game sessions, or custom time managers:

// Create a custom time sourcepublicclassGameSessionTimeSource:MonoBehaviour,ITimeSource{privatefloat_sessionTimeRemaining=300f;// 5 minutespublicfloatGetTimeRemaining()=>_sessionTimeRemaining;publicvoidSetTimeRemaining(floattimeRemaining)=>_sessionTimeRemaining=timeRemaining;publicboolCanSetTime=>true;// Allow timer to modify timevoidUpdate(){// Update session time from your game logic_sessionTimeRemaining-=Time.deltaTime;}}// Use custom time source with a timer// (MonoBehaviour time sources must be added as components, not constructed with new)varsessionTimeSource=gameObject.AddComponent<GameSessionTimeSource>();vartimer=newStandardTimer(300f,sessionTimeSource);timer.StartTimer();// Timer now syncs with sessionTimeSource instead of tracking its own time

Using TimeSourceProvider Component

For automatic Unity component integration, extend TimeSourceProvider:

publicclassNetworkTimeProvider:TimeSourceProvider{privatefloat_networkTime;publicoverridefloatGetTimeRemaining()=>_networkTime;publicoverridevoidSetTimeRemaining(floattimeRemaining)=>_networkTime=timeRemaining;publicoverrideboolCanSetTime=>false;// Read-only from networkvoidStart(){// Fetch network timeStartCoroutine(SyncWithServer());}IEnumeratorSyncWithServer(){// Your network sync logic hereyieldreturnnull;}}

Attach the NetworkTimeProvider component to the same GameObject as a Timer component and they will connect automatically. Note that the automatic connection requires the concrete Timer MonoBehaviour; other ITimer implementations will log a warning and remain unconnected.

Service Integration

TimerKit ships with optional integrations that activate automatically when the matching package is installed (via assembly definition version defines):

  • Installing ServiceKit 2.0.0+ defines TIMERKIT_SERVICEKIT_SUPPORT
  • Installing ServiceLocator 0.5.0+ defines TIMERKIT_SERVICE_LOCATOR_SUPPORT

ServiceKit

Use the built-in TimerService (registered as ITimerService), or derive your own:

usingNonatomic.ServiceKit;usingNonatomic.TimerKit.Extensions.ServiceKit;publicinterfaceIGameTimerService:IBaseTimerService{}[Service(typeof(IGameTimerService))]publicclassGameTimerService:BaseTimerService,IGameTimerService{// Your custom timer logic here}

ServiceLocator

usingNonatomic.TimerKit.Extensions.ServiceLocator;publicinterfaceIGameTimerService:IBaseTimerService{}publicclassGameTimerService:BaseTimerService<IGameTimerService>,IGameTimerService{// Your custom timer logic here}

Migration Guide

From SimpleTimer to StandardTimer

If you're using the deprecated SimpleTimer class:

Before:

vartimer=newSimpleTimer(10f);

After:

vartimer=newStandardTimer(10f);

The API is identical, but StandardTimer provides better clarity about the class's capabilities.

Choosing the Right Timer Class

  • Use BasicTimer when you only need start/stop/reset functionality
  • Use MilestoneTimer when you need milestone support but want a lighter class
  • Use StandardTimer for full functionality (recommended for most use cases)
  • Use Timer (MonoBehaviour) for Unity Inspector integration

Breaking Changes & Migration

Version 0.4.0 Changes

⚠️ Deprecation Notice: SimpleTimer has been deprecated in favor of StandardTimer. This is not a breaking change - all existing code continues to work unchanged, but you'll see compiler warnings.

What's Deprecated

  • SimpleTimer class - Use StandardTimer instead

What's Not Breaking

  • All existing APIs remain identical - no code changes required
  • Full backward compatibility maintained
  • All existing functionality preserved

Recommended Updates (Optional)

// Old (still works, but shows deprecation warning)vartimer=newSimpleTimer(10f);// New (recommended for new code)vartimer=newStandardTimer(10f);

New Architecture Benefits

The new class hierarchy provides better separation of concerns:

  • Smaller classes for specific needs (BasicTimer for simple cases)
  • Clearer intent with descriptive names (StandardTimer vs SimpleTimer)
  • Better extensibility with proper inheritance chain

No Action Required

Existing projects can continue using SimpleTimer without any changes. The deprecation warning can be suppressed if needed:

#pragma warning disable CS0618// Type or member is obsoletevartimer=newSimpleTimer(10f);
#pragma warning restore CS0618

About

Unity timers

Resources

Contributing

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages