Is it possible to manually iterate a TActorIterator over multiple ticks?

Is it possible to manually iterate a TActorIterator over multiple ticks?

I tried declaring the Iterator in the .h but couldn’t figure out how.

I would like to manually iterate to the next Actor once per tick in the .cpp, but it keeps resetting each time. (because I can only figure out how to declare it locally in the Tick())

I am using this code to test:


	TActorIterator<AActor> ActorIterator(GetWorld());
	GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Red, "(Itr Count) " + FString::FromInt(ActorIterator.GetProgressNumerator()));
	++ActorIterator;

The Debug message’s displayed value should increase each tick after “++ActorIterator”, but doesn’t because ActorIterator is local to the Tick() and resets.

Maybe there is a way to tell the iterator to just go to the Actor at a specified index?
That way I can just make a global int variable, increase that every tick, and use it as the index…

Just copy things to an array and then you can use your index solution.




// In your .h
TArray<TWeakObjPtr<AActor>> MyActorList;

int  MyCurrentActorIndex;

// In some code (you could do it in a tick)

if (MyActorList.Num() == 0) // Our array is empty, populate it.
{
   TActorIteractor<Aactor> ActorIter(GetWorld());
   while (ActorIter) 
   {
       MyActorList.Add(ActorIter);
    }
    // Start at index 0.
    MyCurrentActorIndex = 0;
}
else
{
   if (MyActorList[MyCurrentActorIndex].IsValid()) // The IsValid check makes sure the Object wasn't deleted out from under us.
   {
      // Do Whatever
   } 
  ++MyCurrentActorIndex; // Increment our index for next frame (you'll want to clamp to make sure you don't go over your array size, loop back around to zero if you want, etc.)
}

Im already well aware of how to manually increment a TArray.

The whole point of wanting to manually iterate a TActorIteractor was to avoid cycling through the Iterator’s Actor List in a single tick.
Which is a requirement of your solution. And I can’t just do a one time copy of the Iterator’s Actor List into a TArray in BeginPlay() or other single call function, because the game spawns new Actors during play. Id need to update that TArray with the new Actors, by doing a full cycle through the Iterator’s Actor List, which again is what Im trying to avoid.

I can’t see a scenario where this isn’t either super messy or prone to bugs, to be honest. What’s the motivation for doing this in the first place?

TActorIterator can be thought of as just a wrapper for a TArray, it’s not a singleton that exists in the world somewhere, it’s a type with some handy accessor functions. You can get the Index by calling ->GetIndex()

So far as I can tell, you’re only option here is to store a TActorIterator member variable, and increment it manually a set number of times each tick. It’ll be up to you to ensure the iterator is up to date with what’s in or not in the world, but I don’t see how you’re going to be able to do that easily without just creating a new iterator - at which point your manual iteration will be out of date anyway.

As @TheJamsh points out, TActorIterator is just a snapshot, not a live list. When you create a TActorIterator, it use GetObjectsOfClass(…) to recursively go through the world, check every actor, and add it to an internal list which the iterator just is a thin wrapper above the results (which is an array).

Keeping an index into that list (where objects could be deleted, or objects are added and your list is out of date) is going to be tricky at best.

The idea is to slowly build the list of actors over time to reduce lag.
The game makes use of alot of TArrays as it is and Im going through and seeing which ones can be split up across multiple ticks to soften the strain.
Searching through the list of active Actors is one that Im trying to optimize. It doesn’t need to be up to date in a single tick for what Im using it for anyway.

If I can’t iterate through a TActorIterator of the games current Actor list, what about GetWorld()->PersistentLevel->Actors?
Its a TTransArray, but I seem to be able to get the actors out of it, and it appears to work like a regular TArray. Is there something about TTransArrays that I would need to be aware of while using them?

I can’t imagine how it’s supposed to work? Sure it could iterate the actor list of the world over multiple ticks, but they aren’t sorted so if something else adds/removes actors inbetween you will get garbage.

But you’re still going to have the same problems. You can’t iterate an array sequentially if you’re changing the elements in it during iteration.

What is the end goal of this anyway? Accessing the Actors of the level is fast - what you’re checking against each one is probably the slow part.

Why not? .Add() just puts it at the end of the TArray, and .Num() tells me the length of the TArray.
All I have to do is make sure my Index int loops back to 0 at the right time(>= .Num())…


void Tick()
{
	if (Array.IsValidIndex(Index))
	{
		//Do things to Array[Index];
		TransitionArray.Add(Array[Index]);
	}
	Index++;
	if (Index >= Array.Num())
	{
		FinalArray = TransitionArray;
		TransitionArray.Empty();
		Index = 0;
	}
}

So unless the UWorld is putting Actors into PersistentLevel->Actors in some funky manner I don’t see why they would ever be out of order.
Unless it is moving them around in the TArray for some reason? I don’t know. Hence why I asked about it.

EDIT:
In fact since last posting I have been doing exactly that with most of my pawn’s TArrays.

Ive actually narrowed down the remaining Lag to GetCharacterMovement()->Velocity = CurrentVelocity;
At around 45 pawns my FPS dips below 60, and continues to dip with each new pawn.
Commenting this line out allows the game to maintain 60 FPS up to 100 pawns(I stop spawning them at that point), but I am using that line to move the pawn so it is kind of important. XD

Consider the following situation:

PersistentLevel->Actors has 5 Actors; A, B, C, D, E.

You iterate and Tick Actor A. So now Iterator index is 1.

Next frame, no changes, B gets ticked. Now Iterator index is 2.

Next frame, B is destroyed and removed. PersistentLevel->Actors now looks like this: A, C, D, E.

Your iterator, now rather than ticking C (as it should), skips it and ticks D. Iterator index is now 3.

“But what if I just check if the size changed and adjust accordingly if its less than last frames count.”

Okay, let’s say B is removed but a new Actor was also created during the frame so now it looks like this: A, C, D, E, F.

See the problem with just using an index?

That assumes Im not willing to suffer through a bit of inaccuracy for a few ticks. Im not expecting the Actors in the TArray to be added or removed frequently.
It is set up to do normal Full For Loop passes when the pawn is initialized after spawn. Any changes after that is expected to have MANY ticks before any further changes which should be plenty of time to correct the inaccuracy. In fact if it comes down to it I can just force a normal Full For Loop Pass when something is removed. The manual iteration is meant to replace a Full For Loop Pass of an unchanging TArray…


Wait. Hold up, Ive gone WAY off topic here.
The whole point of the manual iteration in the case of my OP was to get a potentially VERY LARGE list of Actors without jamming that action all onto one tick. ORDER of actors was actually not of importance to that specific goal, so your scenario is actually an acceptable one.

This is my fault. I got caught up in the discussion and started applying other parts of my code that are actually not relevant to this question. Sorry. :confused:

Character Movement is expensive. The movement component itself is very complex, and if you want to move 100 pawns, you’re probably better off writing a simpler movement component to handle them. Paragon’s minions for example use a much simpler movement component that effectively just snaps the character to the NavMesh.

The question is though - what is the end goal for the actor iterator? What is it you need to access the entire list of actors for? If you’re just looking for specific actors, there are far more optimal ways to do it than with an actor iterator.