← SEARCHABLE ARCHIVE
C / C++ · 2026-07-12

C++ 從基礎到精通 — 第 12 講

C++ 組織成遊戲結構

INTERACTIVE READING MODE →

該部落格上撰寫的文章是出於教學目的而創建的。 另外,文章中的資訊可能不正確,請謹慎。

讓我們將迄今為止學到的語法與實際的遊戲結構聯繫起來。 決定哪些類別和資料結構具有哪些職責比記住文法更困難。

分擔責任

角色可以負責生命值和攻擊力,怪物可以負責怪物狀態。 DungeonGame 管理您的隊伍、活動怪物、生成隊列和專案資料庫。

class DungeonGame
{
public:
    void AddCharacter(std::unique_ptr<Character> character)
    {
        if (character)
        {
            party.push_back(std::move(character));
        }
    }

    void QueueMonster(std::shared_ptr<Monster> monster)
    {
        if (monster)
        {
            spawnQueue.push(std::move(monster));
        }
    }

private:
    std::vector<std::unique_ptr<Character>> party;
    std::vector<std::shared_ptr<Monster>> activeMonsters;
    std::queue<std::shared_ptr<Monster>> spawnQueue;
    std::stack<int> killHistory;
    std::unordered_map<int, Item> itemDatabase;
    std::weak_ptr<Monster> currentTarget;
};

派對角色完全由 DungeonGame 擁有,因此使用了 uniqueptr。 怪物可以在隊列和活動列表之間共享,因此我們使用了sharedptr。目前目標是weak_ptr,因為它不被擁有。

只需查看類型,您就可以在某種程度上判斷是誰在使物件保持活動狀態。

邊界值處理

如果您假設只收到正常輸入,則遊戲代碼很快就會崩潰。

bool AttackMonster(std::size_t characterIndex, int monsterId)
{
    if (characterIndex >= party.size())
    {
        return false;
    }

    auto iter = std::find_if(
        activeMonsters.begin(),
        activeMonsters.end(),
        [monsterId](const auto& monster)
        {
            return monster && monster->GetId() == monsterId;
        }
    );

    if (iter == activeMonsters.end())
    {
        return false;
    }

    (*iter)->TakeDamage(party[characterIndex]->Attack());
    return true;
}

它首先檢查無效索引、不存在的 ID 和 nullptr 可能性並提前返回。 您不應該在空隊列上呼叫 front 或在空堆疊上呼叫 top。

生命值不應低於 0,且已死亡的物件不應再次處理。 保護措施並不是一個華而不實的功能,但它們可以顯著減少調試時間。

與虛幻相對應的概念

一般 C++ 和 Unreal 類型中的資料結構通常具有相似的作用。

  • std::vector和TArray
  • std::unordered_map班級TMap
  • std::unique_ptr班級TUniquePtr
  • std::shared_ptr班級TSharedPtr
  • std::weak_ptr班級TWeakPtr

但是,std::shared_ptr 不應該僅僅因為名稱相似而用於 UObject。 常規 C++ 物件的生命週期由 RAII 和智慧指標管理,而 UObject 則由 Unreal 的 GC 管理。

AActor 並不像普通物件那樣直接刪除。Destroy(),終止過程為EndPlay()加工於 需要注意的是,一般F類型和UObject類型有不同的歸屬方式。

此外,如果 TArray 和 std::vector 都發生重新分配,則指向該元素的指標可能會變得無效。

最後,C++ 語法不是一個單獨的函數。 類別綁定職責,容器儲存狀態,智慧指標代表生命週期,演算法執行操作。

一旦你開始看到這個結構,你將能夠理解為什麼程式碼看起來像這樣,而不僅僅是記住語法。