Monday, April 15, 2013

Iterating with ForEach in UnrealScript

Before talk about iteration let's imagine an example. Say you built a game that in the levels there are several UTWeaponPickupFactory actors, but only one of them will have a special behavior that will be programmed in UnrealScript.

The UTWeaponPickupFactory class is found in this class hierarchy:



For this example I will place in the level several UTWeaponPickupFactory actors as shown in the image below.



I selected one of the UTWeaponPickupFactory actors, opened the Properties Window (F4 key) and set the "Tag" property with the value SPECIAL_FACTORY. This Tag property is in the Object category as seen in this image.


When starting the game we need to iterate through all the UTWeaponPickupFactory actors until find the one that has the value SPECIAL_FACTORY in the "Tag" property. When find it we will store the object reference of the current UTWeaponPickupFactory actor to be able to handle it in UnrealScript.

That's what this code does.
class TestIteration extends UTGame;

var UTWeaponPickupFactory SpecialFactory;

function StartMatch()
{
    local UTWeaponPickupFactory factory;
   
    Super.StartMatch();
   
    foreach DynamicActors( class'UTWeaponPickupFactory', factory )
    {
       
      if( factory.tag == 'SPECIAL_FACTORY' ) 
      {
        SpecialFactory = factory;
        break;     // Ends the search
      }
    }
    
    Broadcast( self, " Object Name =" @ SpecialFactory.name) ;    
}

To test this code in UDK set the Game Type to "TestIteration". (UDK Menu: View -> WorldProperties -> Game Type

The "foreach" command works with one type of function known as Iterator. These functions have two parameters, the first parameter specifies the type of actor that it should search and the second is a variable that will receive the reference of the current actor in each iteration.

DynamicActors() is the most used iterator function. It searches in the Actors that are not StaticMeshActor. There are several other iterator functions. The main iterators are declared together in the Actor class, just search for "iterator" in the file Actor.uc.

There are other important iterators declared in the WorldInfo class (Actor->Info->ZoneInfo->WorldInfo). To use them you need to put a variable of the type "WorldInfo" in front of the iterator function name. All Actors have a "WorldInfo" variable. Code Sample:
//Extract from GameInfo.uc (Actor->Info->GameInfo)

function StartBots()
{
    local Controller P;

    foreach WorldInfo.AllControllers(class'Controller', P)
    {
        if (P.bIsPlayer && !P.IsA('PlayerController'))
        {
            if (WorldInfo.NetMode == NM_Standalone)
            {
                RestartPlayer(P);
            }
            else
            {
                P.GotoState('Dead','MPStart');
            }
        }
    }
}