====== Итерирование ======
Чтобы реализовать вашу реальную логику Механики, вам потребуется обработать все Сущности, удовлетворяющие [[ru:toolworks:docs:apparatus:filter|Фильтру]]. В целях эффективности и последовательности это делается вручную через [[ru:toolworks:docs:apparatus:enchaining|Цепи]]. Итерируясь по цепям, вы итерируетесь по всем Сущностям и Сущностным объектам внутри конкретной цепи.
В итерации по цепям помогают Курсоры. Их семантика напоминает итераторы в стандартных контейнерах.
===== Работа в C++ =====
Итерирование по цепи сделано через специальный тип объекта //Курсор// (Cursor).
Можете использовать столько, сколько захотите, но обычно, достаточно одного:
FChain::FCursor Cursor = Chain->Iterate();
Если цепь [[ru:toolworks:docs:apparatus:solidity|твердотельная]], то код будет выглядеть так:
FSolidChain::FCursor SolidCursor = SolidChain->Iterate();
Когда вы получили желанный курсор, вы можете построить простой while-цикл:
while (Cursor.Provide())
{
auto Trait = Cursor.GetTrait();
...
}
''[[appi>struct_t_chain_1_1_f_cursor.html#a4d4c891b529d09be7a57791cd886587b|Provide()]]'' метод подготавливает нужное состояние и возвращает ''false'', когда закончились слоты в цепи (''true'' иначе).
Имея твердотельный Курсор вы можете получить прямую ссылку (без копирования) на трейт (используя метод ''[[appi>struct_t_chain_1_1_f_cursor.html#acf92a6a5684871a0c24f1e7360314ada|GetTraitRef()]]''):
while (SolidCursor.Provide())
{
auto& Trait = SolidCursor.GetTraitRef();
...
}
Пожалуйста, заметьте, что цепи утилизируются автоматически, когда все итерируемые курсоры закончили итерироваться по слотам.
Чтобы предотвратить такое поведение особо, можете использовать вызовы ''[[appi>struct_t_chain.html#abdde97a57f62214dff4e643287eb2fae|Retain()]]''/''[[appi>struct_t_chain.html#a665154da00eac2bcc7760851583db281|Release()]]'', чтобы самостоятельно контролировать время жизни объектов:
Chain->Retain(); // Забрать цепь.
FChain::FCursor Cursor = Chain->Iterate();
while (Cursor.Provide())
{
...
}
// Здесь выполняем операции над цепью.
// Гарантируется, что она не будет удалена.
...
Chain->Release(); // Очищаем данные цепи.
==== Встроенные курсоры ====
Аппарат предоставляет способ итерироваться по цепям встроенными Курсорами. В основном, эта технология используется внутри плагина для корректной работы Blueprint-ов, и вам её стоит избегать в своём C++ коде.
Код будет довольно прост. Он состоит из while-цикла с одним условием:
while (Chain.BeginOrAdvance())
{
...
}
Внутри этого цикла вы можете реализовать нужную логику, используя [[appi>struct_f_subject_handle.html|Сущности]] напрямую или служебные методы [[appi>struct_f_chain.html|Цепей]]:
while (Chain.BeginOrAdvance())
{
FSubjectHandle Subject = Chain.GetSubject();
UMyDetail* MyPosition = Chain.GetDetail();
FMyTrait MyVelocity;
Chain.GetTrait(MyVelocity);
MyPosition->X += MyVelocity.VelocityX * DeltaTime;
MyPosition->Y += MyVelocity.VelocityY * DeltaTime;
...
MyVelocity.VelocityX = 0;
MyVelocity.VelocityY = 0;
Subject.SetTrait(MyVelocity);
}
Когда указатель-Курсор Цепи пройдёт последнюю доступную Сущность (или Сущностный объект), Цепь будет уничтожена и ранее заблокированные чанки и ремни вновь разблокируются, все ожидаемые структурные изменения будут незамедлительно выполнены (если они вообще были).
==== Прямое итерирование ====
Если вам необходимо итерироваться по чанкам напрямик, вы можете инициализировать Чанк-Прокси.
Обычно, вам потребуется проитерироваться по всем собранным Прокси через соответсвующий [[appi>class_a_mechanism.html#ae72188d973bed3d8484dc5ab87e5e1e1|Enchain]] метод и
в каждом чанке итерироваться по его Сущностям-слотам.
Пример будет таким:
TArray> ChunkProxies;
Mechanism->Enchain(TFilter(), ChunkProxies);
for (int32 i = 0; i < ChunkProxies.Num(); ++i) // Итерируемся по всем подходящим чанкам...
{
auto& ChunkProxy = ChunkProxies[i];
for (int32 j = 0; j < ChunkProxy.Num(); ++j) // Итерируемся по слотам...
{
// Perform the necessary logic...
ChunkProxy.TraitRefAt(j).Position += FVector::UpVector * DeltaTime;
}
}
Заметьте, однако, что этот подход абсолютно ручной, и не выполняет какие-либо логические проверки во время итерирования, например, [[ru:toolworks:docs:apparatus:flagmark|совпадение флагов]].
Если вы также выполняете изменение топологии внутри ваших циклов, то ваши сущности могут произвольно изменить свои чанки или слоты, поэтому вам скорее всего потребуется проверять слоты на (не-)свежесть.
Сделать это можно так:
for (int32 j = 0; j < ChunkProxy.Num(); ++j) // Итерируемся по всем слотам чанка...
{
if (ChunkProxy.IsStaleAt(j)) continue; // Пропустить сущность, если она была перемещена или удалена...
ChunkProxy.SubjectAt(j).SetTrait(FSpeedBoostTrait{10.0f});
}
Итерация чанков напрямую (через прокси) может дать определенный прирост производительности по сравнению с обычной итерацией и [[ru:toolworks:docs:apparatus:operating|оперированием]]. В основном это связано с возможностью контролировать каждый аспект итерации вручную и исключать все лишние проверки. Вам просто нужно точно знать, что вы делаете и чего пытаетесь достичь, так как этот способ менее безопасен.