- | - |
---|---|
文章 | UE4入門學習4:C++編程介紹 |
作者 | 游藍海( http://blog.csdn.net/you_lan_hai ) |
UE4直接使用C++作為邏輯層語言,這樣引擎層與邏輯層語言統一,不需要膠水代碼去轉發,消除了邏輯層和引擎層的交互成本。為了便於開發,UE4對C++做了一些包裝,比如反射和垃圾回收,大大減輕C++開發的難度。本文結合UE4官方C++編程指南文檔,對C++相關特性做一些描述和總結。
反射
C++本來是不支持反射的,只有一個基本的RTTI(運行時類型信息)特性,僅能在運行時獲取對象的類型信息,無法得到成員變量和函數列表信息。如果我們要對成員變量進行序列化(存檔/讀檔),需要自己寫很多輔助的讀取和寫入方法,非常麻煩。
UE4在C++編譯開始前,使用工具UnrealHeaderTool
,對C++代碼進行預處理,收集出類型和成員等信息,並自動生成相關序列化代碼。然后再調用真正的C++編譯器,將自動生成的代碼與原始代碼一並進行編譯,生成最終的可執行文件。這個過程類似於Qt的qmake預處理機制。
拿出我們之前工程的代碼來分析:
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"
UCLASS()
class HELLOUE4_API AMyActor : public AActor
{
GENERATED_BODY()
public:
// 默認構造函數,初始化一些成員屬性。
AMyActor();
protected:
// 游戲開始或者被創建出來后調用。
virtual void BeginPlay() override;
public:
// 每幀都會被調用
virtual void Tick(float DeltaTime) override;
// 這個變量會出現在編輯器編輯界面。
UPROPERTY(EditAnywhere)
int32 MyID;
private:
// 這是個內部變量。不會出現在編輯器界面。
float RunningTime;
};
這個代碼文件包含了一些特殊的頭文件和宏,下面逐一介紹:
名稱 | 描述 |
---|---|
MyActor.generated.h | 這個文件是UE4自動生成的,里面存貯了UE4收集的類型信息。 |
UCLASS | 告訴UE4這個類需要收集類型信息 |
UPROPERTY | 告訴UE4這個成員變量的信息需要被收集 |
GENERATED_BODY | 告訴UE4自動生成的代碼注入在這里 |
以下是MyActor.generated.h
中的部分代碼:
#define HelloUE4_Source_HelloUE4_MyActor_h_9_INCLASS_NO_PURE_DECLS \
private: \
static void StaticRegisterNativesAMyActor(); \
friend HELLOUE4_API class UClass* Z_Construct_UClass_AMyActor(); \
public: \
DECLARE_CLASS(AMyActor, AActor, COMPILED_IN_FLAGS(0), 0, TEXT("/Script/HelloUE4"), NO_API) \
DECLARE_SERIALIZER(AMyActor) \
/** Indicates whether the class is compiled into the engine */ \
enum {IsIntrinsic=COMPILED_IN_INTRINSIC};
實際上,GENERATED_BODY
這個宏最終展開后就對應了宏HelloUE4_Source_HelloUE4_MyActor_h_9_INCLASS_NO_PURE_DECLS
,也就是說自動生成的代碼會在C++編譯的時候注入到了類AMyActor
中。
注意,如果聲明變量或類型不加上前綴,是不會生成類型信息的。下面是一些基本的類型標記:
- UCLASS() - 告訴UE4生成類的反射數據。類必須派生自 UObject。
- USTRUCT() - 告訴UE4生成結構體的反射數據。
- UENUM() - 告訴UE4生成枚舉的反射數據。
- GENERATED_BODY() - UE4 使用它替代為類型生成的所有必需樣板文件代碼。
- UPROPERTY() - 使 UCLASS 或 USTRUCT 的成員變量可用作 UPROPERTY。UPROPERTY 用途廣泛。它允許變量被復制、被序列化,並可從藍圖中進行訪問。垃圾回收器還使用它們來追蹤對 UObject 的引用數。
- UFUNCTION() - 使 UCLASS 或 USTRUCT 的類方法可用作 UFUNCTION。UFUNCTION 允許類方法從藍圖中被調用,並在其他資源中用作 RPC。
序列化
有了反射功能之后,成員變量的序列化也就更方便了。UE4收集了每個類成員的類型信息,這樣存檔和讀檔時,根據名稱和類型就可以自動完成了,整個過程不需要人工干預。
需要序列化的成員變量,需要在變量聲明的時候在前面加上UPROPERTY()
宏,宏參數有很多,分別表示變量的詳細屬性,下面列舉一些常用的:
UPROPERTY參數 | 說明 |
---|---|
EditAnywhere | 表示該屬性可從編輯器內的屬性窗口編輯。 |
Category | 定義屬性的分類。使用方法: Category=CategoryName. (分類=分類名稱) |
Const | 編輯器中不能修改該值 |
BlueprintReadOnly | 在藍圖中只讀,不可修該。 |
BlueprintReadWrite | 在藍圖中可讀寫。 |
BlueprintCallable | 僅能用於Multicast代理。該代理可被藍圖調用。 |
與UPROPERTY
對應的還有一個用於修飾函數的宏UFUNCTION
,該宏常用於描述如何從藍圖中訪問C++的函數。
UFUNCTION參數 | 說明 |
---|---|
BlueprintCallable | 這種類型的函數,只能在C++中實現和重寫。可以理解為藍圖“只讀”函數。 |
BlueprintImplementableEvent | 只能在藍圖中實現的函數。類似於C++的純虛函數 |
BuleprintNativeEvent | C++可以提供默認實現,藍圖可以重寫。 |
熱重載
在編輯器模式下,UE4將工程代碼編譯成動態鏈接庫,這樣編輯器可以動態的加載和卸載某個動態鏈接庫。UE4為工程自動生成一個cpp文件(本工程為HelloUE4.generated.cpp
),cpp文件包含了當前工程中所有需要反射的類信息,以及類成員列表和每個成員的類型信息。在動態鏈接庫被編輯器加載的時候,自動將類信息注冊到編輯器中。反之,卸載的時候,這樣類信息也會被反注冊。
在開發的過程中,當我們編譯完成工程的時候,UE4編輯器會自動檢測動態鏈接庫的變化,然后自動熱重載這些動態鏈接庫中的類信息。
垃圾回收
有了反射機制,UE4也能夠知道哪些類型是指針類型,以及哪些變量需要被垃圾收集系統管理。被垃圾收集系統管理的對象,不需要手動調用delete,只需要正確的維持變量的引用即可。我沒有看過UE4垃圾收集系統實現源碼,姑且將UE4的垃圾收集系統看成是一個使用了“標記-清掃”算法的一個沙盒,當不需要使用某個指針變量的時候,我們只需要把他置為NULL,在下個垃圾回收階段中,系統會自動回收。
使用垃圾回收的時候,需要遵從一定的規范:
- 所有需要被托管的成員變量需要用宏
UPROPERTY()
標記 - 所有從UObject派生的類,才能被系統托管。非UObject派生的類可以考慮從類FGCObject派生,並實現
AddReferencedObjects
方法,或者使用智能指針 - 數組TArray的UObject類型指針元素,可以被自動托管
- Actor類型對象在不用的時候,需要手動調用Destroy。調用Destroy后不會立即消耗,也會等待下個垃圾回收階段
編碼規范
類名前綴
UE4的類名必須遵從命名規范,需要在類名前面加上正確的前綴,與之對應的C++文件名則不加前綴。否則會編譯報錯。
- 派生自 Actor 的類前綴為 A,如 AController。
- 派生自 UObject 的類前綴為 U,如 UComponent。
- 枚舉 的前綴為 E,如 EFortificationType。
- 接口 類的前綴通常為 I,如 IAbilitySystemInterface。
- 模板 類的前綴為 T,如 TArray。
- 派生自 SWidget(Slate UI)的類前綴為 S,如 SButton。
- 其余類的前綴均為 字母 F ,如 FVector。
其他類型命名
bool類型變量需要加上b前綴,如 bCallable。
參考
- UE4 中的 C++ 編程介紹
https://docs.unrealengine.com/latest/CHN/Programming/Introduction/index.html - UE4 編碼規范
https://docs.unrealengine.com/latest/CHN/Programming/Development/CodingStandard/index.html
本系列文章會和我的個人公眾號同步更新,感興趣的朋友可以關注下我的公眾號:游戲引擎學習。掃下面的二維碼加關注: