虛幻4藍圖快速入門(6)
決定什麼時候使用C++或者藍圖有兩個主要考慮的因素:
- 速度
- 表示式複雜度
除了這兩個因素外,還有整個遊戲的複雜程度和團隊的組成。如果你的團隊中有比程式設計師更多的美術人員,那麼使用的藍圖可能會比C++程式碼要多很多。相反,如果團隊中有很多的程式設計師,那麼他們可能會更喜歡用C++來編寫邏輯。而我們希望人們在中間取一個折中點。在英佩,一般流程是這樣的,內容創作者會製作一個特別複雜的藍圖,然後程式設計師會檢視這個藍圖並且思考如何把其中的很多工作轉換成一個新的藍圖結點,這樣就可以把一塊的功能轉換成C++程式碼。一個比較好的經驗大規模地使用藍圖,然後當它們達到一定複雜度需要對一個功能進行精確描述時(或者當它們對於一個非程式設計師過於複雜時),或者執行速度指示要轉換成C++程式碼時,把它們轉換成C++程式碼。
速度
對於速度來說,藍圖要比C++慢得多。也並不是說效能就非常得差,但是如果你要的事情需要很多的計算量,或者以一個很大的頻率呼叫時,那麼你最好使用C++而不是藍圖。然而,兩個組合使用來滿足你團隊和工程效能的需要也是可能的。如果你的一個藍圖有很多功能,那麼你可以把其中的一部分放入C++程式碼中用來提升執行速度,但是保留其它的部分用來確保靈活性。如果效能分析結果顯示藍圖中的某個操作比較耗時,那麼你就需要考慮把它移植到C++中,然後把其它的留在藍圖中實現。
複雜度
對於表示式複雜度來說,有些東西在C++中做起來比在藍圖裡面容易很多。藍圖在很多方面做的不錯,但是有些東西用結點並不是那麼容易表示。操作大量的資料,字串操作,大規模資料的數學計算等都非常複雜,並且在視覺化系統中並不容易看懂。這些東西最好放在C++中去實現而不是藍圖中,就是因為它們更容易檢視並且容易理解到底要做什麼。表示式複雜也是一個多人系統最好在C++中實現的一個原因。
例子
由於不同的功能適合實現的方式不同,有的在C++實現比較好,有的在藍圖實現比較好,這裡有一些例子告訴C++程式設計師和藍圖創作者如何共同工作來製作一個遊戲。
- 程式設計師可以在C++中建立一個Character類,它會定義一些自定義事件,然後藍圖可以用來擴充套件Character類,並且設定網格和設定一些預設值。示例可以檢視ShooterGame示例工程中角色控制和敵人的實現。
- 一個能力系統的基類在C++中實現,但是設計者可以建立通過建立藍圖來做具體的事情。在StrategyGame示例中,在C++中定義了一個炮塔(turret)基類,但是火焰噴射器,加農炮等都是在藍圖裡面定義的。
- 一個可拾取物,它的'Collect'和'Respawn'函式都是藍圖可實現的(BlueprintImplementableEvent)事件,它們可以用來讓設計者來重寫用來 生成不同的粒子發射器和聲音。ShooterGame和StrategyGame裡面都有這樣建立可拾取物的示例。
建立一個藍圖API:技巧
當建立一個暴露給藍圖使用的API時有幾個需要注意的點:
- 預設引數在藍圖裡面得到了很好的支援
1 /** 2 * Prints a string to the log, and optionally, to the screen 3 * If Print To Log is true, it will be visible in the Output Log window. Otherwise it will be logged only as 'Verbose', so it generally won't show up. 4 * 5 * @param InString The string to log out 6 * @param bPrintToScreen Whether or not to print the output to the screen 7 * @param bPrintToLog Whether or not to print the output to the log 8 * @param bPrintToConsole Whether or not to print the output to the console 9 * @param TextColor Whether or not to print the output to the console 10 */ 11 UFUNCTION(BlueprintCallable, meta=(WorldContext="WorldContextObject", CallableWithoutWorldContext, Keywords = "log print", AdvancedDisplay = "2"), Category="Utilities|String") 12 static void PrintString(UObject* WorldContextObject, const FString& InString = FString(TEXT("Hello")), bool bPrintToScreen = true, bool bPrintToLog = true, FLinearColor TextColor = FLinearColor(0.0,0.66,1.0));
- 儘量編寫返回很多引數的函式而少用返回結構體的引數。這裡有一小段程式碼來展示如何建立多個輸出引腳。
1 UFUNCTION(BlueprintCallable, Category = "Example Nodes") 2 static void MultipleOutputs(int32& OutputInteger, FVector& OutputVector);
- 向一個已有函式新增新的引數是沒問題的,但是如果你想移除或者改變它們,那麼你應該廢棄原有的函式並且新增一個新的函式。確保使用廢棄這個元資料,這樣關於使用新函式的資訊就會顯示在藍圖裡面了。
1 UFUNCTION(BlueprintCallable, Category="Collision", meta=(DeprecatedFunction, DeprecationMessage = "Use new CapsuleOverlapActors", WorldContext="WorldContextObject", AutoCreateRefTerm="ActorsToIgnore")) 2 static ENGINE_API bool CapsuleOverlapActors_DEPRECATED(UObject* WorldContextObject, const FVector CapsulePos, float Radius, float HalfHeight, EOverlapFilterOption Filter, UClass* ActorClassFilter, const TArray<AActor*>& ActorsToIgnore, TArray<class AActor*>& OutActors);
- 如果一個函式需要用列舉當作引數,考慮使用'expand enum as execs'這個元資料,這樣可以讓這個節點更容易使用。
1 UFUNCTION(BlueprintCallable, Category = "DataTable", meta = (ExpandEnumAsExecs="OutResult", DataTablePin="CurveTable")) 2 static void EvaluateCurveTableRow(UCurveTable* CurveTable, FName RowName, float InXY, TEnumAsByte<EEvaluateCurveTableResult::Type>& OutResult, float& OutXY);
- 許多耗時的函式(例如移動到這裡)應該是潛伏(latent)函式。
1 /** 2 * Perform a latent action with a delay. 3 * 4 * @param WorldContext World context. 5 * @param Duration length of delay. 6 * @param LatentInfo The latent action. 7 */ 8 UFUNCTION(BlueprintCallable, Category="Utilities|FlowControl", meta=(Latent, WorldContext="WorldContextObject", LatentInfo="LatentInfo", Duration="0.2")) 9 static void Delay(UObject* WorldContextObject, float Duration, struct FLatentActionInfo LatentInfo );
- 如果可能的話,考慮把函式放入一個共享庫。這樣在多個類之間更方便使用,並且會少一個'target'的引腳。
1 class DOCUMENTATIONCODE_API UTestBlueprintFunctionLibrary : public UBlueprintFunctionLibrary
- 記得儘可能地標記結點為純(pure)的,因為這樣會少產生一個需要連線的執行引腳。
1 /* Returns a uniformly distributed random number between 0 and Max - 1 */ 2 UFUNCTION(BlueprintPure, Category="Math|Random") 3 static int32 RandomInteger(int32 Max);
- 把一個函式標記了常量函式(const)也會避免讓這個藍圖結點產生執行引腳。
1 /** 2 * Get the actor-to-world transform. 3 * @return The transform that transforms from actor space to world space. 4 */ 5 UFUNCTION(BlueprintCallable, meta=(DisplayName = "GetActorTransform"), Category="Utilities|Transformation") 6 FTransform GetTransform() const;