遊戲功能編程需求中 STL容器的運用

C++標準庫的容器分爲序列容器和關聯容器。編程

序列容器簡單的有vectorlistdeque,複雜的還有配接器stackqueuepriority_queue數組

關聯容器簡單的有setmap,複雜的有multisetmultimap,這都是基於RB-tree的,基於hashtable的也有hash-sethash-maphash-multisethash-multimap。數據結構

工做中不少容器並不經常使用,經常使用的無非這幾個vectorlistsetmapqueue。下面就簡單總結一下這幾個簡單容器的適用需求(遊戲功能邏輯方面的需求)。函數

 

1.vector優化

使用頻率超高。一般咱們使用數組是由於有明確的上限。而使用vector是由於有可能擴展。spa

有明確上限的,好比月簽到系統。只須要一個31長度的數組,由於一個月最多31天。code

沒有明確上限的,好比成就係統。成就係統裏的殺怪成就,一開始需求多是10,100,1000,也就是殺怪10個,100個,1000個能夠得到獎勵。可是上線以後以爲數值不合適,須要多一個2000的條目。這種就很是適合用vector。blog

更多的狀況實際上是用數組和vector都行的。這種狀況就須要編程者本身斟酌了。索引

看一個例子,等級禮包。如圖:隊列

 

咱們先枚舉出獎勵的的各類狀態。

enum RoleLevelRewardState
{
    RLR_INVALID = 0,            //不可領取
    RLR_CAN_GET,                //可領取
    RLR_HAS_GET,                //已領取
};

咱們能夠規定一個上限。這個上限是獎勵的條目上限。若是策劃需求改動了條目數量,好比加了一個100級的等級獎勵,那咱們就須要改動MAX_ROLE_REWARD這個值。

固然通常狀況下,最好是擴展,而不改動已經存在的。由於要兼顧老玩家,好比老玩家已經100級了,前面領取過50,70,90級的獎勵。可是這時策劃須要增長一個60級的獎勵…這就比較亂。最好和策劃商量兼容老玩家。

若是是增長一個120級的獎勵,這天然就比較簡單。

static const int MAX_ROLE_REWARD = 3;

定義咱們的數組:

int m_role_level_reward[MAX_ROLE_REWARD];

若是用vector,就是這樣寫:

std::vector<int> m_role_level_reward;

數組在構造函數裏的初始化,或者重置清空一般是這樣寫:

memset(m_role_level_reward, 0, sizeof(m_role_level_reward));

vector初始化和清空就簡單些:

m_role_level_reward.clear();

邊界問題是咱們須要重視的,在引用數組或vector元素時,都用首先判斷邊界。

bool UpdateRoleRewardState(int index)
{
    if (index < 0 || index >= MAX_ROLE_REWARD) return false;
        
    m_role_reward[index] = 1; //改變內容
    return true;     
}

更多的不一樣在於循環時:

for (int i = 0; i < MAX_ROLE_REWARD; ++i) // 數組
for (int i = 0; i < (int)m_role_level_reward.size() && i < MAX_ROLE_REWARD; ++i) // vector

(int)強轉是由於.size()返回的是size_t類型,其實判斷i < size已經足夠,還要加上MAX_ROLE_REWARD是保證需求的數量限制和異常出現,較爲穩妥。

固然vector用迭代器遍歷更爲標準。但下標遍歷也是能夠的。

 

2.list

有不少狀況,需求可使用list,也可使用vector。其實只須要記得,若是需求須要隨意在某個位置插入隨意刪除某個位置的記錄,改變總體順序的就用list,其餘均可以用vector。

list是個循環的雙向鏈表,vector是連續空間。因此list能夠隨意在任何位置插入,隨意刪除某個位置的記錄,而vector不行。vector的insert和erase代價都很大,會使得迭代器失效。 

一個典型的例子,記錄玩家殺死大boss的記錄表。

首先它的數量確定不肯定的,因此數組不適合。並且要求只記錄最近的50條,新的記錄顯示在前面。這就很是適合用list了。

由於咱們確定會有一個邏輯是記錄數大於50的時候,刪掉最後1條記錄。再把新的記錄加到前面。

static const MAX_KILL_RECORD_NUM = 50;
if ((int)m_list.size() > MAX_KILL_RECORD_NUM)
{
    m_list.pop_back();
    //m_list.push_front();  //插入的數據結構略過
}

list能夠任意的insert,erase,pop,push都是代價很小的。

咱們要遍歷list,就不能像vector同樣了。由於vector有[]操做,list沒有。因此list須要用迭代器遍歷。

 

3.map

map也是很經常使用的容器。與vector,list差異很大,它是一個關聯容器。所謂關聯容器就是有key和value對應。底層是紅黑樹(RB-Tree)實現的。

一般的使用場景是記錄的數據有一個惟一的key做爲索引的標誌。好比結婚系統,夫妻的相關數據。

夫妻有關的數據結構好比是:

struct MarriageInfo
{
    char man_name[64];
    char woman_name[64];
    int man_role_id;
    int woman_role_id;
    int love_value; //恩愛值  
};

存在一個map裏,key是夫妻倆的role_id組合。好比A的role_id是1000,B的role_id是1011,那麼兩人共同的夫妻數據key就是1000_1011,一個字符串類型。

std::map<string, MarriageInfo> m_marriage_data;

這個數據裏有個惟一的key與其value對應。這種類型的數據一般用map合適。但其實你仔細想一想,用vector也能實現須要的功能。那樣的話數據結構里加一項類型key的項就行。取決於編程者的風格。

struct MarriageInfo
{
    string marriage_key;
    char man_name[64];
    char woman_name[64];
    int man_role_id;
    int woman_role_id;
    int love_value; //恩愛值  
};
std::vector<MarriageInfo> m_marriage_data;

 map在引用元素以前能夠用key作find操做,來判斷。

bool UpdateMarriageData(string key)
{
    std::map<string, MarriageInfo>::iterator iter = m_marriage_data.find(key);
    if (iter == m_marriage_data.end()) return false;
    iter->second.love_value = 100;
    return true;
}

插入一個map元素,一般能夠make_pair一下,而後insert。也有一種簡單的寫法,就是直接[]引用。

bool InsertMarriageData(string key, MarriageInfo marriage_info)
{
    std::map<string, MarriageInfo>::iterator iter = m_marriage_data.find(key);
    if (iter == m_marriage_data.end()) return false;
    m_marriage_data[key] = marriage_info;
    return true;
}

 

4.set

set是實際上是一種簡單的map,它的value隱藏了,只顯示key出來。set適用比較簡單的需求。

好比記錄玩家得到的物品獎勵,已經得到過的物品不能再得到,物品id做爲key。

std::set<ItemId> m_reward_record;

那麼set和map的用法相似,是簡化的map。取決於需求的複雜度。

 

5.queue

隊列queue。queue實際上是一種適配器。底層是deque實現,屏蔽了deque的部分功能,封裝成一個隊列。隊列是一個後進先出的結構。

有一類需求適合,就是匹配需求。就拿吃雞來講。首先進入的是一個等待副本。先進來的玩家進入排隊。滿100我的,隊列就出去100我的,進入另一個副本。

匹配實際是一個複雜的功能需求,取決於匹配的條件是什麼。吃雞的需求多是滿100個,送走100個。這100我的有什麼等級限制啊,經驗限制啊,小隊限制啊,都是複雜的。

另外匹配仍是一個輪詢過程。輪詢的優化策略也有講究,再也不展開。

可是毫無疑問queue隊列很符合這類的需求。

相關文章
相關標籤/搜索