2012年6月17日 星期日

自訂UnrealScript迭代子

UnrealScript的迭代子(iterator)提供簡潔的foreach語法循序取得一個陣列中的所有元素。而且它不限只能用於陣列,開發者可以自訂迭代子取出任何列表的元素,即使這個列表實際上並不存在。在Actor.uc裡即可找到許多迭代子的宣告,例如:
native(307) final iterator function TouchingActors( class<Actor> BaseClass, out Actor OutActor );
迭代子宣告在一個原生UnrealScript類別裡,由C++實作,實際上是一個成員函式。要在UnrealScript中使用一個迭代子,需要透過foreach語法。下列程式碼示範如何使用一個迭代子:
function PrintTouchingActors()
{
    local Actor TouchedActor;
    foreach TouchingActors(class'Actor', TouchedActor)
    {
        `log(TouchedActor.Name);
    }
}

範例


以下程式碼示範如何自訂一個可以取得所有類別的迭代子:
class MyIterator extends Object
    native;
   
native iterator static function AllClasses( class BaseClass, out class OutClass );

static function PrintAllClasses( class BaseClass )
{
    local class cls;
    foreach AllClasses( BaseClass, cls )
    {
       `log( cls );
    }
}
編譯過上列UnrealScript後會自動產生以下的C++類別定義:
class UMyIterator : public UObject
{
public:

    DECLARE_FUNCTION(execAllClasses);
    DECLARE_CLASS(UMyIterator,UObject,0,MyGame)
    NO_DEFAULT_CONSTRUCTOR(UMyIterator)
};
其中的execAllClasses()函式正是對應到先前在UnrealScript宣告的迭代子。DECLARE_FUNCTION 巨集宣告的函式參數都一樣,第一個參數是執行UnrealScript用的堆疊,可由此取得運算元,第二個參數是存放傳回值位址的void指標。以下是該 函式的實作碼:
void UMyIterator::execAllClasses( FFrame& Stack, RESULT_DECL )
{
    P_GET_OBJECT(UClass, BaseClass);
    P_GET_OBJECT_REF(UClass, OutClass);
    P_FINISH;
   
    TObjectIterator<UClass> It;
   
    PRE_ITERATOR
       OutClass = NULL;
       // Fetch a valid output.
       while( It )
       {
           UClass* CurrentClass = *It;
           ++It;
           
           if( CurrentClass->IsChildOf(BaseClass) )
           {
               OutClass = CurrentClass;
               break;
           }
       }
       
       // If there is no output, skip POST_ITERATOR and go to the end of the script block.
       if(! OutClass)
       {
           Stack.Code = &Stack.Node->Script(wEndOffset + 1);
           break;
       }
    POST_ITERATOR
}
其中的PRE_ITERATOR和POST_ITERATOR巨集其實是一個do while迴圈,必須在POST_ITERATOR前取得每次迭代的輸出物件並且指派到輸出參數(本例中是OutClass),POST_ITERATOR本身會執行一次迴圈內容然後前進到下一次迭代。所以當遇到終止條件時就要逕自跳離巨集所形成的迴圈,以避免執行POST_ITERATOR,並且利用如上面if區塊的程式碼跳離UnrealScript的foreach區塊。

沒有留言:

張貼留言