H2Engine游戲伺服器設計之屬性管理器

来源:https://www.cnblogs.com/zhiranok/archive/2018/01/09/h2engine_propmgr.html
-Advertisement-
Play Games

游戲中角色擁有的屬性值很多,運營多年的游戲,往往會有很多個成長線,每個屬性都有可能被N個成長線模塊增減數值。舉例當角色戴上武器時候hp+100點,卸下武器時HP-100點,這樣加減邏輯只有一處還比較好控制,如果某天有個特殊功能當被某技能攻擊時,角色武器會被擊落,這樣就會出現減數值的操作不止一處。如果... ...


游戲伺服器設計之屬性管理器

  游戲中角色擁有的屬性值很多,運營多年的游戲,往往會有很多個成長線,每個屬性都有可能被N個成長線模塊增減數值。舉例當角色戴上武器時候hp+100點,卸下武器時HP-100點,這樣加減邏輯只有一處還比較好控制,如果某天有個特殊功能當被某技能攻擊時,角色武器會被擊落,這樣就會出現減數值的操作不止一處。如果邏輯處理不當,比如擊落的時候沒有恰當的減數值,再次穿戴武器就導致屬性值加了兩邊,也就是玩家經常說的刷屬性。這種bug對游戲平衡性影響很大,反響很惡劣,bug又很難被測試發現。本文將介紹一種管理屬性的思路,最大限度的避免此類bug,如果出現bug,也能夠很好的排查。

設計思路

  刷屬性bug的核心原因是某功能的模塊數值加了N次,所以各個模塊加的屬性要被記錄,加過了必須不能重覆加。設計這樣的數據結構。

//!各個屬性對應一個總值
//!各個屬性對應各個模塊的分值
template<typename T>
class PropCommonMgr
{
public:
    typedef T ObjType;
    typedef int64_t (*functorGet)(ObjType);
    typedef void (*functorSet)(ObjType, int64_t);
    struct PropGetterSetter
    {
        PropGetterSetter():fGet(NULL), fSet(NULL){}        
        functorGet fGet;
        functorSet fSet;
        std::map<std::string, int64_t> moduleRecord;
    };
    void regGetterSetter(const std::string& strName, functorGet fGet, functorSet fSet){
        PropGetterSetter info;
        info.fGet = fGet;
        info.fSet = fSet;
        propName2GetterSetter[strName] = info;
    }
  public:
      std::map<std::string, PropGetterSetter>    propName2GetterSetter;
  };
  1. 關於數據結構的get和set,我們為每個屬性命名一個名字,這樣處理數據的時候會非常方便(比如道具配增加屬性等等),角色屬性有很多種,這裡不能一一定義,所以屬性管理器只是映射屬性,並不創建屬性值。通過regGetterSetter介面,註冊get和set的操作映射。為什麼不需要提供add和sub介面能,因為add和sub可以通過get和set組合實現。get和set的介面實現如下:
int64_t get(ObjType obj, const std::string& strName) {
        typename std::map<std::string, PropGetterSetter>::iterator it = propName2GetterSetter.find(strName);
        if (it != propName2GetterSetter.end() && it->second.fGet){
            return it->second.fGet(obj);
        }
        return 0;
    }
    bool set(ObjType obj, const std::string& strName, int64_t v) {
        typename std::map<std::string, PropGetterSetter>::iterator it = propName2GetterSetter.find(strName);
        if (it != propName2GetterSetter.end() && it->second.fSet){
            it->second.fSet(obj, v);
            return true;
        }
        return false;
    }
  1. 關於add和sub,前面提到要避免刷屬性,就必須避免重覆加屬性。所以每個模塊再加屬性前必須檢查一下是否該模塊已經加了屬性,如果加過一定要先減後加。因為每次模塊加屬性都記錄在屬性管理器中,那麼減掉的數值一定是正確的。這樣可以避免另外一種常見bug,如加了100,減的時候計算錯誤減了80,也會積少成多造成刷屬性。add和sub的代碼如下:
