using Allocator = TInlineAllocator<8>;
TBitArray<Allocator> Changed, Active; - массивы для храниения битов (аналог bool) да или нет
const auto active = Active.GetData();
for (TConstSetBitIterator<Allocator> bit{ Changed }; bit; ++bit)
{
const auto i = bit.GetIndex();
Operation(i, FBit{ active, i });
}
TConstSetBitIterator - проходит про битам которые установлены, т.е равны 1
FBit{ active, i } - ссылка на бит, конструирующаяся из элемента массива active под индексом i
Как работает void FZombies::Cull(UWZFCullingComponent* Parent)
С начала мы сохроняем количество всех зомби в size, но берем только тех, кто не помер и не удалился:
int32 size = 0;
int32 partitions = 0;
auto remove = [&, start = 0](const int32 Begin, const int32 End) mutable {
const auto partition = Partition.GetData();
const auto actors = Enemies.GetData();
const auto spawners = Spawners.GetData();
const auto activation = Activation.GetData();
const auto change = Change.Changed.GetData();
const auto active = Change.Active.GetData();
for (int32 i = Begin; i < End; ++i)
{
const auto end = partition[i];
const auto previous = size;
for (auto j = start; j < end; ++j)
{
if (actors[j] != nullptr)
{
ForEach([&](auto& Container) { Container[size] = Container[j]; }, actors, activation);
ForEach([&](auto& Container) { WZF::Culling::FBit{ Container, size } = WZF::Culling::FBit{ Container, j }; }, active);
++size;
}
}
start = end;
const auto spawner = spawners[i] - 1;
if (size > previous)
{
if (partitions < i)
{
Parent->Spawners.Activation[spawner].Zombies = partitions + 1;
}
spawners[partitions] = spawner + 1;
partition[partitions] = size;
++partitions;
}
else if (spawner > 0)
{
Parent->Spawners.Activation[spawner].Zombies = 0;
}
}
};
remove(0, Retained);
const int retained = size;
remove(Retained, Partition.Num());
const auto added = size - retained;
Затем сетим только те биты в тру, которое подходят под условие радиуса
Change.Changed.Init(true, size);
Change.Active.RemoveAt(size, Change.Active.Num() - size);
const auto enemies = Enemies.GetData();
const auto change = Change.Changed.GetData();
const auto radius = Radius.GetData();
for (int32 i = 0; i < Enemies.Num(); ++i)
{
const auto location = enemies[i]->GetActorLocation();
for (const auto player : Parent->Players.Locations)
{
FBit bit = { change, i };
bit = bit & (FVector::DistSquared(location, player) > radius[i]);
}
}
Change.Update();
if (added > 0)
{
Change.Changed.RemoveAt(retained, added);
}
В итоге если Change.Changed содержит тру - значит его нужно удалить, но если Игрок покинул кулинг зомби раньше кулинга спавнера - зомби пзапускает другую логику:
if (retained < Change.Active.Num())
{
const auto active = Change.Active.GetData();
for (TConstSetBitIterator<WZF::Culling::FChange::Allocator> bit{ Change.Active, retained }; bit; ++bit)
{
const auto i = bit.GetIndex();
if (FBit{ active, i })
{
if (const auto enemy = enemies[i])
{
if (!isCanDestroy(enemy))
{
continue;
}
Parent->StopObserveEnemyTags(enemy);
enemy->Destroy();
}
}
}
}
FBitReferences - родной реф для битов
enemies - зомби которые принадлежат спавнеру который за радиусом кулингом