Skip to content

[CookBook] Custom Event Based Timers

Nicki edited this page Jul 19, 2018 · 2 revisions

Introduction

-- This entry is written by NixAJ(Foxter)

  • This page is focused on introducing the user to an event based timer system which can be very useful when working with a limited Tick Rate.
  • The timers shown below will include the required code to be fully functional.
  • The full scope of functionality is not limited to how it is presented below.

Implementation

  • The first step is to include the two files below.

--EventTimer.cs--

using System;

public delegate void EventTimerTickedEventHandler(object sender, EventTimerTickedArgs e);
public class EventTimerTickedArgs
{
    public readonly EventTimer Timer;
    public readonly float DeltaTime;

    public EventTimerTickedArgs(EventTimer timer, float deltaTime)
    {
        Timer = timer;
        DeltaTime = deltaTime;
    }
}

public class EventTimer
{
    private bool _enabled;
    private float _interval, _time;

    public event EventTimerTickedEventHandler Ticked;
    protected virtual void OnTimerTicked(EventTimerTickedArgs e)
    {
        Ticked?.Invoke(this, e);
    }
    public void OnTimerTickedTrigger(EventTimerTickedArgs e)
    {
        OnTimerTicked(e);
    }

    public EventTimer()
    {
        _interval = 1;
        _time = 0;
        _enabled = false;

        EventTimerManager.AddTimer(this);
    }

    public EventTimer(float interval)
    {
        _interval = interval;
        _time = 0;
        _enabled = false;

        EventTimerManager.AddTimer(this);
    }

    public EventTimer(bool state, float interval)
    {
        _interval = interval;
        _time = 0;
        _enabled = state;

        EventTimerManager.AddTimer(this);
    }

    public bool IsEnabled() { return _enabled; }
    public float GetInterval() { return _interval; }
    public float GetTime() { return _time; }

    public void SetEnabled(bool state) { _enabled = state; }
    public void SetInterval(float interval) { _interval = interval; }
    public void SetTime(float time) { _time = time; }
    public void IncrementTime(float time) { _time += time; }
    public void DecrementTime(float time) { _time -= time; }
}

--EventTimerManager.cs--

using System.Linq;
using System.Collections.Generic;

public static class EventTimerManager
{
    private static List<EventTimer> _eventTimers = new List<EventTimer>();

    public static void AddTimer(EventTimer timer)
    {
        if (!_eventTimers.Contains(timer))
            _eventTimers.Add(timer);
    }

    public static void RemoveTimer(EventTimer timer)
    {
        if (_eventTimers.Contains(timer))
            _eventTimers.Remove(timer);
    }

    public static void UpdateTimers(float DeltaTime)
    {
        _incrementTimers(DeltaTime);
    }

    private static void _incrementTimers(float time)
    {
        foreach (EventTimer timer in _eventTimers.Where(t => t.IsEnabled()).ToList())
        {
            timer.IncrementTime(time);

            if (timer.GetTime() >= timer.GetInterval())
            {
                timer.DecrementTime(timer.GetInterval());
                timer.OnTimerTickedTrigger(new EventTimerTickedArgs(timer, time));
            }
        }
    }
}

Usage

  • Using the above system is fairly easy. The EventTimerManager relies on Update or FixedUpdate to properly perform the incrementing of the specified timer. The EventTimer class does not rely on the Manager if you only want to implement the Timer class, however you will have to modify the constructor to exclude the snippet which adds the timer automatically to the manager.

--Example Implementation--

  • Contains just a snippet of "private void Update()"
// Start Method
private void Start()
{
    // I personally don't store the timer in a variable as I never need to dispose of it.
    var HeartbeatTimer = new EventTimer(true, 0.05f).Ticked += HeartbeatUpdateLoop;
}

private void HeartbeatUpdateLoop(object sender, EventTimerTickedArgs e)
{
    // Code Here
}

// Update Loop
private void Update()
{
    EventTimerManager.UpdateTimers(time.deltaTime);
}

Outro

  • The above is in no way a perfect example of perfect optimization and is a personal pre production system. Feel free to suggest optimizations and improvements.