UE4 反射機(jī)制使用(進(jìn)階文章)
反射機(jī)制
? ? ? ?反射(Reflection)是一種編程語言的能力,可以在運(yùn)行時(shí)識(shí)別對(duì)象和類的信息(屬性,方法,繼承關(guān)系等)。如果使用過一些ORM庫的同學(xué)應(yīng)該也比較了解這個(gè)機(jī)制。UE4中大量使用了反射,比如序列化,垃圾回收,藍(lán)圖/C++通信。UE4 的反射系統(tǒng)名為?Unreal Property System,實(shí)質(zhì)上還是一個(gè)MetaData System。最近有個(gè)哥們想要使用 UE 4.20 版本開始提供了python插件做一些事,但是對(duì)于如何在python中調(diào)用C++代碼還有疑惑,所以我稍微研究了一下UE4?PythonScriptPlugin。本文的主要內(nèi)容就是PythonScriptPlugin中python調(diào)用C++代碼的分析。
python調(diào)用C++代碼
? ? ? ?正常將C++代碼導(dǎo)出到python,就是編寫wrapper。比如我們要導(dǎo)出函數(shù)char * hello(char * what)

然后把wrapper添加到module中

再編寫一個(gè)setup.py在python中聲明此module

當(dāng)然也有一些庫,比如SWIG,pybind11,Boost.Python可以簡化上面的過程
? ? ? ?對(duì)于UE4的PythonScriptPlugin,封裝了?PyCFunctionCast
?和?PyCStrCast
?宏,用來向python導(dǎo)出方法和屬性。實(shí)際上確實(shí)有一些模塊,比如數(shù)學(xué)庫,是通過該方法導(dǎo)出的。但是我在PythonScriptPlugin并沒有發(fā)現(xiàn)導(dǎo)出StaticMesh之類UObject的方法,但是在C++中確實(shí)可以調(diào)用。說明這些和Gameplay相關(guān)的代碼導(dǎo)出,必定另有玄妙。
Unreal Property System
? ? ? ?也不賣關(guān)子,導(dǎo)出到python的玄妙,就是這個(gè)Unreal Property System。這個(gè)東西大家應(yīng)該是非常熟悉的,UENUM()
,?UCLASS()
,?USTRUCT()
,?UFUNCTION()
,和?UPROPERTY()
?宏可以將C++代碼導(dǎo)出給藍(lán)圖使用,而PythonScriptPlugin文檔中有這樣的話
The?
unreal
?module exposes nearly everything that is exposed from C++ to Blueprints in your Editor environment. It's not pre-generated; it automatically reflects whatever is available in Blueprints in your Editor. As you enable new plugins in the Unreal Editor, anything those plugins expose to Blueprints also becomes available in Python as well. The same goes for any C++ code that you write in your Project and expose to Blueprints.
? ? ? ?也就是說,PythonScriptPlugin使用了藍(lán)圖相同的代碼導(dǎo)出方法,兩者都是基于反射實(shí)現(xiàn)。對(duì)于任何一個(gè)UClass或UScriptStruct,都可以使用UTypeName::StaticClass()
或FTypeName::StaticStruct()來查詢其類型。而對(duì)一個(gè)UClass或UScriptStruct,可以使用TFieldIterator來查詢其方法和屬性。

TFieldIterator中的模板參數(shù)是一個(gè)UField,其樹形派生關(guān)系如下

? ? ? ?到此為止思路就很清晰了,封裝一個(gè)binding函數(shù)。然后收集所有需要導(dǎo)出的UObject的信息,之后遍歷其UFunction
和
UProperty
,調(diào)用binding函數(shù)完成導(dǎo)出。
PythonScriptPlugin中C++ reflect導(dǎo)出過程分析
?
PythonScriptPlugin中C++ reflect導(dǎo)出過程分析
? ? ? ?在PythonScriptPlugin module初始化的部分,可以發(fā)現(xiàn)如下代碼。

感覺很可疑,所以調(diào)試一下,會(huì)發(fā)現(xiàn)很有意思的東西

ObjectsToProcess應(yīng)該就是要找的東西了,不過為了確認(rèn)還是仔細(xì)查看一下

? ? ? ?可以看到,ObjectsToProcess就是所有需要導(dǎo)出的UField信息了,之后就是調(diào)用函數(shù)GenerateWrappedTypeForObject
?和?GenerateWrappedTypesForReferences 來完成變量的 wrap 并導(dǎo)入到 python module 中。GenerateWrappedTypeForObject 里也可以看到變量 UClass/UStruct 的屬性和方法完成binding的代碼,
這個(gè)過程還是
挺復(fù)雜的
,有興趣的可以自己看一下。
? ?從上面查看變量可以發(fā)現(xiàn),就連插件中的代碼也完成了導(dǎo)出(3765開始的UClass就是PythonScriptPlugin自己的類)。這可以說是一個(gè)相當(dāng)理想的情況。對(duì)于開頭提到的那個(gè)哥們來說,只要正常開發(fā)插件的方式,python就可以調(diào)用,并不需要額外手寫binding代碼。
結(jié)語
? ?通過對(duì)PythonScriptPlugin中,c++將UObject導(dǎo)出給python使用的過程分析,相信大家對(duì)于藍(lán)圖如何調(diào)用C++應(yīng)該有了新的認(rèn)識(shí)。對(duì)于網(wǎng)絡(luò)通信使用xml/json信息spwan actor這種需求有了新的想法。甚至有了自己編寫UE4到lua/ruby/js etc.?binding的打算。希望本文對(duì)大家有所幫助。這篇文章我忽略了一些細(xì)節(jié),如果你感覺到有什么模糊或者錯(cuò)誤的地方請一定提出來,我盡快改正