[UE4]動態數組:TArray容器


為什么使用UE4提供的容器類?

如果你用過C++的STL庫,你就知道STL提供了各種各樣的容器/數據結構,使得你對處理很多數據的時候非常快捷高效。UE4同樣也提供了類似的庫,庫里面的類型是以T開頭的,使用UE4提供的容器庫可以更好地實現跨平台。所以在UE4進行開發的時候我們很少去使用STL容器,更多時候是使用UE4提供的容器。

一、TArray<T>是什么

如果你學過C++的模板,你就知道TArray<T>是一個模板類型,T是由我們指定的任意類型。比如你想創建一個數組元素都是int類型的動態數組,那么這個數組類型就是TArray<int>,類似的,你可以創建TArray<double>、TArray<FString>等等,T甚至是可以是你自己創建的C++類。

TArray<T>中所有數據元素都是T類型的,因此不能混合各種不同類型的元素進去這個容器。TArray沒有設計成被繼承的,所以不應該去繼承它。並且new/delete一個TArray是危險的行為。當TArray的生命周期結束時(超出作用域),容器里面的所有元素也會被銷毀。當你從另一個TArray創建一個新的TArray會復制所有數據元素到新的變量,而不會共享這些元素的內存。

二、創建動態數組

為了創建一個動態數組,你可以這樣寫:

TArray<int32> IntArray;
注意,int32是32位整形,而int會根據機器不同而字節不同,所以為了跨平台建議使用int32。

這時候,因為我們沒有數據填充到該數組里面,所以還沒有內存被分配。

三、填充動態數組

1) TArray::Init

在UE4的官方文檔可以看到該函數的聲明:

void Init(const ElementType & Element, int32 Number)

該函數用於設置數組為Number個,並且每個元素值的為Element,例如:

IntArray.Init(10, 5);// IntArray == [10,10,10,10,10]

2) TArray::Add

先來看該函數的聲明:

int32 Add( const ElementType & Item)

該函數用於添加新元素到動態數組末尾。

3) TArray::Emplace

先來看該函數的聲明:

template<typename... ArgsType>int32 Emplace( ArgsType &&... Args)

該函數也是用於添加新元素到動態數組末尾。示例:

TArray<FString> StrArr;StrArr.Add(TEXT("Hello"));StrArr.Emplace(TEXT("World"));// StrArr == ["Hello","World"]

雖然Add和Emplace都是添加新元素到動態數組末尾,不過它們的內部實現是不同的:

  • Add會復制元素到數組容器里面
  • Emplace使用你給的參數來構造一個新的元素類型的實例


在上面的示例中,Add創建了一個FString臨時變量,然后傳遞給數組,在數組內部再次調用拷貝構造函數來復制這個FString臨時變量。但是Emplace使用了C++11的右值引用技術,它不會構造臨時變量,而是在數組內部使用這個字符串常量來創建FString變量。因此,由於Emplace少了一次拷貝構造函數操作,所以Emplace會比FString高效。但是由於Add比Emplace更具可讀性,如果是普通的C++內置類型(不會有拷貝構造函數的巨大開銷),建議使用Add。

4) TArray::Push

TArray::Push提供了兩個一樣功能的重載函數可以分別代替Add和Emplace。所以無論何時使用Push比Add和Emplace更加方便了:

void Push( const ElementType & Item )void Push( ElementType && Item )

四、迭代動態數組

通常迭代都至少有兩種方式,一種是使用索引,一種是使用迭代器。

1) 使用索引迭代動態數組

為了練習我們的數組,我們可以在靠近NPC的函數中打印該數組。我們將ANPC::Prox_Implementation修改成如下代碼:

void ANPC::Prox_Implementation(
    AActor* otherActor,
    UPrimitiveComponent* otherComp,
    int32 otherBodyIndex,
    bool bFromSweep,
    const FHitResult & sweepResult
) {
    //通過強制轉換成AAVatar是否成功來判斷是否玩家角色
    if (Cast<AAvatar>(otherActor) == nullptr)
    {
        return;
    }
    //獲得第一人稱控制器
    APlayerController* PController = GetWorld()->GetFirstPlayerController();
    if (PController)
    {
        //獲得HUD界面
        AMyHUD* hud = Cast<AMyHUD>(PController->GetHUD());
        hud->AddMessage(Message(NpcMessage, 5.f, FColor::White));
        //測試數組
        TArray<int> array;
        array.Push(1);
        array.Push(3);
        array.Push(7);
        for (int index = 0; index < array.Num(); index++)
        {
            //GEngine是全局引擎變量,我們使用它的AddOnScreenDebugMessage函數來在游戲屏幕上打印調試信息。
            //該函數第一個參數是調試輸出的位置,填寫-1表示總在原來的調試信息上方。
            //第二個參數是字體大小,第三個參數是字體顏色,第四個參數是要打印的字符串,這里用FromInt函數將Int轉換FString。
            GEngine->AddOnScreenDebugMessage(-1, 40, FColor::White, FString::FromInt(array[index]));
        }
    }
}

注意,每次調試打印的信息都在原來的調試信息上面,所以數組里面的內容依次是1、3、7,而不是7、3、1。

2) 使用迭代器循環數組

void ANPC::Prox_Implementation(
    AActor* otherActor,
    UPrimitiveComponent* otherComp,
    int32 otherBodyIndex,
    bool bFromSweep,
    const FHitResult & sweepResult
) {
    //通過強制轉換成AAVatar是否成功來判斷是否玩家角色
    if (Cast<AAvatar>(otherActor) == nullptr) {
        return;
    }
    //獲得第一人稱控制器
    APlayerController* PController = GetWorld()->GetFirstPlayerController();
    if (PController)
    {
        //獲得HUD界面
        AMyHUD* hud = Cast<AMyHUD>(PController->GetHUD());
        hud->AddMessage(Message(NpcMessage, 5.f, FColor::White));
        //測試數組
        TArray<int> array;
        array.Push(1);
        array.Push(3);
        array.Push(7);
        //在使用容器的時候,為了容器的操作一致性,通常都會像下面這樣使用迭代器來循環
        //所謂的迭代器其實類似於一個指針,當對指針進行++時,就指向后面的元素。
        //當超出容器范圍的時候,迭代器為空,跳出循環
        for (TArray<int>::TIterator it = array.CreateIterator(); it; ++it)
        {
            //GEngine是全局引擎變量,我們使用它的AddOnScreenDebugMessage函數來在游戲屏幕上打印調試信息。
            //該函數第一個參數是調試輸出的位置,填寫-1就不會覆蓋以前的調試信息。
            //第二個參數是字體大小,第三個參數是字體顏色,第四個參數是要打印的字符串,這里用FromInt函數將Int轉換FString。
            GEngine->AddOnScreenDebugMessage(-1, 40, FColor::White, FString::FromInt(*it));
        }
    }
}

在實際開發中,因為使用迭代器進行迭代更簡潔美觀,我們通常都會這樣進行迭代容器。

找到一個元素是否在 TArray
尋找UE4容器是容易的。它通常使用find成員函數完成。使用前面創建的數組,我們可以通過鍵入以下代碼行來找到值10的索引:
int index = array.Find( 10 ); // would be index 3 in image above


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM