Class: Every - a new free helper class for periodic events

Here is a little script helper to help me with periodic events.

(there is a more readable color-coded code on PasteBin: )

using UnityEngine;

/// <summary>
/// Helper for periodic events
/// </summary>
public class Every
{
	/// <summary>
	/// Constructs an Every object
	/// </summary>
	/// <param name="interval">Interval in which event is to occur</param>
	public Every(float interval)
	{
		_interval = interval;
		_tickCount = 0;
		_lastMoment = Time.time;
	}

	/// <summary>
	/// Returns True if the event should happen, false otherwise. 
	/// <remarks>Setting this property to True forces the IsTriggered to fire on next call</remarks>
	/// </summary>
	public bool IsTriggered
	{
		get
		{
			_tickCount = 0;

			float currentTime = Time.time;

			if (currentTime < _lastMoment + _interval)
			{
				return false;
			}
			else if (currentTime == _lastMoment + _interval)
			{
				return true;
			}
			else
			{
				_tickCount = (int)((currentTime - _lastMoment) / _interval) - 1;
				_lastMoment += _interval;
				return true;
			}
		}
		set
		{
			/*
			 * Setting this property to True will cause the IsTriggered to return True on the next call.
			 * 
			 * Why is this useful? For example, if you have something that happens every 60 seconds, you would
			 * create this object with 
			 * 
			 * Every someEvent = new Every(60); 
			 * 
			 * However, the event will fire After first 60 seconds pass. If you want it to fire upon creation
			 * you can set it to True and it will fire on next IsTriggered query. This is only an example,
			 * setting this property basically forces the event to fire, even if time has not yet passed.
			 * However, new period is calculated from the time you set this property to true, not from the
			 * initialy set time. But that's obvious.
			 */
			if (value)
				_lastMoment -= _interval;
		}
	}

	/// <summary>
	/// Returns true if events accumulated after the last call to IsTriggered
	/// </summary>
	private bool IsLate
	{
		get
		{
			return _tickCount > 0;
		}
	}

	/// <summary>
	/// Returns the number of accumulated events after the last call to IsTriggered.
	/// </summary>
	private int LateCount
	{
		get
		{
			return _tickCount;
		}
	}

	private int _tickCount;
	private float _lastMoment;
	private float _interval;
}

How to use it?

For example, you have a turret and you want it to fire at the closest object. So you need to check which one is the closest. But if there are many objects, you should not do that every frame, but, for example, every 200 ms. (5 times a second).

This is how you would utilize (mostly for readability) the Every object.

public class TurretController : MonoBehaviour
{
	private Every checkAim;
	private Transform closest;
	
	void Start()
	{
		checkAim = new Every(0.2f); // Every 200 ms
		checkAim.IsTriggered = true;
	}

	void Update()
	{
		if (checkAim.IsTriggered)
			closest = FindClosestBox(); // some algoritm or whatever
		
		// Do something with the "closest" object here
		// ...
		// ...
	}

}

I haven’t made an example for the accumulated events, but you can see what it does and apply to your scenario if such a need arises.

Also, I’d like some comments. Perhaps, I’ve overlooked something important which may become an issue… So I’m open to suggestions and ideas.

Just curious, why use this over InvokeRepeating?

