IT技術互動交(jiao)流平台

重庆快乐十分官网

作(zuo)者︰風(feng)戀(lian)殘(can)雪  來源︰IT165收集  發布日期︰2020-02-21 20:04:52

前言

這(zhe)里,我們(men)打算對虛幻(huan)4 中藍(lan)圖虛擬機的實現(xian)做一(yi)個大概(gai)的講(jiang)解,如(ru)果對其它的腳(jiao)本語言的實現(xian)有比較(jiao)清楚的認識,理解起來會容易很多(duo),我們(men)先會對相關術語進行一(yi)個簡單的介(jie)紹,然後會對藍(lan)圖虛擬機的實現(xian)做一(yi)個講(jiang)解。

術語

編程語言一(yi)hua)惴治wei)編譯語言和解釋型(xing)語言。

編譯型(xing)語言

程序在執行之前需要一(yi)個專(zhuan)門的編譯過程,把程序編譯成(cheng) 為(wei)機器語言的文件,運行時(shi)不(bu)需要重新翻譯,直(zhi)接使用(yong)編譯的結果就行了(liao)。程序執行效率高,依(yi)賴編譯器,跨平台性差些(xie)。如(ru)C、C++、Delphi等.

解釋性語言

編寫的程序不(bu)進行預先編譯,以文本方式存儲程序代碼(ma)。在發布程序時(shi)bao) 雌鵠詞×liao)道編譯工序。但(dan)是(shi),在運行程序的時(shi)候,解釋性語言必(bi)須(xu)先解釋再(zai)運行xiao)/p>

然而關于Java、C#等是(shi)否為(wei)解釋型(xing)語言存在爭議(yi),因為(wei)它們(men)主(zhu)流的實現(xian)並不(bu)是(shi)直(zhi)接解釋執行的,而是(shi)qie)脖嘁氤cheng)字節(jie)碼(ma),然後再(zai)運行在jvm等虛擬機上的。

UE4中藍(lan)圖的實現(xian)更像是(shi)lua的實現(xian)方式,它並不(bu)能獨立運行,而是(shi)作(zuo)為(wei)一(yi)種嵌入宿(su)主(zhu)語言的一(yi)種擴展腳(jiao)本,lua可以直(zhi)接解釋執行,也可以編譯成(cheng)字節(jie)碼(ma)並保(bao)存到磁盤上,下次(ci)調(diao)用(yong)可以直(zhi)接加載(zai)編譯好的字節(jie)碼(ma)執行xiao)/p>

什(shi)麼是(shi)虛擬機

虛擬機最初由波佩(pei)克[a]與戈德堡(bao)定義(yi)為(wei)有效的、獨立的真(zhen)實機器的副本。當前包(bao)括跟(gen)任何真(zhen)實機器無(wu)關的虛擬機。虛擬機根據(ju)它們(men)的nao)擻yong)和與直(zhi)接機器的相關性分為(wei)兩大類。系統虛擬機(如(ru)VirtualBox)提供一(yi)個可以運行xing)暾僮zuo)系統的完整系統平台。相反(fan)的,程序虛擬機(如(ru)Java JVM)為(wei)運行單個計算機程序設(she)計,這(zhe)意它支持單個進程。虛擬機的一(yi)個本質特(te)點是(shi)運行在虛擬機上的軟件被(bei)局限在虛擬機提供的資(zi)源里——它不(bu)能超出虛擬世界。

而這(zhe)里我們(men)主(zhu)要關心的是(shi)程序虛擬機,VM既然被(bei)稱為(wei)'機器',一(yi)hua)閎餃wei)輸入是(shi)滿足某種指令集架構(gou)(instruction set architecture,ISA)的指令序列,中間轉(zhuan)換為(wei)目(mu)標ISA的指令序列並加以執行,輸出為(wei)程序的執行結果的,就是(shi)VM。源與目(mu)標ISA可以是(shi)同一(yi)種,這(zhe)是(shi)所謂same-ISA VM。

