Contents
The Actor List
All actors in Unreal are connected though a gigantic list (well it is actually a dynamic array, but to make life easier think of it as a list) that exists only in the native C++ code. Without this list, actors would be just plain old objects. The base of this "list" is actually the native level object (the XLevel
property of all actors). This list exists on both client and server. When a client "receives" an actor, it is added and when its connection is closed (except with bNetTemporary), it is removed.
Spawning an actor automatically adds it at the end of the list. Note that LevelInfo (accessed via the Level
property of any actor) is always the first actor. Destroying an actor automatically removes it from the list.
UnrealScript's ForEach
command makes it easy to deal with large groups of actors, for example all of the actors in a level, or all of the actors within a certain distance of another actor. ForEach
works in conjunction with a special kind of function called iterator function whose purpose is to iterate through a list of actors. Most of these iterator functions use the native actor list, so sometimes it's faster to use For
to iterate through much smaller linked lists like the pawn list or one of the mutator lists.
Syntax
All iterator function use a similar syntax:
local aClassName aVariable; foreach IteratorFunction(class'aClassName', aVariable, other parameters) { // execute something here }
The variable used as the second parameter must declared to hold an instance of the class used as the first parameter. The class must be class'Actor'
or a subclass for all iterators except AllObjects and AllDataObjects which can use class'Object'
or any other Object subclass.
Iterators can also be called for other objects:
local aClassName aVariable; foreach anotherObject.IteratorFunction(class'aClassName', aVariable, other parameters) { // execute something here }
This syntax doesn't make sense for all iterator functions, though. It is useful when using an iterator function which returns objects based on the object it was called for (e.g. ZoneActors or ChildActors) or when you want to use an iterator that isn't available in your class. (e.g. DynamicActors from an Interaction)
Note that the game will crash with the error "Unknown code token 58" when the anotherObject
variable is None
. Also the actor iterators should only be called in a game environment. They will cause problems (e.g. a crash with "Unknown code token 31") when executed e.g. from BrushBuilder code in the editor.
Definitions
To make the iterator function descriptions easier to read, the following simplifications are used:
- A class is a subclass of the BaseClass
- This means the class could be BaseClass itself or a direct or indirect subclass of BaseClass. Basically this relation corresponds to the result of the global function
ClassIsChildOf(aClass, BaseClass)
. - Actor A is owned by actor B
- This includes direct (
A.Owner == B
) and indirect ownership, i.e. alsoA.Owner.Owner == B
orA.Owner.Owner.Owner == B
etc. - An actor is (not) bHidden
- short for: "An actor's bHidden property is (not) set to
True
"
Even though the descriptions here (and the actual declarations in Object, Actor and other classes defining iterator functions) talk about class<Object/Actor> as the first parameter type and Object/Actor as the second parameter type, you can and should use subclass types. The actual type of the second parameter can be narrowed down at compile-time by specifying a class literal as the first parameter:
local class<Info> InfoClass< SEMI > local Info I; local Actor A; foreach DynamicActors(class'Info', I) { // compiles fine // loops through all Info actors } foreach DynamicActors(class'ReplicationInfo', I) { // compile-time error (type mismatch in second parameter) // even though Info actors would fit into the RI variable, // the compiler requires a perfect type match } InfoClass = class'ReplicationInfo'; foreach DynamicActors(InfoClass, I) { // compile-time error (type mismatch in second parameter) // InfoClass could be None, in which case the iterator would fall back // to class'Actor', which would not fit into a variable of type Info. } foreach DynamicActors(InfoClass, A) { // compiles fine // loops through all ReplicationInfo actors, but A needs to be typecasted // if you want to access any Info or ReplicationInfo properties or functions }
The descriptions here are for UT2004. They may not be accurate for other games. For example, AllDataObjects is specific to UT2004. UT doesn't have AllObjects or CollidingActors. It does have VisibleCollidingActors, though. The AllObjects iterator is available in Deus Ex, even though it's an UnrealEngine 1 game like UT.
Object
- AllObjects (class<Object> BaseClass, out Object Object)
- Iterates through all objects existing in the game and returns those whose class is a subclass of the BaseClass.
Warning: By all means stay away from this iterator if you want to work with actors!
Actor
- AllActors (class<Actor> BaseClass, out Actor Actor, optional name MatchTag)
- Iterates through all actors in the level and returns those whose class is a subclass of the BaseClass. If a MatchTag was specified, an actor is only returned if its Tag matches the specified value.
Note: For some base classes like NavigationPoint, Pawn (UT), Controller or Vehicle there are linked lists (e.g.Level.NavigationPointList
orLevel.Game.VehicleList
) which may be faster to iterate using a for loop than going through the entire actor list with ForEach AllActors.
- DynamicActors (class<Actor> BaseClass, out Actor Actor, optional name MatchTag)
- Iterates through all actors in the level and returns those whose class is a subclass of the BaseClass. If a MatchTag was specified, an actor is only returned if its Tag matches the specified value.
Internally the actor list is sorted so it starts with the static actors, followed by the non-static actors. The engine remembers the index of the first non-static actor, so this iterator can start right at that index and actually does not have to check the value of the bStatic property.
- ChildActors (class<Actor> BaseClass, out Actor Actor)
- Iterates through all actors in the level and returns those whose class is a subclass of the BaseClass and that are owned by the actor the iterator function is called on.
- BasedActors (class<Actor> BaseClass, out Actor Actor)
- Iterates over the actor's Attached array (i.e. all actors "standing on" this actor) and returns those actors whose class is a subclass of the BaseClass.
- TouchingActors (class<Actor> BaseClass, out actor Actor)
- Iterates over the actor's Touching array (i.e. all actors "interpenetrating" this actor) and returns those actors whose class is a subclass of the BaseClass.
- TraceActors (class<Actor> BaseClass, out Actor Actor, out vector HitLoc, out vector HitNorm, vector End, optional vector Start, optional vector Extent)
- Performs a trace from the start location to the end location, using the specified extent. Then iterates over the resulting list of actors hit by the trace and returns those whose class is a subclass of the BaseClass.
HitLocation and HitNormal are set to the corresponding values for each of the actors returned. If no Start location is specified, this actor's location is used instead. If no Extent is specified, the zero vector is used.
- RadiusActors (class<Actor> BaseClass, out Actor Actor, float Radius, optional vector Loc)
- Iterates through all actors in the level and returns those whose class is a subclass of the BaseClass and whose distance to the specified location is less than Radius + that actor's CollisionRadius.
If no location is specified, this actor's Location is used instead.
Note: The target actor's CollisionRadius is used regardless of its collision properties.
- VisibleActors (class<Actor> BaseClass, out Actor Actor, optional float Radius, optional vector Loc)
- Iterates through all actors in the level and performs a FastTrace from the specified location to the those actors that are not bHidden, whose class is a subclass of the BaseClass and whose distance to the specified location is less than Radius. If that trace does not report any collision with world geometry, the actor is returned.
If no location is specified, this actor's Location is used instead. The Radius defaults to 0.0 and thus should always be specified, unless only actors in that exact location should be returned.
- VisibleCollidingActors (class<Actor> BaseClass, out Actor Actor, float Radius, optional vector Loc, optional bool bIgnoreHidden)
- Fetches a list of actors within the specified Radius around the specified location from the collision hash. Then iterates over the resulting list of actors and performs a FastTrace from the specified location to the those actors whose class is a subclass of the BaseClass and (if told to bIgnoreHidden) that are not bHidden. If that trace does not report any collision with world geometry, the actor is returned.
If no location is specified, this actor's Location is used instead.
- CollidingActors (class<Actor> BaseClass, out Actor Actor, float Radius, optional vector Loc)
- Fetches a list of actors within the specified Radius around the specified location from the collision hash. Then iterates over the resulting list of actors and returns those actors whose class is a subclass of the BaseClass.
If no location is specified, this actor's Location is used instead.
= ZoneInfo
- ZoneActors (class<Actor> BaseClass, out Actor Actor)
- Iterates through all actors in the level and returns those whose class is a subclass of the BaseClass and that are in the zone represented by the ZoneInfo the iterator function was called on.
GameInfo
- AllDataObjects (class objClass, out Object obj, string packageName)
- Iterates through all objects existing in the game and returns those that are contained in the specified package and whose class is a subclass of the BaseClass.
Note: This iterator will not return any objects if the specified data package is not loaded.
Performance Analysis
See Code Optimization for more specific information.
Iterator functions can be divided into several performance classes:
- object list iterators
- AllDataObjects
- AllObjects
These iterator functions walk over the entire object list and should be avoided if somehow possible.
- actor list iterators
- AllActors
- ChildActors
- DynamicActors
- RadiusActors
- VisibleActors
- ZoneActors
These iterator functions walk over the level's actor list and are considerably faster than the previous performance class.
DynamicActors skips the part of the list that contains only static actors, so it's faster than AllActors. Whether it also outperforms the other iterator functions in this list depends on the situation. The others perform their checks in native code, which you'd have to implement thoise checks in UnrealScript if you wanted to use DynamicActors.
VisibleActors should be used with care as it performes a FastTrace for every actor matching the other criterias. If you can rule out more actors with other checks, try using RadiusActors (or DynamicActors + radius check) + your additional checks + FastTrace instead.
- collision hash iterators
- CollidingActors
- VisibleCollidingActors
These iterator functions fetch a list of actors from the collision hash instead of walking the actor list. This makes these iterators faster than their counterparts RadiusActors and VisibleActors, as long as the radius stays small. The actual meaning for "small" may vary, but starting at around 2000UU you should think about switching to an actor list iterator.
Even below that radius, VisibleCollidingActors should be used with care as it performes a FastTrace for every actor matching the other criterias. If you can rule out more actors with other checks, try using CollidingActors + your additional checks + FastTrace instead.
- special purpose iterators
- BasedActors
- TouchingActors
- TraceActors
These iterator functions are very fast, since they walk over specific lists of actors. BasedActors and TouchingActors iterate over the Attached and Touching arrays respectively. TraceActors uses the result of a "multi-hit trace" as its actor list, i.e. it first does the tracing, then starts returning actors.
These performance categories are based on UT2004. There's no guarantee that they also apply to other UnrealEngine games. Especially BasedActors might have been implemented as actor list iterator in first generation engine games, since there's no Attached array.
Usage
Iterating actors is one of the most important concepts to understand in UnrealScripting. There are many times when this will be the only way for you to gain valid reference to other classes for use in your function, and as such, it is essential that you gain a very familiar understanding of the methods used in iterating.
AllActors is normally used with two parameters - the class, and a variable. The variable parameter may be a global, local, or argument variable, and there are situations when it is good to use each one. To use AllActors, first define a variable of the class you want to iterate. Next, designate the variable that will host the reference to this class object. Inside the brackets, specify what you want to happen each time an object of the specified class is found. The iterator variable is always equal to the last object that was found matching that class, so if you want to get a valid reference to each object in the game of a particular type, you would need to somehow assign that object to yet another variable inside the iterator, before it continues on to the next object of that class.
class MyMut extends Info; function PostBeginPlay() { local Pawn OnlyThisPawn; foreach AllActors(class'Pawn', OnlyThisPawn) { break; } }
This above code reads something like "Look for all actors in the game that are of the class 'Pawn'. When a Pawn is found is found, assign THAT Pawn to the variable OnlyThisPawn, then perform the statements within the next set of brackets. Once finished with those statements, look for the next actor of the class 'Pawn' and assign THAT pawn to the OnlyPawnVariable, overwriting the last value of the OnlyThisPawn variable." But, there is only one statement there! The break statement causes the foreach iterator to stop it's cycling through the Actors on the level. (It will stop looking for other Actors to assign to OnlyThisPawn). This is an extremely useful way to get a valid reference to an instance of a particular class when there isn't aren't any other ways of attaching to this class. In the example above, the iterator assigns the first actor of class'pawn' that it finds to the OnlyThisPawn variable, breaks out of the "ForEach" statement, and script execution continues through the rest of the function. You may now use the OnlyThisPawn variable without receiving the "Access None" error in the logfile, as long as there are valid Pawns in the level when you perform this iterator.
Typically, however, a ForEach statement should be used to perform a particular action on multiple instances of a class, to be most efficient. Avoid using extensive iterators in rapidly executing code (such as Tick), and instead try to place in situations where they are only executed once or at least rarely (PostBeginPlay, BeginState are good places). You should also avoid using iterators for assigning object variables, instead trying less resource-expensive methods of gaining valid references. For some ideas of how to do this, see Traversing Classes
Wormbo: Iterating through all Pawns is a bad example. Avoid using ForEach AllActors(class'Pawn', P)
on the server.
For (P = Level.PawnList; P != None; P = P.NextPawn)
does the same and is much more efficient. Always look for a linked list that contains the actors you want to access before using ForEach AllActors(...)
.
Using Iterators From Objects
Ever have an Object and want to use an iterator but realized much to your frustration that they are all defined down in Actor? Well boys and girls, this is actually very easily worked around as demonstrated by the following example:
class MyObj extends Object; var Actor MyActorRef; function SomeNiftyFunc() { local Actor AnActor; foreach MyActorRef.AllActors( class'Actor', AnActor ) break; }
Since iterators are just normal functions you can treat them as such and call them from any other Object that has a reference to them. Just be sure that your Actor reference is valid before you attempt to call an iterator on it, otherwise all sorts of "bad things" can happen.
function SomeNiftyFunc() { local Actor AnActor; if ( MyActorRef == NONE || MyActorRef.bDeleteMe ) return; foreach MyActorRef.RadiusActors( class'Actor', AnActor, 500 ) break; }
Related Topics
- Tim Sweeney's reference: UnrealScript Language Reference/Advanced Language Features
- Flow Syntax
- Code Optimization – If your framerate drops considerably when using iterators.
- Linked List/Existing Lists In Unreal Tournament also has some info on execution speed of the various iterator functions
Comments
Foxpaw: Destroying actors in Iterators seems to cause some strangeness. The following code caused mysterioud GPFs for the longest time for me:
foreach AllObjects( class'VehicularThing', Old ) Old.Destroy();
It wouldn't crash as soon as I executed it, but would go for about 8 cycles or so before crashing. I believe this may cause some kind of a memory leak or something, possibly due to the internal workings of the iterators. I stopped the GPFs by doing the following, and it hasn't caused a crash since:
foreach AllObjects( class'VehicularThing', Old ) MarkedMen[MarkedMen.Length] = Old; for (i=0;i<MarkedMen.Length;i++) MarkedMen[i].Destroy();
Daid303: The killall cheat never crashed for me... but it uses DynamicActors, why are you using AllObjects? (I can't think of any slower function)
exec function KillAll(class<actor> aClass) { local Actor A; if ( ClassIsChildOf(aClass, class'AIController') ) { Level.Game.KillBots(Level.Game.NumBots); return; } if ( ClassIsChildOf(aClass, class'Pawn') ) { KillAllPawns(class<Pawn>(aClass)); return; } ForEach DynamicActors(class 'Actor', A) if ( ClassIsChildOf(A.class, aClass) ) A.Destroy(); }
Foxpaw: It may only apply to AllObjects, since that one operates on non-actors as well, and the notion of an object having been destroyed in a previous iteration doesn't really apply for garbage collection, which is what it was designed for. I'm using AllObjects because it's being called from a non-actor class, and only actors can use the AllActors, DynamicActors, etc. iterators. As far as I can tell only AllObjects is valid for use in classes not derived from actor.
Wormbo: Avoid AllObjects when AllActors or DynamicActors gives you the same results. Also when UsingAllObjects, make sure the actor you try to Destroy() isn't already destroyed (i.e. bDeleteMe should not yet be True).
Foxpaw: Well, objects not derived from Actor can't use any iterators except AllObjects, so my options are limited. Plus, since it's in the GUI, I don't really have to worry much about framerate.
Dma: Actually, you can use any iterator even in a static function in a non-actor class by doing something like this:
static final function Test() { local PlayerController PC; foreach class'Object'.AllObjects(class'PlayerController', PC) { foreach PC.AllActors(...) { // ... } return; } }
EricBlade: Am I correct in my findings, that say you have a couple of actors in the world, A and B, and if you do a RadiusActors, or for that matter, any other Radius based iterator from A.location, if B.location is not within that radius, even if B collides within that radius, you will not find B in that iterator? Example: Created a test map with a pane of glass, that was absolutely monstrously oversized from it's original dimensions, using DrawScale to make it somewhere around 20X it's original size. I could hit it anywhere on it's plane with a hitscan weapon, and the glass would shatter. However, hitting it with a projectile with a very small DamageRadius, no matter the amount of damage, would not cause the glass to shatter, unless it hit very near the middle. (the shattering is done in TakeDamage() .. and logging from the Projectile showed that the VisibleCollidingActors() iterator used in Actor::HurtRadius() was not actually hitting the glass, unless it was originating from very near it's center location point. I solved this problem by simply making projectiles do damage to what they collide with, before they do radius damage, but it seems like a pretty serious flaw for using a radius iterator for any purpose.
SuperApe: My suspicion is that RadiusActors is not collision based but works off of the object's center. You're huge pane of glass probably has the center in the middle, while you may have shot farther away. The note in the code mentions that RadiusActors is slow, like AllActors. This leads me to believe that it's simply a check of AllActors for those who's Location is within the Radius of the actor in question. IOW, something like
if ( VSize( Location - Actor.Location ) =< Radius )
.
EricBlade: Exactly. Now, since that's fairly useless in a world where things take up more than one point of space, what can be done to properly get all actors that have a collision within the radius? Do a CollidingActors, with a ludicrous max radius, and then do a trace from all found actors to the source, to see if they collide within the actual radius we want to affect? Actually, that brings up the problem of what to do if you have a room full of garbage-can sized objects too.. obviously an explosion would affect all of them, within it's blast range, but using Visible check from A to B didn't find most of them.
SuperApe: Just a guess, but couldn't you use another object just to detect damage in that area, like a volume?
EricBlade: eh, when you can fix projectile explosions the right way, why use a hacky way? :D Next question .. what are requirements to get a hit on TouchingActors() ? I've tried it with a couple different things, and have had results that were not at all useful. First, with a projectile, I set it's collisionradius to the damageradius, and then tried to use TouchingActors() to handle the blowing up, but got nothing operated on. Did the same with a Fire, and got nothing :(
SuperApe: (briefly) Do it however you want. I just suggest that mappers can use the stock tools and methods available to get the job done. I'm pretty sure there's more than one breaking glass tut on this site alone. And IIRC, they do use different actors to detect the collision.
Wormbo: The best way to handle a really small explosion radius is to look for actors in a much larger radius such that any actor possibly touching the real radius is included. Then you only have to do a very simple distance check from the center of the explosion to the actor's collision cylinder. I came up with the following code for a remake of the Descent 2 Gauss Cannon: (this is an InstantFire subclass)
function ExtendedHurtRadius(vector HitLocation, vector AimDir, Actor HitActor) { local Actor Victims; local float damageScale, dist, damageAmount; local vector dir; DamageAmount = RandRange(DamageMin, DamageMax) * DamageAtten; foreach Weapon.VisibleCollidingActors(class'Actor', Victims, DamageRadius + 200, HitLocation) { // don't let blast damage affect fluid - VisibleCollisingActors doesn't really work for them - jag if (Victims != self && Victims.Role == ROLE_Authority && !Victims.IsA('FluidSurfaceInfo')) { dist = DistToCylinder(Victims.Location - HitLocation, Victims.CollisionHeight, Victims.CollisionRadius); if ( dist > DamageRadius ) continue; dir = Normal(Victims.Location - HitLocation); if (Victims == HitActor) dir = Normal(dir + AimDir); damageScale = 1 - FMax(0, dist / DamageRadius); Victims.TakeDamage(damageScale * DamageAmount, Instigator, Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dir, damageScale * Momentum * dir, DamageType); if (Vehicle(Victims) != None && Vehicle(Victims).Health > 0) Vehicle(Victims).DriverRadiusDamage(DamageAmount, DamageRadius, Instigator.Controller, DamageType, Momentum, HitLocation); } } } /** Calculates a point's distance to a cylinder. */ static function float DistToCylinder(vector CenterDist, float HalfHeight, float Radius) { CenterDist.X = VSize(vect(1,1,0) * CenterDist) - Radius; if (CenterDist.X < 0) CenterDist.X = 0; CenterDist.Y = 0; if (CenterDist.Z < 0) CenterDist.Z *= -1; CenterDist.Z -= HalfHeight; if (CenterDist.Z < 0) CenterDist.Z = 0; return VSize(CenterDist); }
Iterator Timing Comments
Xian: I wanted to start a small discussion about the timing on these... as I was trying some experimental stuff on UE1 I tried timing it in UE2. This is the code and log (stripped of the boring stuff) from the map Flux2 with around 9-10 bots:
function PostBeginPlay () { Test1(); Test2(); Test3(); Test4(); Test5(); Test6(); } function Test1 () { local Actor Actors; StopWatch(False); foreach VisibleCollidingActors (Class 'Actor', Actors, 50000, Location, False) { Test(Actors); Log("Execute 1"); } Log("FINISHED !!!!!!!!!!!! TEST 1"); StopWatch(True); } function Test2 () { local Actor Actors; StopWatch(False); foreach VisibleCollidingActors (Class 'Actor', Actors, 50000, Location, True) { Test(Actors); Log("Execute 2"); } Log("FINISHED !!!!!!!!!!!! TEST 2"); StopWatch(True); } function Test3 () { local Actor Actors; StopWatch(False); foreach CollidingActors (Class 'Actor', Actors, 50000, Location) { Test(Actors); Log("Execute 3"); } Log("FINISHED !!!!!!!!!!!! TEST 3"); StopWatch(True); } function Test4 () { local Actor Actor; local Actor Actors[512]; local int i, j; -- i; StopWatch(False); foreach AllActors (Class 'Actor', Actor) { if (!Actor.bHidden && !Actor.bStatic && Actor.bCollideActors && Actor.bCollideWorld && (Actor.CollisionHeight > 2) && (Actor.CollisionRadius > 2) && (Abs(VSize(Actor.Location - Location)) <= 50000) && FastTrace(Actor.Location,PlayerPawn.Location)) { Actors[++ i] = Actor; Log("Save 4"); } Log("Log 4"); } ++ i; for (j = 0; j < i; ++ j) { Test(Actors[j]); Log("Execute 4"); } Log("FINISHED !!!!!!!!!!!! TEST 4"); StopWatch(True); } function Test5 () { local Actor Actor; local Actor Actors[512]; local int i, j; -- i; StopWatch(False); foreach VisibleCollidingActors (Class 'Actor', Actor, 50000, Location, True) { if (!Actor.bStatic && Actor.bCollideActors && Actor.bCollideWorld && (Actor.CollisionHeight > 2) && (Actor.CollisionRadius > 2)) { Actors[++ i] = Actor; Log("Save 5"); } Log("Log 5"); } ++ i; for (j = 0; j < i; ++ j) { Test(Actors[j]); Log("Execute 5"); } Log("FINISHED !!!!!!!!!!!! TEST 5"); StopWatch(True); } function Test6 () { local Actor Actor; StopWatch(False); foreach VisibleCollidingActors (Class 'Actor', Actor, 50000, Location, True) { if (!Actor.bStatic && Actor.bCollideActors && Actor.bCollideWorld && (Actor.CollisionHeight > 2) && (Actor.CollisionRadius > 2)) { Test(Actor); Log("Execute 6"); } Log("Log 6"); } Log("FINISHED !!!!!!!!!!!! TEST 6"); StopWatch(True); } function Test (Actor Actor) { Log("Test()" @ Actor); }
It was spawned near my Controller's location so the Location pointer should be equivalent to mine. Please ignore the ugly code... My code is usually cleaner, but this was just a test so bleh... This is the log (with the irrelevant info removed):
ScriptLog: 22.892699: FINISHED !!!!!!!!!!!! TEST 1 Log: Time=22.902054 ms ScriptLog: 11.660470: FINISHED !!!!!!!!!!!! TEST 2 Log: Time=11.670268 ms ScriptLog: 8.632781: FINISHED !!!!!!!!!!!! TEST 3 Log: Time=8.639348 ms ScriptLog: 7.206910: FINISHED !!!!!!!!!!!! TEST 4 Log: Time=7.212730 ms ScriptLog: 11.221647: FINISHED !!!!!!!!!!!! TEST 5 Log: Time=11.227758 ms ScriptLog: 11.784850: FINISHED !!!!!!!!!!!! TEST 6 Log: Time=11.796611 ms
To my suprise, AllActors was the fastest... the only one that came close was CollidingActors. I tried it with 9000 as Radius as well... The result was that CollidingActors and AllActors were fairly in the same range (with AllActors being faster). While I do know the power of code optimization (as you can see I REALLY filtered things out :) ), I am still surprised that the whole actor hash was faster than the colliding actor hash. I will be honest, this was at the beginning of the match, but still... I was always under the impression that VCL and CL are faster than the rest. Any comments/thoughts ?
Wormbo: Check out the descriptions above:
VCA – "Usually this is much faster than AllActors
because it uses the collision hash instead of the much larger native actor list, but with a very high radius VisibleActors might be more efficient."
CA – "This is slightly faster than RadiusActors with radii at least up to 2000UU and much faster than VisibleActors or VisibleCollidingActors due to the visibility check those iterators have to perform for each actor."
Generally the collision hash should only be used if you want to find actors with collision enabled and located within a small area. The Visible*Actors iterators always perform a FastTrace before transfering control back to the UnrealScript code inside the loop, so they must be considered a lot slower than a compareable CollidingActors or RadiusActors loop with additional filter mechanisms. And last but not least it depends on the class of actors your loop iterates over. The class always is the first thing checked, so it makes a huge difference if you iterate over class'Actor' or e.g. class'Projectile': The latter will ignore all those static mesh actors, pickups, players and so on, dramatically changing the number of actors that have to go through the full check.
And another thing: Try reordering the tests. When benchmarking my own code I often found that the first iteration of the first test took longer than later tests.
Xian: I did check the descs, however considering that all of them checked the same things, the small number of bots/projectiles a.s.o. was relatively small, I was still quite surprised on the result, considering AllActors has (basically) no checks since the others have radius, traces, hidden etc. I will try reordering them and perhaps, play for longer with a SetTimer() on the traces...
Wormbo: I rewrote the iterator descriptions for UT2004 and added a performance clasification. Someone should remove obsolete discussions from this page.