Использование битовых массивов в UE

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 - зомби которые принадлежат спавнеру который за радиусом кулингом

Last updated

Was this helpful?