====== Итерирование ====== Чтобы реализовать вашу реальную логику Механики, вам потребуется обработать все Сущности, удовлетворяющие [[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|оперированием]]. В основном это связано с возможностью контролировать каждый аспект итерации вручную и исключать все лишние проверки. Вам просто нужно точно знать, что вы делаете и чего пытаетесь достичь, так как этот способ менее безопасен.