Skip to content

Instantly share code, notes, and snippets.

@SradnickDev
Last active September 1, 2021 23:58
Show Gist options
  • Save SradnickDev/fa92b1bebf764de2b5c388d357156da0 to your computer and use it in GitHub Desktop.
Save SradnickDev/fa92b1bebf764de2b5c388d357156da0 to your computer and use it in GitHub Desktop.
Delay calls by time or frames, alternative for coroutines and Monobehaviour.Invoke.
using System.Collections.Generic;
using UnityEngine;
public interface IDeferredAction
{
void OnExecuteDeferred(byte iKey);
}
/// <summary>
/// Delay method calls by time,frame and/or execute repeatedly.
/// You need to implement <see cref="IDeferredAction"/>, <see cref="IDeferredAction.OnExecuteDeferred"/> use the the key(byte) to differentiate between different calls
/// <remarks>Use this instead of invoke, invokeRepeating or own implementations for delaying and/or repeating method calls.</remarks>
/// </summary>
public class DeferredExecutor
{
private const int DEFAULT_CAPACITY = 32;
private abstract class DeferredAction
{
public IDeferredAction Action;
public byte Key;
public int RepeatAmount;
public DeferredAction(IDeferredAction action, byte iKey, int iRepeatAmount)
{
this.Action = action;
this.Key = iKey;
this.RepeatAmount = iRepeatAmount;
}
}
private class TimeDeferredAction : DeferredAction
{
public float ExecutionTime;
public float Delay;
public TimeDeferredAction(float fExecutionTime, float fDelay, IDeferredAction action, byte iKey, int iRepeatAmount) :
base(action, iKey, iRepeatAmount)
{
this.ExecutionTime = fExecutionTime;
this.Delay = fDelay;
}
}
private class FrameDeferredAction : DeferredAction
{
public int ExecutionFrame;
public int Delay;
public FrameDeferredAction(int iExecutionFrame, int iDelay, IDeferredAction action, byte iKey, int iRepeatAmount) :
base(action, iKey, iRepeatAmount)
{
this.ExecutionFrame = iExecutionFrame;
this.Delay = iDelay;
}
}
private List<TimeDeferredAction> pTimeDeferredActions = new List<TimeDeferredAction>(DEFAULT_CAPACITY);
private List<FrameDeferredAction> pFrameDeferredActions = new List<FrameDeferredAction>(DEFAULT_CAPACITY);
public void AddFrameAction(IDeferredAction pDeferredAction, int iFrameDelay, byte iKey = 0)
{
AddRepeatedFrameAction(pDeferredAction, iFrameDelay, 0, iKey);
}
public void AddRepeatedFrameAction(IDeferredAction pDeferredAction, int iFrameDelay, int iRepeatAmount, byte iKey = 0)
{
if (pDeferredAction == null && iFrameDelay <= 0)
{
// assert
return;
}
var fExecutionTime = Time.frameCount + iFrameDelay;
var pNewAction = new FrameDeferredAction(fExecutionTime, iFrameDelay, pDeferredAction, iKey, iRepeatAmount);
this.AddFrameActionInternal(pNewAction);
}
private void AddFrameActionInternal(FrameDeferredAction pNewAction)
{
var iCount = pFrameDeferredActions.Count;
//add directly on empty list
//no need to iterate through all if ExecutionTime is larger or same as last which is highest
if (iCount == 0 ||
iCount > 0 && pNewAction.ExecutionFrame >= pFrameDeferredActions[iCount - 1].ExecutionFrame)
{
pFrameDeferredActions.Add(pNewAction);
return;
}
//always insert at proper position to keep list sorted
for (var i = 0; i < iCount; i++)
{
if (pFrameDeferredActions[i].ExecutionFrame > pNewAction.ExecutionFrame)
{
pFrameDeferredActions.Insert(i, pNewAction);
return;
}
}
}
public void AddTimeAction(IDeferredAction pDeferredAction, float fDelay, byte iKey = 0)
{
this.AddRepeatedTimeAction(pDeferredAction, fDelay, 0, iKey);
}
public void AddRepeatedTimeAction(IDeferredAction pDeferredAction, float fDelay, int iRepeatAmount, byte iKey = 0)
{
if (pDeferredAction == null || fDelay <= 0f)
{
// assert
return;
}
var fExecutionTime = Time.time + fDelay;
var pNewAction = new TimeDeferredAction(fExecutionTime, fDelay, pDeferredAction, iKey, iRepeatAmount);
this.AddTimeActionInternal(pNewAction);
}
private void AddTimeActionInternal(TimeDeferredAction pNewAction)
{
var iCount = pTimeDeferredActions.Count;
//add directly on empty list or
//no need to iterate through all if ExecutionTime is larger or same as last which is highest
if (iCount == 0 ||
iCount > 0 && pNewAction.ExecutionTime >= pTimeDeferredActions[iCount - 1].ExecutionTime)
{
pTimeDeferredActions.Add(pNewAction);
return;
}
//always insert at proper position to keep list sorted
for (var i = 0; i < iCount; i++)
{
if (pTimeDeferredActions[i].ExecutionTime > pNewAction.ExecutionTime)
{
pTimeDeferredActions.Insert(i, pNewAction);
return;
}
}
}
public void Update()
{
this.UpdateFrameDependentActions();
this.UpdateTimeDependentActions();
}
private void UpdateFrameDependentActions()
{
//to make sure all with same execution frame are executed at the same frame ~
while (pFrameDeferredActions.Count > 0 &&
pFrameDeferredActions[0].ExecutionFrame <= Time.frameCount)
{
var pAction = pFrameDeferredActions[0];
pAction.Action.OnExecuteDeferred(pAction.Key);
pFrameDeferredActions.RemoveAt(0);
if (pAction.RepeatAmount >= 1)
{
pAction.RepeatAmount--;
pAction.ExecutionFrame = Time.frameCount + pAction.Delay;
AddFrameActionInternal(pAction);
}
}
}
private void UpdateTimeDependentActions()
{
//to make sure all with same execution time are executed at the same time ~
while (pTimeDeferredActions.Count > 0 &&
pTimeDeferredActions[0].ExecutionTime <= Time.time)
{
var pAction = pTimeDeferredActions[0];
pAction.Action.OnExecuteDeferred(pAction.Key);
pTimeDeferredActions.RemoveAt(0);
if (pAction.RepeatAmount >= 1)
{
pAction.RepeatAmount--;
pAction.ExecutionTime = Time.time + pAction.Delay;
AddTimeActionInternal(pAction);
}
}
}
public bool Cancel(IDeferredAction pActionToCancel, byte iKey = 0)
{
var bResult = false;
for (var i = pFrameDeferredActions.Count - 1; i >= 0; i--)
{
var pFrameDeferredAction = pFrameDeferredActions[i];
if (pFrameDeferredAction.Action == pActionToCancel && pFrameDeferredAction.Key == iKey)
{
pFrameDeferredActions.RemoveAt(i);
bResult = true;
}
}
for (var i = pTimeDeferredActions.Count - 1; i >= 0; i--)
{
var pTimeDeferredAction = pTimeDeferredActions[i];
if (pTimeDeferredAction.Action == pActionToCancel && pTimeDeferredAction.Key == iKey)
{
pTimeDeferredActions.RemoveAt(i);
bResult = true;
}
}
return bResult;
}
public void CancelAll()
{
this.pFrameDeferredActions.Clear();
this.pTimeDeferredActions.Clear();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment