本文依據官方文檔翻譯而來,注意官方版本的文檔較舊(UE 4 4.9),新版本的類名已經改變。本文是直接翻譯而來,並沒有做相應改動。
藍圖,像常規的C++類一樣,需要編譯后才能在運行的時候使用。當你在藍圖編輯器中按下編譯按鈕時,那么編譯器就會把藍圖資源的屬性和圖轉換成類。
術語
FkismetCompilerContext
執行編譯工作的類。每一次編譯都會生成一個新的實例。存儲需要編譯類的引用,藍圖等。
FKismetFunctionContext
持有編譯一個函數的信息,比如相關圖的引用,屬性以及生成的UFunction。
FNodeHandlingFunctor
一個用來處理一類節點的幫助類。包含用於注冊引腳連接的函數,並且生成編譯語句。
FKismetCompiledStatement
編譯中的工作單元。編譯器會把節點轉換成一系列的語句,然后編譯器后端會把這些節點翻譯成字節碼操作。
例子:變量賦值、無條件跳轉(goto),調用(call)
FKismetTerm
圖中的一個末端(字面量、常量、或者變量引用)。每一個數據引用接連都會跟它們中的一個連接。為了抓取變量或者中間值等你也可以在NodeHandlingFunctor中來創建你自己的項。
編譯過程
編譯藍圖的一個基本過程如下圖所示:
*僅適用於完全編譯
清理類
類是就地編譯的,這意味着同一個UBlueprintGeneratedClass被清除並且會重復得使用,因此指向這個類的指針不必修正。CleanAndSanitizeClass()把屬性和函數從類中移動到一個臨時包中的垃圾類中,然后清除類中的所有數據。
創建類的屬性
編譯器遍歷藍圖的NewVariables數組以及其它地方(比如構建腳本等)來找到類需要的所有屬性,然后通過函數CreateClassVariablesFromBlueprint()在UClass內創建UProperties。
創建函數列表
編譯器通過處理事件圖表、常規函數圖表來創建函數列表,並且預編譯這些函數。例如為每一個上下文調用PrecompileFunction()函數。
處理事件圖表
事件圖表的處理通過調用CreateAndProcessUberGraph()函數而進行。它把所有的事件圖表拷貝到一個大的圖表中去,這之后節點會被展開。然后為圖表中的每一個事件節點創建一個函數的存根(stub),並且第一個事件節點都會生成一個對應 的FKismetFunctionContext。
處理函數圖表
處理常規函數圖表的過程是通過調用ProcessOneFunctionGraph()函數來完成的,它會復制每一個圖表到一個臨時的圖表里面,在這個圖表里節點被會展開。每一個函數圖表都會創建一個FKismetFunctionContext。
預編譯函數
函數的預編譯是通過調用每一個上下文中的PrecompileFunction()。這個函數會做以下幾步的操作:
- 安排執行並且計算數據依賴。
- 去除那此沒有使用的或者不是一個數據依賴的節點。
- 在每一個存在的節點上運行RegisterNets()節點處理器。
- 為函數中的值創建FKismetTerms。
- 創建UFunction和相關的屬性。
綁定和鏈接類
現在編譯器已經知道類的所有UFunction和UProperty了,現在它可以調用綁定和鏈接類了,這個過程包括創建屬性鏈,設置屬性大小,函數映射等。在這個時候,它實際上只有一個類頭,不包含最終的標記和元數據,也沒有一個類默認對象(CDO)。
編譯函數
接下來需要為剩下的節點生成FKismetCompiledState對象,通過節點處理器的Compile()函數調用AppendStatementForNode()完成。這個函數會為當前編譯的函數創建FKismetTerm對象只要它們只用來局部使用。
完成編譯類
為了完成編譯這個類,在通過一些最終檢查來確保所有事情都做好之前,編譯器會定下來類的標記並且從父類繼承標記和元數據。
后端發出生成的代碼
后端把每個函數上下文中的語句集合轉換成代碼。有兩個可以使用的后端。
- FKismetCompilerVMBackend 把FKCS轉換成UnrealScript 虛擬機的字節碼,然后就把這些字節碼序列化到函數的腳本數組中。
- FKismetCppBackend 產生用於調試的像C++的代碼。
拷貝類默認對象屬性
使用一個特別的函數CopyPropertiesForUnrelatedObjects(),編譯器會把類原來的CDO中的值拷貝到新的CDO。屬性通過標記的序列化拷貝,因此只要名字一致,它們應該會被正確的轉移。CDO的組件會被重新實際化並且在這個階段修正。這個生成類的CDO是權威的(authoritative)。
重新實際化
由於類可能會改變了大小 或者屬性被添加或移除,編譯器需要重新實例化剛編譯的類的所有對應。這個過程使用TObjectIterator來找到這個類的所有實例,生成一個新的然后使用CopyPropertitiesForUnrelatedObjects()函數來把舊的實例拷貝到新的實例里面去。
要想了解更多的細節,查看FBlueprintCompileReinstancer類。