Триггер таймер для циклических функций
Описание
Класс представляет собой условно бесчисленный контейнер физических таймеров. Физический таймер – это циклозависимая структура, содержащая в себе набор инструментов для работы с временными операциями. Зависимость от цикла означает, что каждый таймер должен быть строго определен в цикле, в котором планируется проведение операций. Чтобы определить таймеры относительно цикла, достаточно сделать привязку экземпляра класса.
Методы
Attach: Синхронизирует объект класса с циклом.
подробно
Отвечает за привязку объекта к циклу, обеспечивая корректную обработку возвращаемых значений физических таймеров.
aCTimer Timer;
void Loop()
{ 
    Timer.Attach();
}
Tick: выполняет большинство операций над таймерами.
подробно
Экземпляр класса содержит безмерное хранилище таймеров (структура CTimer), обращение к которым происходит посредством метода Tick ( или оператор () ), принимающим 3 значения:
   ид таймера,
   задержка (в мс. дэфолт 100мс),
   режим:
      T_LINEAR – Возвращает значение каждую итерацию с учётом производительности игры.
      T_PRIMARY – Работает аналогично первому, но возвращает значение уже на этапе регистрации.
      T_EXACT – Возвращает значение каждую итерацию без учёта погрешности на производительность игры. Обращаясь к таймеру (а это происходит через его id) которого не существует, он автоматически создастся (назовем это моментом регистрации).
