由UnrealScript定義的函式必須放在一個繼承自Object的類別內,UnrealScript並沒有支援非成員函式。Object類別在C++中的對應型別為UObject。由於Object這個名稱容易混淆,所以接下來 皆以UObject稱之。每個UObject都會記得自己所屬類別以及相關成員,包括函式在內。可以用UObject::FindFunction()函式取得對應的UFunction物件,並且用ProcesEvent()函式執行UFunction。
UnrealScript類別的狀態(state)內也可定義函式,會遮蔽同名的非狀態函式。FindFunction()預設會先找目前作用中的狀態是否有同名函式,然後再找父狀態,都沒有才找同名的非狀態函式。如果不想要狀態函式的話可以讓第二個參數為TRUE。
使用ProcessEvent()函式時要注意如果要執行的UFunction有參數或傳回值,第二個參數必須傳入一塊代表參數和傳回值的區塊,用來傳遞給UFunction實際的參數並且傳回執行結果。例如若要在C++執行以下UnrealScript函式:
function int foo(int i);就要準備相應的大小的參數區塊:
struct { INT Parameter; INT Result; } Params={Arg, 0}; ProcessEvent( Function, &Params );FindFunction()也會傳回delegate,可以檢查UFunction的FunctionFlags是否標記為FUNC_Delegate來判斷。delegate不能用ProcessEvent()來執行,而是必須找出delegate屬性,然後傳入ProcessDelegate()來執行。 delegate屬性會記錄實際指派的物件位址和函式名稱,因此可以找出指派的UFunction來執行。
範例
以下程式碼展示如何在C++取得並執行指定的UnrealScript函式:
class FunctionAccess extends Object native; native function InvokeVoidFunction(string Path); native function int InvokeIntFunction(string Path, int Arg); cpptext { UFunction* GetFunction(const FString& Path, UBOOL GlobalOnly=FALSE); void InvokeFunc(UFunction* Function, void* Params); }實作碼:
UFunction* UFunctionAccess::GetFunction(const FString& Path, UBOOL GlobalOnly) { UFunction* Function = NULL; INT DotPos = Path.InStr(TEXT(".")); if( DotPos == INDEX_NONE ) { Function = FindFunction( FName(*Path), GlobalOnly ); } else if(! GlobalOnly) { FName StateName = *Path.Left( DotPos ); FName FunctionName = *Path.Mid( DotPos+1 ); for( UState* State = FindState(StateName); State; State = State->GetSuperState() ) { Function = State->FuncMap.FindRef( FunctionName ); if( Function ) break; } } return Function; } void UFunctionAccess::InvokeFunc(UFunction* Function, void* Params) { if( Function->FunctionFlags & FUNC_Delegate ) { UDelegateProperty* DelegateProperty = FindField<UDelegateProperty>( GetClass(), *FString::Printf(TEXT("__%s__Delegate"), *Function->GetName()) ); if( DelegateProperty ) { FScriptDelegate* ScriptDelegate = (FScriptDelegate*)((BYTE*)this + DelegateProperty->Offset); ProcessDelegate( FunctionGetFName(), ScriptDelegate, Parmas ); } else { ProcessEvent( Function, Params ); } } } void UFunctionAccess::InvokeVoidFunction(const FString& Path) { UFunction* Function = GetFunction( Path ); if( Function && Function->NumParms == 0 ) { InvokeFunc( Function, NULL ); } } Int UFunctionAccess::InvokeIntFunction(const FString& Path, INT Arg) { struct { INT Parameter; INT Result; } Params={Arg, 0}; UFunction* Function = GetFunction( Path ); if( Function && Function->NumParms == 2 && Function->PropertyLink && Function->PropertyLink->IsA(UIntProperty::StaticClass()) && Function->GetReturnProperty() && Function->GetReturnProperty()->IsA(UIntProperty::StaticClass()) ) { checkSlow( Function->ParmsSize == sizeof(Params) ); InvokeFunc( Function, &Params ); } return Params.Result; }以下類別包括幾個測試用的函式:
class Test extends FunctionAccess; function VoidFunction() { `log("global"@GetFuncName()); } function int IntFunction(int i) { `log("global"@GetFuncName()); return i; } delegate VoidDelegate(); delegate int IntDelegate(int i); state MyState { function VoidFunction() { `log("MyState."$GetFuncName()); } function int IntFunction(int i) { `log("MyState."$GetFuncName()); return i; } function int StateOnlyFunction(int i) { `log("MyState."$GetFuncName()); return i; } }把測試案例寫在CheatManager裡方便執行:
class MyCheatManager extends CheatManager; exec function InvokeVoidFunction() { local Test T; T = new class'Test'; T.InvokeVoidFunction("VoidFunction"); T.InvokeVoidFunction("MyState.VoidFunction"); } exec function InvokeIntFunction(int i) { local Test T; local int r; T = new class'Test'; r = T.InvokeIntFunction("IntFunction", i); `log("result="$r); r = T.InvokeIntFunction("MyState.IntFunction", i); `log("result="$r); } exec function InvokeStateOnlyFunction(int i) { local Test T; local int r; T = new class'Test'; r = T.InvokeIntFunction("StateOnlyFunction", i); `log("result="$r); r = T.InvokeIntFunction("MyState.StateOnlyFunction", i); `log("result="$r); }
沒有留言:
張貼留言