文章目錄
- 前言
- 一、原生c++的枚舉類型
-
-
-
- 關鍵字class
- int8 - 枚舉的基礎類型(`underlying type`)
-
-
- 二、枚舉類型的靈活運用
-
-
-
- 位運算
- 枚舉循環周遊
-
-
- 三、虛幻風格的枚舉類型
-
-
-
- UENUM
- UMETA
- TEnumAsByte
-
-
- 總結
前言
虛幻引擎中的代碼部分實作了一套反射機制,為c++代碼帶了更多友善的特性。本篇文章将會着眼于其中更加細節的部分——虛幻中的enum。
在虛幻風格的代碼中,我們經常能使用這樣的方法來建立枚舉類型:
UENUM(BlueprintType)
enum class EMyEnum : uint8
{
Option1 UMETA(DisplayName = "Option 1"),
Option2 UMETA(DisplayName = "Option 2"),
Option3 UMETA(DisplayName = "Option 3")
};
似乎有一些c++的影子,但又好像有一些不一樣的部分。它有基本c++的架構,但又有一些額外的東西。如果你對c++很熟悉,那再好不過。隻需要始終把握一點,一切還是源于c++,額外的内容的目的是将該類型加入到反射系統中,使其支援序列化,使其能夠支援藍圖編輯。
一、原生c++的枚舉類型
我們來先将上面的例子簡化一下
enum EMyEnum
{
Option1,
Option2,
Option3
};
這是我們最熟悉的枚舉建立方法,
EMyEnum
是枚舉的名字。
下面是它一般的使用方式
// Example use of the enum
void MyFunction()
{
EMyEnum MyOption = Option1;
if (MyOption == Option2)
{
// Do something
}
}
接下來,我們一點一點為它擴充。
關鍵字class
首先添加關鍵字
class
。
enum class EMyEnum
{
Option1,
Option2,
Option3
};
在這個例子中,我們的枚舉類型
EMyEnum
有三個具體的選項:
Option1
,
Option2
和
Option3
。其中
class
關鍵字的作用是說明這是一個有作用範圍(作用域)的枚舉(
scoped enum
),意思是枚舉的值需要在枚舉類型的作用範圍内使用,具體如下:
// Example use of the enum
void MyFunction()
{
EMyEnum MyOption = EMyEnum::Option1;
if (MyOption == EMyEnum::Option2)
{
// Do something
}
}
int8 - 枚舉的基礎類型( underlying type
)
underlying type
可以利用
int8
确定枚舉值的基本類型
enum class EMyEnum : int8
{
Option1,
Option2,
Option3
};
這意味着每個枚舉值都會以一個8位的整型值來表示。其他可用基本類型還有
uint8
,
uint16
,
uint32
,
int16
,
int32
等等。
那這有什麼用呢?比如說希望判斷枚舉值是否在某個範圍内,或者是多個枚舉值之間進行比較的時候。
下面看一個具體的例子
enum class EDamageType : uint8
{
Blunt,
Piercing,
Fire,
Ice,
Electric,
Poison
};
我們建立了一個表示傷害類型的枚舉,當然從遊戲的層面來講,枚舉值可能遠遠不止這些。
這裡,我們可以利用
>
和
<
符号來判斷枚舉值是否在一個範圍内,比如
EDamageType DamageType = EDamageType::Fire;
if (DamageType < EDamageType::Electric)
{
// Do something for non-electric damage types
}
else
{
// Do something for electric damage type
}
例子中,隻要待判斷的傷害類型屬于
Blunt
,
Piercing
,
Fire
或
Ice
中的任何一個,判斷條件都将成立,因為他們都小于
Electric
。
更直覺的,我們将每個枚舉值的預設
uin8
都寫出來
enum class EDamageType : uint8
{
Blunt = 0,
Piercing = 1,
Fire = 2,
Ice = 3,
Electric = 4,
Poison = 5
};
在進行比較或者範圍判定的時候,實際上是這些枚舉值背後的數字在進行判斷。
當然,也可以不使用預設值而用任意指定的值,比如
enum class EDamageType : uint8
{
Blunt = 100,
Piercing = 200,
Fire = 300,
Ice = 400,
Electric = 500,
Poison = 600
};
可以使用
static_cast
或
reinterpret_cast
來進行枚舉類型和其基礎類型之間的顯示轉換
EDamageType DamageType = EDamageType::Fire;
uint8 DamageTypeValue = static_cast<uint8>(DamageType);
那麼如果不指定基本類型會怎麼樣?像一開始那樣直接将這個省略會有什麼結果?
答案是編譯器會根據枚舉值自動為它挑選合适的基本類型,是以似乎省略也沒有什麼不妥,甚至為書寫友善了不少。但是還是推薦在書寫的時候指明基本類型,否則可能會有一些類型比對方面的問題,亦或是出現一些無法預測的行為。
二、枚舉類型的靈活運用
除了上面介紹的特性和基本應用,枚舉還有一些其他好玩的用法。
位運算
利用位運算,可以快捷得将枚舉值配置設定為以2為系數的等比數列,進而可以将枚舉值表示為二進制碼的組合,比如:
enum class EMyFlags : uint8
{
None = 0,
Flag1 = 1 << 0,
Flag2 = 1 << 1,
Flag3 = 1 << 2,
Flag4 = 1 << 3,
Flag4 = 1 << 5,
All = Flag1 | Flag2 | Flag3 | Flag4,
};
在這個例子中,每個标志都進行左移運算,即每一個值都是前一個值的2倍。可以利用位運算符号(|,&,^)對這些枚舉值進行組合。
All
就是所有标志組合的結果,将它與其中某個枚舉值進行
&
運算,即可判斷它是否包含該枚舉值
EMyFlags MyFlag = EMyFlags::Flag1;
if (EMyFlags::All & MyFlag)
{
// Do something
}
這裡的結果自然是通過的。
枚舉循環周遊
在
c++ 11
的新特性中,可以對枚舉類型的值使用循環周遊,如
for (EMyEnum Value : TEnumRange<EMyEnum>())
{
// Do something with Value
}
在這個例子中,
TEnumRange
模闆提供了一種周遊所有
EMyEnum
的枚舉值的方式。
三、虛幻風格的枚舉類型
虛幻在基礎c++枚舉的基礎上,也加入了自己風格的代碼,這自然也為它添加了更多的功能和特性。
這裡以一個遊戲中典型的枚舉為例
UENUM(BlueprintType)
enum class EWeaponType : uint8
{
Pistol UMETA(DisplayName = "Pistol"),
Shotgun UMETA(DisplayName = "Shotgun"),
RocketLauncher UMETA(DisplayName = "Rocket Launcher")
};
EWeaponType
是該枚舉類型的名稱,
Pistol
,
Shotgun
,
RocketLauncher
是具體的枚舉值。
UENUM
UENUM
将枚舉類型進行标記,友善UHT為其生成相應的類型檔案,生成反射系統需要的代碼。
BlueprintType
說明符令該枚舉可以在藍圖中自如使用。
UMETA
使用
UMETA
宏來指定每個枚舉值指定一個顯示名,即在藍圖使用中和UI中顯示的名稱。
TEnumAsByte
虛幻引擎還提供了一種特殊類型的枚舉,稱為
TEnumAsByte
,用于加強類型安全并且解決一些常見的錯誤,主要是一些将枚舉類型強行用作其基礎類型的應用場合,例如意外使用枚舉作為數組的索引。
這裡就聲明了一個
TEnumAsByte<EWeaponType>
類型的枚舉變量
Weapon
,它本身其實是一個結構體,其中隻有一個簡單的
uint8
類型的字段。這是一種典型的面向對象的做法,用一個類型對象将資料封裝,這樣,當你用一個枚舉值去為另一個枚舉值指派的時候就會報錯了,這種做法進一步保證了類型安全。
在将枚舉類型變量公布給藍圖使用時,通常需要将它與基本枚舉類型結合起來進行類型的聲明。
總結
枚舉類型是一種強大的程式設計工具,可以使你的代碼更加清晰、簡潔、易于維護。在使用時,要注意選擇适當的技巧,以確定你的代碼能夠正常工作并避免潛在的錯誤。
希望文中的例子可以幫助更多人更好地了解如何在編寫代碼時使用枚舉類型。