Пример ниже демонстрирует обращение к таймерам. Можно заметить, что объектной целью в обоих случаях является экземпляр имеющий id 10. По этому выполнения обоих условий будет всегда идентично.
aCTimer Timer;
void Loop()	
{			
    Timer.Attach();
		
    if(Timer.Tick(10))	
        <...>	
		
    while(...)	
    {		
        if(Timer.Tick(10))	
            <...>			
    }		
}
Задержка определяет интервал срабатывания таймера. Задержку можно изменить во время обращения к таймеру. Изменения войдут в силу с новой итерацией.
В примере ниже представлен таймер, задержка которого будет постепенно расти:
aCTimer Timer;
int c=0;	
void Loop()			
{	
    Timer.Attach();		
    if(Timer.Tick(10,c++))	
        <...>			
}
Если установить задержку таймера на -1, то он заморозится.
Код ниже сработает спустя 5 секунд с момента объявления объекта, после чего его выполнение прервется:
aCTimer Timer;
int c = 5000;	
void Loop()		
{	
    if(Timer.Tick(10,с))			
        c = -1;
}
При объявлении таймера задержку достаточно указать один раз.
aCTimer Timer;
void Loop()
{
    Timer.Attach();	
			
    if(Timer.Tick(10,2000))	//Работает раз в 2 секунды
        <...>

    if(Timer.Tick(10))	//Таймер работает одновременно с предыдущим
        <...>	
}
T_LINEAR: Режим установлен по умолчанию. Вступает в работу не сразу, а через итерацию, равной задержке.
В примере ниже описаны 3 эквивалентных вызова линейного таймера:
aCTimer Timer;
void Loop()
{
    Timer.Attach();

    if(Timer.Tick(10,200))	
        <...>

    if(Timer.Tick(10,200,0))
        <...>

    if(Timer.Tick(10,200,T_LINEAR))
        <...>
}
T_PRIMARY: Режим позволяет сработать таймеру в момент его создания. Таким образом, можно создавать конструкции, которые вызываются лишь 1 раз за время своего существования.
В примере ниже таймер вернет значение только 1 раз при объявлении, после чего сразу заморозится:
aCTimer Timer;
void Loop()		
{	
    Timer.Attach();	

    if(Timer.Tick(10,-1,T_PRIMARY))	
        <...>	

}
T_ EXACT: Строгий таймер является аналогом T_LINEAR. Разница заключается в том, что линейный таймер сглаживает погрешности производительности игры и подстраивается под нее, строгие же наоборот избегает какого-либо контакта с движком, выполняясь независимо от него. Полезно выбирать строгий режим при необходимости использования коротких задержек при низкой частоте цикла с целью решения математически точных функций или создания конструкций восстановления *потерянных итераций*. Потерянные итерации - это своевременно невыполненные функции движка, обусловленные сбоем, зависанием, более медленным циклом по отношению к таймеру или из-за других подобных проблем. о восстановлении см. макрос force.
*Глобально принципиальной разницы о том, какой же таймер использовать - нету. Все происходит на усмотрение программиста - исключительно на его понимание работы режимов и ситуации, для которой он выбирается.*
New: создаёт дочерний экземпляр относительно таймера.
подробно
Так как таймеры циклозависимы, использовать один таймер в другом, пользуясь при этом одним и тем же экземпляром, - нельзя, поскольку каждый физический объект является циклом с собственной задержкой. Метод New автоматически создаёт экземпляр aCTimer относительно заданного объекта CTimer. (aCTimer::CTimer[]->aCTimer)
Ниже приводится пример регистрации новго экземпляра aCTimer внутри объекта CTimer с id 10:
aCTimer Timer;
void Loop()	
{
    Timer.Attach();

    if(Timer.Tick(10,200,1))	
    {	
        if(Timer.New(10)->Tick(10,400))
            <...>
    }	
}
Delete: ручное удаление таймера.
подробно
Удаление, в основном, происходит автоматически внутри экземпляра aCTimer. Для общего понимания: следить за поведением таймеров нам помогает метод Attach. Если появляется объект, который по каким-либо причинам перестает использоваться, - он сразу удаляется. Происходит это не моментально, а только после того, как алгоритм в этом точно убедится (Связь разрывается, если не происходит обращений через метод Tick). Так вот бывают случаи, когда встает задача избавиться от таймера без перекрытия метода Tick. В этом поможет Delete для моментального уничтожения объекта.
aCTimer Timer;
void Loop()
{	
    Timer.Attach();
 
    if(KeyPress(KEY_0))
    {
        if(Timer.Tick(10,500,T_PRIMARY))
            <...>
    }	
    else
        Timer.Delete(10);
}
Exist: проверяет существование таймера.
подробно
Данный метод возвращает результат существования таймера. С его помощью можно отслеживать момент удаления таймеров, регистрировать новые, уникальные и многое другое.
В примере ниже представлен вариант, где по некоторому условию необходимо удалить таймер 10, если он существует:
aCTimer Timer;
void Loop()
{
    Timer.Attach();
 
    if(...)
        if(Timer.Exist(10))
            Timer.Delete(10);
}
force: макрос программного восстановления итераций.
подробно
force – это цикл, отслеживающий число пропущенных по каким-либо причинам итераций.
Представим ситуацию: имеется таймер частотой 100мс. Вдруг выполнение программы по неизвестным причинам приостанавливается на 2 секунды. В момент отвисания приложения теряется 20 итераций (2000 / 100). force отследит этот разрыв и автоматически восстановит его.
В примере ниже представлен таймер 10 и вписанный в него дочерний объект. Счётчики i и j считают количество итераций таймеров.
aCTimer Timer;
int i = 0;
int j = 0;
void Loop()
{
    Timer.Attach();
 
    if(Timer.Tick(10,1000))
    {
        i++;
        if(Timer.New(10)->Tick(20,200,T_PRIMARY))
        {	
            j++;
        }	
    }
}
На выходе можно заметить, что значения i и j равны, даже при разных задержках. Это обусловлено тем, что вложенный таймер не может выполняться раньше родительского. Чтобы исправить это, используем макрос force. Для исключения погрешностей воспользуемся режимом T_EXACT.
В следующем примере результат счётчика j будет больше счётчика i в 5 раз.
aCTimer Timer;
int i = 0;
int j = 0;
void Loop()
{
    Timer.Attach();
 
    if(Timer.Tick(10,1000))
    {
        i++;	
        force(Timer.New(10)->Tick(20,200,T_EXACT))
        {	
            j++;
        }
    }
}
Макрос force также может принимать условия, в которых, например, можно заблокировать выполнение дочернего таймера, если значение счётчика i чётное. Соответственно результат счётчика j на выходе будет в 2 раз меньше предыдущего примера.
aCTimer Timer;
int i = 0;
int j = 0;
void Loop()
{
    Timer.Attach();
 
    if(Timer.Tick(10,1000))
    {
        i++;	
        force(Timer.New(10)->Tick(20,200,T_EXACT) && i%2 != 0)
        {	
            j++;
        }	
    }
}