由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()並沒有去處理錯誤的輸入路徑。
沒有留言:
張貼留言