2014年4月29日 星期二

使用C++實作UnrealScript也認得的原生類別

一般而言,想要使用C++實作類別給UnrealScript使用時,通常會使用UnrealScript撰寫原生類別,這樣會編譯器自動產生對應的C++類別定義,比較方便。但如果想要連一行UnrealScript都不寫,只用C++實作出UnrealScript也認得的類別,其實還是做得到的。

C++的類別可以分為POD(Plain Old Data)以及非POD。POD可以對應到UnrealScript的struct;而非POD的類別則要對應到UnrealScript的class,也就是必須繼承UObject才行。這種類別稱為固有類別(Intrinsic Class)。

固有類別比起一般的C++類別多了一些要注意的事項:
  • 必須直接或間接繼承UObject
  • 要在類別定義內使用DECLARE_CLASS_INTRINSIC巨集
  • 若有想要在UnrealScript中使用的欄位,要在成員函式StaticConstructor註冊。
  • 若有參考到UnrealScript物件的欄位,必需使用EmitObjectReference函式或AddReferencedObjects函式註冊。
  • 可在成員函式InitializeIntrinsicPropertyValues給定欄位初值。
  • 在AUTO_INITIALIZE_REGISTRANTS_XXX使用StaticClass()註冊,XXX替換成實際的Script Package名稱。

固有類別註冊過的欄位,不僅在UnrealScript中可以使用,也可以在PropertyWindow中存取。

範例


以下程式碼示範如何定義固有類別:
class FMyPOD
{
public:

    UBOOL MyBool;
    FName MyName;
};
上面的C++ POD類別如果要在UnrealScript中當作struct使用,就需要產生對應的UScriptStruct才行:
UScriptStruct* MakeMyPODStruct(UStruct* Outer, TCHAR* Name)
{
    UScriptStruct* Struct = new(Outer,Name,RF_Public|RF_Transient|RF_Native) UScriptStruct(NULL);
    INT StructPropertyOffset = 0;
    new(Struct,TEXT("MyBool"),RF_Public) UBoolProperty(EC_CppProperty,StructPropertyOffset,TEXT(""), CPF_Edit | CPF_Native);
    StructPropertyOffset += sizeof(UBOOL);
    new(Struct,TEXT("MyName"),RF_Public) UNameProperty(EC_CppProperty,StructPropertyOffset,TEXT(""), CPF_Edit | CPF_Native);
    StructPropertyOffset += sizeof(FName);
    return Struct;
}
然而UScriptStruct需要放在一個UnrealScript類別內,所以在此就撰寫一個固有類別來存放它,順便也示範如何定義一個固有類別:
class UMyIntrinsicClass : public UObject
{
    DECLARE_CLASS_INTRINSIC(UMyIntrinsicClass,UObject,CLASS_CollapseCategories|0,UDKGame);

public:

    INT     MyInt;
    FLOAT   MyFloat;
    FString MyString;
    FMyPOD  MyPOD;
    
    void StaticConstructor();
    void InitializeIntrinsicPropertyValues();
};
主要是在StaticConstructor()裡針對想要在UnrealScript中使用的欄位產生對應的UProperty,如果並不想被UnrealScript處理的話是不需要特別註冊。
IMPLEMENT_CLASS(UMyIntrinsicClass);

void UMyIntrinsicClass::StaticConstructor()
{
    UProperty* MyIntProp = new(GetClass(),TEXT("MyInt"), RF_Public)         UIntProperty( CPP_PROPERTY(MyInt), TEXT(""), CPF_Edit );
    UProperty* MyFloatProp = new(GetClass(),TEXT("MyFloat"), RF_Public)     UFloatProperty( CPP_PROPERTY(MyFloat), TEXT(""), CPF_Edit );
    UProperty* MyStringProp = new(GetClass(),TEXT("MyString"), RF_Public)   UFloatProperty( CPP_PROPERTY(MyString), TEXT(""), CPF_Edit );
    
    UScriptStruct* MyStruct = MakeMyPODStruct(GetClass(), TEXT("MyPOD"));
    
    MyStruct->SetPropertiesSize(sizeof(FMyPOD));
    MyStruct->AllocateStructDefaults();
    FArchive ArDummy0;
    MyStruct->Link(ArDummy0,0);
    
    UProperty* MyStuctProp = new(GetClass(),TEXT("MyStuct"),RF_Public)      UStructProperty(CPP_PROPERTY(MyPOD),TEXT(""),CPF_Edit,MyStruct);
}

沒有留言:

張貼留言