由UnrealScript定義的類別必須繼承自Object,它在C++中的對應型別為UObject。由於Object這個名稱容易混淆,所以接下來皆以UObject稱之。每個UObject都會記得自己當初建立時的UnrealScript類別,在UnrealScript中可以用Object:Class屬性取得,在C++中可以用UObject::GetClass()函式取得。它在C++中是一個型別為UClass的物件,記錄著對應UnrealScript類別的相關資訊,可以從這裡得知這個類別包括屬性在內的所有成員。
取得屬性
以下程式碼展示如何在一個UObject中取得其所有屬性:
for( UProperty* Property = GetClass()->PropertyLink; Property; Property = Property->PropertyLinkNext ) { ... }UnrealScript屬性在C++中的對應型別稱為UProperty,它是一個基底類別,依據屬性的型別有各自的具象類別:
UnrealScript型別 | 屬性型別 | C++型別 |
bool | UBoolProperty | BITFIELD |
byte | UByteProperty | BYTE |
int | UIntProperty | INT |
float | UFloatProperty | FLOAT |
enum | UByteProperty | BYTE |
name | UNameProperty | FName |
string | UStrProperty | FString |
class | UClassProperty | UClass* |
object | UObjectProperty | UObject* |
component | UComponentProperty | UComponent* |
struct | UStructProperty | native struct的對應型別 |
array | UArrayProperty | FScriptArray |
delegate | UDelegateProperty | FScriptDelegate |
interface | UInterfaceProperty | FScriptInterface |
取得屬性值
UProperty有個成員叫做Offset會記錄該屬性在物件裡的位置,所以如果要取得一個INT屬性,利用下列程式碼即可:
(INT*)((BYTE*)this + Property->Offset)其他的屬性型別也可如法炮製。
不過bool在C++中其實只佔一個位元,需要再用一個BitMask過濾:
(*(BITFIELD*)((BYTE*)this + Property->Offset)) & Property->BitMaskUnrealScript的static array在C++中其實就是內建陣列,它沒有對應的UProperty具象類別,而是將相關資訊記錄在UProperty的成員裡:
(BYTE*)this + Property->Offset + Property->ElementSize * IndexUnrealScript的dynamic array在C++中的對應型別為FScriptArray,UArrayProperty的Inner成員會記錄陣列元素的屬性,當要取得各別元素時可由此得知元素大小:
FScriptArray* Array = (FScriptArray*)((BYTE*)this + ArrayProperty->Offset); (BYTE*) Array->GetData() + ArrayProperty->Inner->ElementSize * IndexUnrealScript的struct即使沒有定義對應的C++型別,還是可以從UStructProperty取得相關資訊來取出所有成員:
BYTE* Base = (BYTE*)this + StructProperty->Offset; for( TFieldIterator<UProperty> It(StructProperty->Struct); It; ++It ) { Base + (*itr)->Offset }
範例
以下程式碼展示如何存取指定路徑的屬性,並且實作存取整數和布林的函式,其他型別都可以以此類推:
class PropertyAccess extends Object native; static native function int GetInt(Object This, string Path, optional int DefaultValue); static native function bool SetInt(Object This, string Path, int NewValue); static native function bool GetBool(Object This, string Path, optional bool DefaultValue); static native function bool SetBool(Object This, string Path, bool NewValue); cpptext { static UProperty* FindProperty( UObject* This, const FString& Path, void*& OutData ); static UProperty* FindPropertyNonAggr( UProperty* Property, BYTE* Base, const FString& Path, void*& OutData ); static UProperty* FindPropertyInArray( UArrayProperty* Outer, BYTE* Base, const FString& Path, void*& OutData ); static UProperty* FindPropertyInStruct( UStructProperty* Outer, BYTE* Base, const FString& Path, void*& OutData ); static UProperty* FindMemberPropertyInStruct( UStructProperty* Outer, BYTE* Base, const FString& Path, void*& OutData ); static FName SplitPropertyPath( const FString& Path, INT& OutIndex, FString& OutRemaining ); }以下是實作碼:
INT UPropertyAccess::GetInt(UObject* This, FString Path, optional INT DefaultValue) { INT Result = DefaultValue; void* Data = NULL; UProperty* Property = FindProperty( This, Path, Data ); if( Property ) { UIntProperty* IntProperty = Cast<UIntProperty>(Property); if( IntProperty ) { Result = *(INT*)Data; } } return Result; } UBOOL UPropertyAccess::SetInt(UObject* This, const FString& Path, INT NewValue) { void* Data = NULL; UProperty* Property = FindProperty( This, Path, Data ); if( Property ) { UIntProperty* IntProperty = Cast<UIntProperty>(Property); if( IntProperty ) { *(INT*)Data = NewValue; return TRUE; } } return FALSE; } UBOOL UPropertyAccess::GetBool(UObject* This, FString Path, optional UBOOL DefaultValue) { UBOOL Result = DefaultValue; void* Data = NULL; UProperty* Property = FindProperty( This, Path, Data ); if( Property ) { UBoolProperty* BoolProperty = Cast<UBoolProperty>(Property); if( BoolProperty ) { Result = (*(BITFIELD*)Data & BoolProperty->BitMask) ? TRUE : FALSE; } } return Result; } UBOOL UPropertyAccess::SetBool(UObject* This, const FString& Path, UBOOL NewValue) { void* Data = NULL; UProperty* Property = FindProperty( This, Path, Data ); if( Property ) { UBoolProperty* BoolProperty = Cast<UBoolProperty>(Property); if( BoolProperty ) { *(INT*)Data = NewValue; return TRUE; } } return FALSE; } UProperty* UPropertyAccess::FindProperty( UObject* This, const FString& Path, void*& OutData ) { if( This ) { for( UProperty* Property = This->GetClass()->PropertyLink; Property; Property = Property->PropertyLinkNext ) { if( UArrayProperty* ArrayProperty = Cast<UArrayProperty>(Property) ) { UProperty* Found = FindPropertyInArray( ArrayProperty, (BYTE*)This + Property->Offset, Path, OutData ); if( Found ) return Found; } else if( UStructProperty* StructProperty = Cast<UStructProperty>(Property) ) { UProperty* Found = FindPropertyInStruct( StructProperty, (BYTE*)This + Property->Offset, Path, OutData ); if( Found ) return Found; } else { UProperty* Found = FindPropertyNonAggr( Property, (BYTE*)This + Property->Offset, Path, OutData ); if( Found ) return Found; } } } return NULL; } UProperty* UPropertyAccess::FindPropertyNonAggr( UProperty* Property, BYTE* Base, const FString& Path, void*& OutData ) { FString RemainingPath; INT ElementIndex; FName OuterName = SplitPropertyPath( Path, ElementIndex, RemainingPath ); if( Property->GetFName() == OuterName && RemainingPath.Len() == 0 ) { BYTE* ElementData = Base; if( ElementIndex >= 0 && ElementIndex < Property->ArrayDim ) { // abc[123] ElementData += Property->ElementSize * ElementIndex; } OutData = ElementData; return Property; } return NULL; } UProperty* UPropertyAccess::FindPropertyInArray( UArrayProperty* Outer, BYTE* Base, const FString& Path, void*& OutData ) { FString RemainingPath; INT ElementIndex; FName OuterName = SplitPropertyPath( Path, ElementIndex, RemainingPath ); if( Outer->GetFName() == OuterName ) { if( ElementIndex == INDEX_NONE ) { if( RemainingPath.Len() == 0 ) { OutData = Base; return Outer; } } else { FScriptArray* Array = (FScriptArray*) Base; if( Array->IsValidIndex(ElementIndex) ) { BYTE* ElementData = (BYTE*) Array->GetData() + Outer->Inner->ElementSize * ElementIndex; if( RemainingPath.Len() == 0 ) { OutData = ElementData; return Outer->Inner; } else if( UStructProperty* StructProperty = Cast<UStructProperty>( Outer->Inner ) ) { return FindMemberPropertyInStruct( StructProperty, ElementData, RemainingPath, OutData ); } } } } return NULL; } UProperty* UPropertyAccess::FindPropertyInStruct( UStructProperty* Outer, BYTE* Base, const FString& Path, void*& OutData ) { FString RemainingPath; INT ElementIndex; FName OuterName = SplitPropertyPath( Path, ElementIndex, RemainingPath ); if( Outer->GetFName() == OuterName ) { BYTE* ElementData = Base; if( ElementIndex >= 0 && ElementIndex < Outer->ArrayDim ) { ElementData += Outer->ElementSize * ElementIndex; } if( RemainingPath.Len() == 0 ) { // abc or abc[123] OutData = ElementData; return Outer; } else { // abc.zyz or abc[123].xyz return FindMemberPropertyInStruct( Outer, ElementData, RemainingPath, OutData ); } } return NULL; } UProperty* UPropertyAccess::FindMemberPropertyInStruct( UStructProperty* Outer, BYTE* Base, const FString& Path, void*& OutData ) { for( TFieldIterator<UProperty> itr(Outer->Struct); itr; ++itr ) { UProperty* MemberProperty = *itr; if( UArrayProperty* ArrayProperty = Cast<UArrayProperty>(MemberProperty) ) { UProperty* Found = FindPropertyInArray( ArrayProperty, (BYTE*)Base + MemberProperty->Offset, Path, OutData ); if( Found ) return Found; } else if( UStructProperty* StructProperty = Cast<UStructProperty>(MemberProperty) ) { UProperty* Found = FindPropertyInStruct( StructProperty, (BYTE*)Base + MemberProperty->Offset, Path, OutData ); if( Found ) return Found; } else { UProperty* Found = FindPropertyNonAggr( MemberProperty, (BYTE*)Base + MemberProperty->Offset, Path, OutData ); if( Found ) return Found; } } return NULL; } FName UPropertyAccess::SplitPropertyPath( const FString& Path, INT& OutIndex, FString& OutRemaining ) { FName OuterName; INT DotPos = Path.InStr( TEXT(".") ); INT IndexLeftPos = Path.InStr( TEXT("[") ); INT IndexRightPos = Path.InStr( TEXT("]") ); if( DotPos == INDEX_NONE ) { if( IndexLeftPos != INDEX_NONE && IndexRightPos != INDEX_NONE && IndexLeftPos < IndexRightPos && IndexRightPos == Path.Len() - 1 ) { // abc[123] OuterName = *Path.Left(IndexLeftPos); OutIndex = appAtoi( *Path.Mid(IndexLeftPos+1) ); } else { // abc OuterName = *Path; OutIndex = INDEX_NONE; } } else { if( IndexLeftPos != INDEX_NONE && IndexRightPos != INDEX_NONE && IndexLeftPos < IndexRightPos && IndexRightPos == DotPos - 1 ) { // abc[123].xyz OuterName = *Path.Left(IndexLeftPos); OutIndex = appAtoi( *Path.Mid(IndexLeftPos+1) ); OutRemaining = Path.Mid(DotPos+1); } else { // abc.xyz OuterName = *Path.Left(DotPos); OutIndex = INDEX_NONE; OutRemaining = Path.Mid(DotPos+1); } } return OuterName; }為了縮短範示程式的長度,SplitPropertyPath()並沒有去處理錯誤的輸入路徑。
沒有留言:
張貼留言