Ok, short elaboration. This is my own opinion, somebody else’s can differ.

  1. Readability
  • I am a maintainability freak, so if my code is not readable, I get frustrated. Yes, this requires another object (which is extremely small, 12 bytes and fast to create), but the rest of the code is more readable.
  1. Strings are a bad substitution for types/method names
  • I should never ever (if not absolutely neccessary) have to use a method name as a string (and here I’m talking pure C#). Having a function parameter that is a name of the method to be called is calling for trouble. Compiler won’t notice and the program won’t work. And when I change the method name I have to go around and replace it on several locations (in a scenario). Why Unity team didn’t include delegates/lambdas here is beyond me, but hey, who am I to judge? My guess is - because of cross-language scripting.
  1. Resolving a method from a string?
  • This is a performance issue. Whether the system holds a Dictionary of <string,some_kind_of_delegate> or uses reflection, it’s slower. And if it does use the reflection, then it’s much slower. (Unfortunatelly, I am currently too lazy to come up with a proper example where this may present itself as a problem since it’s something that’s not happening too often).
  1. Being able to force an update at any point in time.
  • This is of course possible with another call to InvokeRepeating(“funcname”, 0, interval), but it makes me write the function name again (or store it in a variable somewhere). If you want to stop repeating, another call, this time to CancelInvoke() must be made (using a string). In my way, I just stop querying the trigger state.
  1. Even though I haven’t yet used this feature, my class will save the number of times the event should have happened, but didn’t, because, for some reason, I have decided not to query the trigger.

Like I said, this is my opinion, other people may not find this useful… and that’s ok.
But thanks anyway.

I have been using this for very long time in C# (unity and not unity). But yours looks faster and has a nice feat that I like, and prob will include in mine, the late counter.

Anyways heres mine…

using System;
public class StopWatch
{
	private DateTime _lastReset;
	public int Delay;
	private bool autoreset = true;
	private bool internalIsReady = false; // performance
	private bool startTrue = false;
	private bool startedTrue = false;
	
	// time in milisecs
	public StopWatch (int delay)
	{
		this.reset ();
		this.Delay = delay;
		this.autoreset = true;
	}
	// time in milisecs
	public StopWatch (int delay, bool autoreset)
	{
		this.reset ();
		this.Delay = delay;
		this.autoreset = autoreset;
	}
	// time in secs
	public StopWatch (float delay)
	{
		this.reset ();
		this.Delay = (int)delay*1000;
		this.autoreset = true;
	}
	// time in secs
	public StopWatch (float delay, bool autoreset) //aka one-shot
	{
		this.reset ();
		this.Delay = (int)delay*1000;
		this.autoreset = autoreset;
	}
	public float getTimeLeft ()
	{
		return (Delay / 1000) - ((DateTime.Now.Ticks - _lastReset.AddTicks (Delay).Ticks) / 10000000);
	}
	public float timeLeft {
		get { return getTimeLeft (); }
	}
	public void reset ()
	{
		_lastReset = DateTime.Now;
		internalIsReady = false;
	}
	
	public float getFPercentLeft{
		get {
			float timeLeft = (Delay) - ((DateTime.Now.Ticks - _lastReset.AddTicks (Delay).Ticks) / 10000);
			return (timeLeft/Delay);
		}
	}
	public int getPercentLeft{
		get{ return (int)getFPercentLeft*100; }
	}
	public bool isReady {
		get {
			if(!startedTrue  startTrue)
				return startedTrue=true;
			bool ready = internalIsReady; // performance
			if(!ready){
				ready = _lastReset.AddMilliseconds (Delay).CompareTo (DateTime.Now) <= -1;
				internalIsReady = ready;
			}
			if(ready  autoreset)
				reset ();
			return ready;
		}
		set { }
	}
	
}

Use it as :

public class BuildAbleUnit : MonoBehaviour
{	
	public string unitName = "NameLess Unit";
	public float unitBuildtime = 5.0f;
	public StopWatch buildTimer = new StopWatch(1000,false);
	public GameObject unitPrefab;
	
	
	private bool startedBuilding = false;
	public void Build(){
		buildTimer = new StopWatch(unitBuildtime);
		startedBuilding = true;
	}
	
       void update(){
            if(!startedBuilding  buildTimer.isReady) // wait 1 sec before starting build
                   Build();
        }
	
	public float getPercentDone{
		get{ return (startedBuilding)?buildTimer.getFPercentLeft:0f; }
	}
	public bool hasStarted{
		get{ return startedBuilding; }
	}
	public bool hasFinishedBuilding{
		get{ return startedBuilding  buildTimer.isReady; }
	}
}