分類

虛擬機實現(xian)分為(wei)基于寄(ji)存器的虛擬機和基于棧的虛擬機。

三地(di)址(zhi)指令

a = b + c;

如(ru)果把它變成(cheng)這(zhe)種形式︰

add a, b, c

那看起來就更像機器指令了(liao),對yuan)桑空zhe)種就是(shi)所謂'三地(di)址(zhi)指令'(3-address instruction),一(yi)hua)閾問轎wei)︰

op dest, src1, src2

許多(duo)操作(zuo)都是(shi)二(er)元運算+賦值。三地(di)址(zhi)指令正(zheng)好可以指定兩個源和一(yi)個目(mu)標,能非(fei)常靈活的支持二(er)元操作(zuo)與賦值的組(zu)合(he)。ARM處理器的主(zhu)要指令集就是(shi)三地(di)址(zhi)形式的。

二(er)地(di)址(zhi)指令

a += b;

變成(cheng):

add a, b

這(zhe)就是(shi)所謂'二(er)地(di)址(zhi)指令',一(yi)hua)閾問轎wei)︰

op dest, src

它要支持二(er)元操作(zuo),就只能把其中一(yi)個源同時(shi)也作(zuo)為(wei)目(mu)標。上面的add a, b在執行過後,就會破壞a原有的值,而b的值保(bao)持不(bu)變。x86系列的處理器就是(shi)二(er)地(di)址(zhi)形式的。

一(yi)地(di)址(zhi)指令

顯(xian)然,指令集可以是(shi)任意'n地(di)址(zhi)'的,n屬于自然數。那麼一(yi)地(di)址(zhi)形式的指令集是(shi)怎樣的呢?

想像一(yi)下這(zhe)樣一(yi)組(zu)指令序列︰

add 5

sub 3

這(zhe)只指定了(liao)操作(zuo)的nao)矗 悄mu)標是(shi)什(shi)麼?一(yi)hua) 此擔 zhe)種運算的目(mu)標是(shi)被(bei)稱為(wei)'累加器'(accumulator)的專(zhuan)用(yong)寄(ji)存器,所有運算都靠更新累加器的狀態來完成(cheng)。那麼上面兩條指令用(yong)C來寫就類似︰

C代碼(ma) 收藏(cang)代碼(ma)

acc += 5;

acc -= 3;

只不(bu)過acc是(shi)'隱藏(cang)'的目(mu)標。基于累加器的架構(gou)近來比較(jiao)少見了(liao),在很老的機器上xi)比俟yi)段時(shi)間。

零(ling)地(di)址(zhi)指令

那'n地(di)址(zhi)'的n如(ru)果是(shi)0的話(hua)呢?

看這(zhe)樣一(yi)段Java字節(jie)碼(ma)︰

Java bytecode代碼(ma) 收藏(cang)代碼(ma)

iconst_1

iconst_2

iadd

istore_0

注意那個iadd(表示整型(xing)加法)指令並沒有任何參數。連源都無(wu)法指定了(liao),零(ling)地(di)址(zhi)指令有什(shi)麼用(yong)??

零(ling)地(di)址(zhi)意味著源與目(mu)標都是(shi)qie) 問 涫迪xian)依(yi)賴于一(yi)種常見的數據(ju)結構(gou)——沒錯,就是(shi)棧。上面的iconst_1、iconst_2兩條指令,分zhi)鶼蛞yi)個叫做'求值棧'(evaluation stack,也叫做operand stack'操作(zuo)數棧'或者expression stack'表達式棧')的地(di)方壓入整型(xing)常量(liang)1、2。iadd指令則(ze)從求值棧頂彈出2個值,將(jiang)值相加,然後把結果壓回到棧頂。istore_0指令從求值棧頂彈出一(yi)個值,並將(jiang)值保(bao)存到局部變量(liang)區的第一(yi)個位置(slot 0)。

