Last active
September 1, 2021 23:58
-
-
Save SradnickDev/fa92b1bebf764de2b5c388d357156da0 to your computer and use it in GitHub Desktop.
Delay calls by time or frames, alternative for coroutines and Monobehaviour.Invoke.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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