int64_t addByModule(ObjType obj, const std::string& strName, const std::string& moduleName, int64_t v) {
        typename std::map<std::string, PropGetterSetter>::iterator it = propName2GetterSetter.find(strName);
        if (it != propName2GetterSetter.end() && it->second.fGet && it->second.fSet){
            int64_t ret =it->second.fGet(obj);
            std::map<std::string, int64_t>::iterator itMod = it->second.moduleRecord.find(moduleName);
            if (itMod != it->second.moduleRecord.end()){
                ret -= itMod->second;
                itMod->second = v;
            }
            else{
                it->second.moduleRecord[moduleName] = v;
            }
            ret += v;
            it->second.fSet(obj, ret);
            return ret;
        }
        return 0;
    }
    int64_t subByModule(ObjType obj, const std::string& strName, const std::string& moduleName) {
        typename std::map<std::string, PropGetterSetter>::iterator it = propName2GetterSetter.find(strName);
        if (it != propName2GetterSetter.end() && it->second.fGet && it->second.fSet){
            int64_t ret =it->second.fGet(obj);
            std::map<std::string, int64_t>::iterator itMod = it->second.moduleRecord.find(moduleName);
            if (itMod == it->second.moduleRecord.end()){
                return ret;
            }
            ret -= itMod->second;
            it->second.moduleRecord.erase(itMod);
            it->second.fSet(obj, ret);
            return ret;
        }
        return 0;
    }
    int64_t getByModule(ObjType obj, const std::string& strName, const std::string& moduleName) {
        typename std::map<std::string, PropGetterSetter>::iterator it = propName2GetterSetter.find(strName);
        if (it != propName2GetterSetter.end() && it->second.fGet && it->second.fSet){
            int64_t ret =it->second.fGet(obj);
            std::map<std::string, int64_t>::iterator itMod = it->second.moduleRecord.find(moduleName);
            if (itMod != it->second.moduleRecord.end()){
                return itMod->second;
            }
        }
        return 0;
    }
    std::map<std::string, int64_t> getAllModule(ObjType obj, const std::string& strName) {
        std::map<std::string, int64_t> ret;
        typename std::map<std::string, PropGetterSetter>::iterator it = propName2GetterSetter.find(strName);
        if (it != propName2GetterSetter.end() && it->second.fGet && it->second.fSet){
            ret = it->second.moduleRecord;
        }
        return ret;
    }

  如上代碼所示,addByModule和subByModule必須提供模塊名,比如穿裝備的時候加血量:addByModule('HP', 'Weapon', 100),而卸下武器的時候只要subByModule('HP', 'Weapon'),因為屬性管理器知道減多少。

總結

  1. 屬性提供一個名字映射有很多好處,比如裝備配屬性,buff配屬性的,有名字相關聯會特別方便
  2. 提供一個get和set介面的映射,這樣屬性管理器就和具體的對象的屬性欄位解耦了。即使是現有的功能模塊也可以集成這個屬性管理器。
  3. 屬性的add和sub操作,都在屬性管理器中留下記錄,這樣即使出現問題,通過getByModule getAllModule兩個介面亦可以輔助查找問題。
  4. 屬性管理已經集成到H2Engine中,github地址: https://github.com/fanchy/h2engine

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、break語句 應用範圍:選擇結構和迴圈結構(多個for迴圈嵌套,跳出離break最近的一個for迴圈) 二、continue語句 應用範圍:只能用於迴圈結構(只作用於當前迴圈。結束本次迴圈,繼續下次迴圈) 結果是:2,4,6,8,10 ...
  •  對於java,其是不支持直接創建泛型數組的。當採用如下的方式去創建一個泛型數組時,其會出現錯誤,編譯無法通過的情況。 但是,在java中,其卻可以創建泛型類型的數組變數,如下所示的代碼,其並不會出現錯誤的情況。  一個問題是,我們想要創建一個泛型類型的數組變數,那麼應當怎麼辦? ...
  • assert 0.猜猜 (x < y and [x] or [y])[0] 實現什麼樣的功能? 若x<y為真,則返回x (參照兩邊為數的邏輯運算:A and B,若A為真,則返回B,反之,返回A A or B ,若A為真,則返回B,反之,返回A )(其中[X][0]表示列表中的第一個元素即X) 若x ...
  • 1. python特性(來自百度百科) 1) 解釋性:一個用編譯性語言比如C或C++寫的程式可以從源文件(即C或C++語言)轉換到一個你的電腦使用的語言(二進位代碼,即0和1)。這個過程通過編譯器和不同的標記、選項完成。 運行程式的時候,連接/轉載器軟體把你的程式從硬碟複製到記憶體中並且運行。而Py ...
  • 本文只討論二維空間中的曼哈頓距離與切比雪夫距離 曼哈頓距離 定義 設平面空間記憶體在兩點,它們的坐標為$(x1,y1)$,$(x2,y2)$ 則$dis=|x1-x2|+|y1-y2|$ 即兩點橫縱坐標差之和 煮個慄子 如圖所示,圖中$A,B$兩點的曼哈頓距離為$AC+BC=4+3=7$ 切比雪夫距離 ...
  • 前言: 之前將各層都拆分出去, 作為一個獨立的可替換的子模塊. 感覺比以前確實是靈活了一些. 不管是電商項目, 還是現在公司做的項目, 其中, 有很多的業務邏輯, 都是一樣的, 但是由於不在一個系統中, 大家需要進行重覆的工作. 有的拷貝還好, 但是有的, 沒法直接拷貝. 相當的蛋疼. 能不能, 將 ...
  • Dubbo簡介: Dubbo 是阿裡巴巴公司開源(以前不開源)的一個高性能優秀的服務框架, 使得應用可通過高性能的 RPC 實現服務的輸入和輸出功能, 可以和spring框架無縫集成. 那麼這裡, 啥是RPC啊? 這麼來說吧, 業務邏輯層和展現層不在同一臺電腦上, 甚至不在同一個城市, 當我展現層想 ...
  • 存在性和唯一性的證明以後再補。。。。 拉格朗日插值 拉格朗日插值,emmmm,名字挺高端的:joy: 它有什麼應用呢? 我們在FFT中講到過 設$n-1$次多項式為 $y=\sum_{i=0}^{n-1}a_i x^i$ 有一個顯然的結論:如果給定$n$個互不相同的點$(x,y)$,則該$n-1$次 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...