零(ling)地(di)址(zhi)形式的指令集一(yi)hua)憔褪shi)通過'基于棧的架構(gou)'來實現(xian)的。請一(yi)定要注意,這(zhe)個棧是(shi)指'求值棧',而不(bu)是(shi)與系統調(diao)用(yong)棧(system call stack,或者就叫system stack)。千(qian)萬別弄混(hun)了(liao)。有些(xie)虛擬機把求值棧實現(xian)在系統調(diao)用(yong)棧上,但(dan)兩者概(gai)念上不(bu)是(shi)qie)桓齠 鰲/p>

由于指令的nao)從 mu)標都是(shi)qie) 模 ling)地(di)址(zhi)指令的'密度'可以非(fei)常高——可以用(yong)更少空間放下更多(duo)條指令。因此在空間緊缺的環(huan)境中,零(ling)地(di)址(zhi)指令是(shi)種可取的設(she)計。但(dan)零(ling)地(di)址(zhi)指令要完成(cheng)一(yi)件事情,一(yi)hua)慊岊榷er)地(di)址(zhi)或者三地(di)址(zhi)指令許多(duo)更多(duo)條指令。上面Java字節(jie)碼(ma)做的加法,如(ru)果用(yong)x86指令兩條就能完成(cheng)了(liao)︰

mov eax, 1

add eax, 2

基于棧與基于寄(ji)存器結構(gou)的區別

保(bao)存臨時(shi)值的位置不(bu)同
  • 基于棧︰hang)jiang)臨時(shi)值保(bao)存在求值棧上。 基于寄(ji)存器︰hang)jiang)臨時(shi)值保(bao)存在寄(ji)存器中xiao)代碼(ma)所佔體積不(bu)同
    • 基于棧︰代碼(ma)緊湊,體積小(xiao),但(dan)所需要的代碼(ma)條件多(duo) 基于寄(ji)存器︰代碼(ma)相對大些(xie),但(dan)所需要的代碼(ma)條件少

      基于棧中的'棧'指的是(shi)'求值棧',JVM中'求值棧'被(bei)稱為(wei)'操作(zuo)數棧'。

      棧幀(zheng)

      棧幀(zheng)也叫過程活動記錄,是(shi)編譯器用(yong)來實現(xian)過程/函數調(diao)用(yong)的一(yi)種數據(ju)結構(gou)。從邏(luo)輯上講(jiang),棧幀(zheng)就是(shi)qie)桓齪 蔥械幕huan)境︰函數參數、函數的局部變量(liang)、函數執行xing)旰蠓fan)回到哪里等等。

      藍(lan)圖虛擬機的實現(xian)

      前面我們(men)qie)丫 虻?媒jie)紹了(liao)虛擬機的相關術語,接下來ci)頤men)來具體講(jiang)解下虛幻(huan)4中藍(lan)圖虛擬機的實現(xian)。

      字節(jie)碼(ma)

      虛擬機的字節(jie)碼(ma)在Script.h文件中,這(zhe)里我們(men)把它全部列出來,因為(wei)是(shi)專(zhuan)用(yong)的腳(jiao)本語言,所以它里面會有一(yi)些(xie)特(te)殊的字節(jie)碼(ma),如(ru)代理相關的代碼(ma)(EX_BindDelegate、EX_AddMulticastDelegate),當然常用(yong)的語句也是(shi)有的,比如(ru)賦值、無(wu)條件跳(tiao)轉(zhuan)指令、條件跳(tiao)轉(zhuan)指令、switch等。

       1 // 2 3 // Evaluatable expression item types. 4 5 // 6 7 enum EExprToken 8 9 { 10 11 // Variable references. 12 13 EX_LocalVariable = 0x00, // A local variable. 14 15 EX_InstanceVariable = 0x01, // An object variable. 16 17 EX_DefaultVariable = 0x02, // Default variable for a class context. 18 19 //   = 0x03, 20 21 EX_Return  = 0x04, // Return from function. 22 23 //   = 0x05, 24 25 EX_Jump   = 0x06, // Goto a local address in code. 26 27 EX_JumpIfNot  = 0x07, // Goto if not expression. 28 29 //   = 0x08, 30 31 EX_Assert  = 0x09, // Assertion. 32 33 //   = 0x0A, 34 35 EX_Nothing  = 0x0B, // No operation. 36 37 //   = 0x0C, 38 39 //   = 0x0D, 40 41 //   = 0x0E, 42 43 EX_Let   = 0x0F, // Assign an arbitrary size value to a variable. 44 45 //   = 0x10, 46 47 //   = 0x11, 48 49 EX_ClassContext  = 0x12, // Class default object context. 50 51 EX_MetaCast = 0x13, // Metaclass cast. 52 53 EX_LetBool  = 0x14, // Let boolean variable. 54 55 EX_EndParmValue  = 0x15, // end of default value for optional function parameter 56 57 EX_EndFunctionParms = 0x16, // End of function call parameters. 58 59 EX_Self   = 0x17, // Self object. 60 61 EX_Skip   = 0x18, // Skippable expression. 62 63 EX_Context  = 0x19, // Call a function through an object context. 64 65 EX_Context_FailSilent = 0x1A, // Call a function through an object context (can fail silently if the context is NULL; only generated for functions that don't have output or return values). 66 67 EX_VirtualFunction = 0x1B, // A function call with parameters. 68 69 EX_FinalFunction = 0x1C, // A prebound function call with parameters. 70 71 EX_IntConst  = 0x1D, // Int constant. 72 73 EX_FloatConst  = 0x1E, // Floating point constant. 74 75 EX_StringConst  = 0x1F, // String constant. 76 77 EX_ObjectConst  = 0x20, // An object constant. 78 79 EX_NameConst  = 0x21, // A name constant. 80 81 EX_RotationConst = 0x22, // A rotation constant. 82 83 EX_VectorConst  = 0x23, // A vector constant. 84 85 EX_ByteConst  = 0x24, // A byte constant. 86 87 EX_IntZero  = 0x25, // Zero. 88 89 EX_IntOne  = 0x26, // One. 90 91 EX_True   = 0x27, // Bool True. 92 93 EX_False  = 0x28, // Bool False. 94 95 EX_TextConst  = 0x29, // FText constant 96 97 EX_NoObject  = 0x2A, // NoObject. 98 99 EX_TransformConst = 0x2B, // A transform constant100 101 EX_IntConstByte  = 0x2C, // Int constant that requires 1 byte.102 103 EX_NoInterface  = 0x2D, // A null interface (similar to EX_NoObject, but for interfaces)104 105 EX_DynamicCast  = 0x2E, // Safe dynamic class casting.106 107 EX_StructConst  = 0x2F, // An arbitrary UStruct constant108 109 EX_EndStructConst = 0x30, // End of UStruct constant110 111 EX_SetArray  = 0x31, // Set the value of arbitrary array112 113 EX_EndArray  = 0x32,114 115 //   = 0x33,116 117 EX_UnicodeStringConst = 0x34, // Unicode string constant.118 119 EX_Int64Const  = 0x35, // 64-bit integer constant.120 121 EX_UInt64Const  = 0x36, // 64-bit unsigned integer constant.122 123 //   = 0x37,124 125 EX_PrimitiveCast = 0x38, // A casting operator for primitives which reads the type as the subsequent byte126 127 //   = 0x39,128 129 //   = 0x3A,130 131 //   = 0x3B,132 133 //   = 0x3C,134 135 //   = 0x3D,136 137 //   = 0x3E,138 139 //   = 0x3F,140 141 //   = 0x40,142 143 //   = 0x41,144 145 EX_StructMemberContext = 0x42, // Context expression to address a property within a struct146 147 EX_LetMulticastDelegate = 0x43, // Assignment to a multi-cast delegate148 149 EX_LetDelegate  = 0x44, // Assignment to a delegate150 151 //   = 0x45,152 153 //   = 0x46, // CST_ObjectToInterface154 155 //   = 0x47, // CST_ObjectToBool156 157 EX_LocalOutVariable = 0x48, // local out (pass by reference) function parameter158 159 //   = 0x49, // CST_InterfaceToBool160 161 EX_DeprecatedOp4A = 0x4A,162 163 EX_InstanceDelegate = 0x4B, // const reference to a delegate or normal function object164 165 EX_PushExecutionFlow = 0x4C, // push an address on to the execution flow stack for future execution when a EX_PopExecutionFlow is executed. Execution continues on normally and doesn't change to the pushed address.166 167 EX_PopExecutionFlow = 0x4D, // continue execution at the last address previously pushed onto the execution flow stack.168 169 EX_ComputedJump  = 0x4E, // Goto a local address in code, specified by an integer value.170 171 EX_PopExecutionFlowIfNot = 0x4F, // continue execution at the last address previously pushed onto the execution flow stack, if the condition is not true.172 173 EX_Breakpoint  = 0x50, // Breakpoint. Only observed in the editor, otherwise it behaves like EX_Nothing.174 175 EX_InterfaceContext = 0x51, // Call a function through a native interface variable176 177 EX_ObjToInterfaceCast = 0x52, // Converting an object reference to native interface variable178 179 EX_EndOfScript  = 0x53, // Last byte in script code180 181 EX_CrossInterfaceCast = 0x54, // Converting an interface variable reference to native interface variable182 183 EX_InterfaceToObjCast = 0x55, // Converting an interface variable reference to an object184 185 //   = 0x56,186 187 //   = 0x57,188 189 //   = 0x58,190 191 //   = 0x59,192 193 EX_WireTracepoint = 0x5A, // Trace point. Only observed in the editor, otherwise it behaves like EX_Nothing.194 195 EX_SkipOffsetConst = 0x5B, // A CodeSizeSkipOffset constant196 197 EX_AddMulticastDelegate = 0x5C, // Adds a delegate to a multicast delegate's targets198 199 EX_ClearMulticastDelegate = 0x5D, // Clears all delegates in a multicast target200 201 EX_Tracepoint  = 0x5E, // Trace point. Only observed in the editor, otherwise it behaves like EX_Nothing.202 203 EX_LetObj  = 0x5F, // assign to any object ref pointer204 205 EX_LetWeakObjPtr = 0x60, // assign to a weak object pointer206 207 EX_BindDelegate  = 0x61, // bind object and name to delegate208 209 EX_RemoveMulticastDelegate = 0x62, // Remove a delegate from a multicast delegate's targets210 211 EX_CallMulticastDelegate = 0x63, // Call multicast delegate212 213 EX_LetValueOnPersistentFrame = 0x64,214 215 EX_ArrayConst  = 0x65,216 217 EX_EndArrayConst = 0x66,218 219 EX_AssetConst  = 0x67,220 221 EX_CallMath  = 0x68, // static pure function from on local call space222 223 EX_SwitchValue  = 0x69,224 225 EX_InstrumentationEvent = 0x6A, // Instrumentation event226 227 EX_ArrayGetByRef = 0x6B,228 229 EX_Max   = 0x100,230 231 };

      棧幀(zheng)

      在Stack.h中我們(men)可以找ye)Frame的定義(yi),雖然它定義(yi)的是(shi)qie)桓黿 gou)體,但(dan)是(shi)執行當前代碼(ma)的邏(luo)輯是(shi)封裝在這(zhe)里面的。下面讓我們(men)看一(yi)下它的數據(ju)成(cheng)員bao)/p>

       1 // Variables. 2 3 UFunction* Node; 4 5 UObject* Object; 6 7 uint8* Code; 8 9 uint8* Locals;10 11 12 13 UProperty* MostRecentProperty;14 15 uint8* MostRecentPropertyAddress;16 17 18 19 /** The execution flow stack for compiled Kismet code */20 21 FlowStackType FlowStack;22 23 24 25 /** Previous frame on the stack */26 27 FFrame* PreviousFrame;28 29 30 31 /** contains information on any out parameters */32 33 FOutParmRec* OutParms;34 35 36 37 /** If a class is compiled in then this is set to the property chain for compiled-in functions. In that case, we follow the links to setup the args instead of executing by code. */38 39 UField* PropertyChainForCompiledIn;40 41 42 43 /** Currently executed native function */44 45 UFunction* CurrentNativeFunction;46 47 48 49 bool bArrayContextFailed;

      我們(men)可以看到,它里面保(bao)存了(liao)當前執行的腳(jiao)本函數,執行該腳(jiao)本的UObject,當前代碼(ma)的執行位置,局部變量(liang),上一(yi)個棧幀(zheng),調(diao)用(yong)返(fan)回的參數(不(bu)是(shi)返(fan)回值),當前執行的nao) sheng)函數等。而調(diao)用(yong)函數的返(fan)回值是(shi)放在了(liao)函數調(diao)用(yong)之前保(bao)存,調(diao)用(yong)結束後再(zai)恢復。大致如(ru)下所示︰

      1 uint8 * SaveCode = Stack.Code;2 3 // Call function4 5 ….6 7 Stack.Code = SaveCode

      下面我們(men)列出FFrame中跟(gen)執行相關的重要函數︰

       1 // Functions. 2 3 COREUOBJECT_API void Step( UObject* Context, RESULT_DECL ); 4 5 6 7 /** Replacement for Step that uses an explicitly specified property to unpack arguments **/ 8 9 COREUOBJECT_API void StepExplicitProperty(void*const Result, UProperty* Property); 10 11 12 13 /** Replacement for Step that checks the for byte code, and if none exists, then PropertyChainForCompiledIn is used. Also, makes an effort to verify that the params are in the correct order and the types are compatible. **/ 14 15 template<class TProperty> 16 17 FORCEINLINE_DEBUGGABLE void StepCompiledIn(void*const Result); 18 19 20 21 /** Replacement for Step that checks the for byte code, and if none exists, then PropertyChainForCompiledIn is used. Also, makes an effort to verify that the params are in the correct order and the types are compatible. **/ 22 23 template<class TProperty, typename TNativeType> 24 25 FORCEINLINE_DEBUGGABLE TNativeType& StepCompiledInRef(void*const TemporaryBuffer); 26 27 28 29 COREUOBJECT_API virtual void Serialize( const TCHAR* V, ELogVerbosity::Type Verbosity, const class FName& Category ) override; 30 31 32 33 COREUOBJECT_API static void KismetExecutionMessage(const TCHAR* Message, ELogVerbosity::Type Verbosity, FName WarningId = FName()); 34 35 36 37 /** Returns the current script op code */ 38 39 const uint8 PeekCode() const { return *Code; } 40 41 42 43 /** Skips over the number of op codes specified by NumOps */ 44 45 void SkipCode(const int32 NumOps) { Code += NumOps; } 46 47 48 49 template<typename TNumericType> 50 51 TNumericType ReadInt(); 52 53 float ReadFloat(); 54 55 FName ReadName(); 56 57 UObject* ReadObject(); 58 59 int32 ReadWord(); 60 61 UProperty* ReadProperty(); 62 63 64 65 /** May return null */ 66 67 UProperty* ReadPropertyUnchecked(); 68 69 70 71 /** 72 73 * Reads a value from the bytestream, which represents the number of bytes to advance 74 75 * the code pointer for certain expressions. 76 77 * 78 79 * @param ExpressionField receives a pointer to the field representing the expression; used by various execs 80 81 *    to drive VM logic 82 83 */ 84 85 CodeSkipSizeType ReadCodeSkipCount(); 86 87 88 89 /** 90 91 * Reads a value from the bytestream which represents the number of bytes that should be zero'd out if a NULL context 92 93 * is encountered 94 95 * 96 97 * @param ExpressionField receives a pointer to the field representing the expression; used by various execs 98 99 *    to drive VM logic100 101 */102 103 VariableSizeType ReadVariableSize(UProperty** ExpressionField);

      像ReadInt()、ReadFloat()、ReadObject()等這(zhe)些(xie)函數,我們(men)看到它的名字就知道lang)shi)做什(shi)麼的,就是(shi)從代碼(ma)中讀取相應的int、float、UObject等。這(zhe)里我們(men)主(zhu)要說下Step()函數,它的代碼(ma)如(ru)下所示︰

      1 void FFrame::Step(UObject *Context, RESULT_DECL)2 3 {4 5 int32 B = *Code++;6 7 (Context->*GNatives[B])(*this,RESULT_PARAM);8 9 }

      可以看到,它的主(zhu)要作(zuo)用(yong)就是(shi)取出指令,然後在原生(sheng)函數數組(zu)中找ye)蕉雜Φ暮 ?蔥小(xiao)/p>

      字節(jie)碼(ma)對應函數

      前面我們(men)列出了(liao)所有的虛擬機的所有字節(jie)碼(ma),那麼對應每個字節(jie)碼(ma)具體執行部分的代碼(ma)在哪里呢,具體可以到ScriptCore.cpp中查找定義(yi),我們(men)可以看到每個字節(jie)碼(ma)對應的nao) sheng)函數都在GNatives和GCasts里面︰

      它們(men)的聲明如(ru)下︰

      1 /** The type of a native function callable by script */2 3 typedef void (UObject::*Native)( FFrame& TheStack, RESULT_DECL );4 5 Native GCasts[];6 7 Native GNatives[EX_Max];

      這(zhe)樣它都會對每一(yi)個原生(sheng)函數調(diao)用(yong)一(yi)下注冊(ce)方法,通過IMPLEMENT_VM_FUNCTION和IMPLEMENT_CAST_FUNCTION宏實現(xian)。

      具體代碼(ma)如(ru)下圖所示︰

       1 #define IMPLEMENT_FUNCTION(cls,func) 2 3 static FNativeFunctionRegistrar cls##func##Registar(cls::StaticClass(),#func,(Native)&cls::func); 4 5 6 7 #define IMPLEMENT_CAST_FUNCTION(cls, CastIndex, func) 8 9 IMPLEMENT_FUNCTION(cls, func); 10 11 static uint8 cls##func##CastTemp = GRegisterCast( CastIndex, (Native)&cls::func );12 13 14 15 #define IMPLEMENT_VM_FUNCTION(BytecodeIndex, func) 16 17 IMPLEMENT_FUNCTION(UObject, func) 18 19 static uint8 UObject##func##BytecodeTemp = GRegisterNative( BytecodeIndex, (Native)&UObject::func );

      可以看到,它是(shi)定義(yi)了(liao)一(yi)個全局靜(jing)態對象,這(zhe)樣就會在程序的main函數執行前就已經把函數放在數組(zu)中對應的位置了(liao),這(zhe)樣在虛擬機執行時(shi)就可以直(zhi)接調(diao)用(yong)到對應的nao) sheng)函數了(liao)。

      執行流程

      我們(men)前面講(jiang)藍(lan)圖的時(shi)候講(jiang)過藍(lan)圖如(ru)何跟(gen)C++交(jiao)互,包(bao)括藍(lan)圖調(diao)用(yong)C++代碼(ma),以及從C++代碼(ma)調(diao)用(yong)到藍(lan)圖里面去。

      C++調(diao)用(yong)藍(lan)圖函數

       1 UFUNCTION(BlueprintImplementableEvent, Category = 'AReflectionStudyGameMode') 2 3 void ImplementableFuncTest(); 4 5 6 7 void AReflectionStudyGameMode::ImplementableFuncTest() 8 9 {10 11 ProcessEvent(FindFunctionChecked(REFLECTIONSTUDY_ImplementableFuncTest),NULL);12 13 }

      因為(wei)我們(men)這(zhe)個函數沒有參數,所有ProcessEvent中傳了(liao)一(yi)個NULL,如(ru)果是(shi)有參數和返(fan)回值等,那麼UHT會自動生(sheng)成(cheng)一(yi)個結構(gou)體用(yong)于存儲參數和返(fan)回值等,這(zhe)樣當在C++里面調(diao)用(yong)函數時(shi)bao) 突崛?EFLECTIONSTUDY_ImplementableFuncTest這(zhe)個名字對應的藍(lan)圖UFunction,如(ru)果找ye)僥敲淳突岬diao)用(yong)ProcessEvent來做進一(yi)步的處理。

      ProcessEvent流程

      藍(lan)圖調(diao)用(yong)C++函數

       1 UFUNCTION(BlueprintCallable, Category = 'AReflectionStudyGameMode') 2 3 void CallableFuncTest(); 4 5 6 7 DECLARE_FUNCTION(execCallableFuncTest) 8 9 { 10 11 P_FINISH; 12 13 P_NATIVE_BEGIN; 14 15 this->CallableFuncTest(); 16 17 P_NATIVE_END; 18 19 }

      如(ru)果是(shi)通過藍(lan)圖調(diao)用(yong)的C++函數,那麼UHT會生(sheng)成(cheng)如(ru)上的代碼(ma),並且(qie)如(ru)果有參數的話(hua),會調(diao)用(yong)P_GET_UBOOL等來獲取對應的參數,如(ru)果有返(fan)回值的話(hua)也bu)嶠jiang)返(fan)回值賦值。

      總結

      至(zhi)此,加上前面我們(men)對藍(lan)圖編譯的剖析,加上藍(lan)圖虛擬機的講(jiang)解,我們(men)qie)丫 岳lan)圖的實現(xian)原理有一(yi)個比較(jiao)深入的了(liao)解,本文並沒有對藍(lan)圖的前身unrealscript進行詳細(xi)的講(jiang)解。有了(liao)這(zhe)個比較(jiao)深入的認識後(如(ru)果想要有深刻的認識,必(bi)須(xu)自己去看代碼(ma)),相信大家(jia)在設(she)計ping)鍛際shi)會更游刃有余。當然如(ru)果有錯誤的地(di)方也請大家(jia)指正(zheng),歡迎大家(jia)踴躍討論。接下來可能會把重心放到虛幻(huan)4渲染相關的模塊上,包(bao)括渲染API跨平台相關,多(duo)線程渲染,渲染流程,以及渲染算法上面,可能中間也bu)崠chuan)插一(yi)些(xie)其他的模塊(比如(ru)動畫、AI等),歡迎大家(jia)持續關注,如(ru)果你有想提前了(liao)解的章節(jie),也bu)隊 諳旅媼粞裕 銥贍芑岣ju)大家(jia)ye)牧粞岳醋 you)先級調(diao)整。

      參考文章

      https://www.usenix.org/legacy/events/vee05/full_papers/p153-yunhe.pdf http://rednaxelafx.iteye.com/blog/492667 http://www.zhihu.com/question/19608553 https://zh.wikipedia.org/wiki/%E8%99%9B%E6%93%AC%E6%A9%9F%E5%99%A8 Java Program in Action 莫(mo)樞(shu)

重庆快乐十分官网

    Tag標簽︰藍(lan)圖  虛幻(huan)  
    • 重庆快乐十分官网

    About IT165 - 廣告服務 - 隱私聲明 - 版權申明 - 免責條款 - 網站地(di)圖 - 網友投(tou)稿 - 聯系方式
    本站內容來自于互聯網,僅供用(yong)于網絡技術學習(xi),學習(xi)中請遵循(xun)相關法律法規
    重庆快乐十分官网 | 下一页