• 一、前言

  《碼神聯(lián)盟》是一款為技術(shù)人做的開源情懷游戲,每一種編程語言都是一位英雄。客戶端和服務(wù)端均使用C#開發(fā),客戶端使用Unity3D引擎,數(shù)據(jù)庫使用MySQL。這個(gè)MOBA類游戲是筆者在學(xué)習(xí)時(shí)期和客戶端美術(shù)策劃的小伙伴一起做的游戲,筆者主要負(fù)責(zé)游戲服務(wù)端開發(fā),客戶端也參與了一部分,同時(shí)也是這個(gè)項(xiàng)目的發(fā)起和負(fù)責(zé)人。這次主要分享這款游戲的服務(wù)端相關(guān)的設(shè)計(jì)與實(shí)現(xiàn),從整體的架構(gòu)設(shè)計(jì),到服務(wù)器網(wǎng)絡(luò)通信底層的搭建,通信協(xié)議、模型定制,再到游戲邏輯的分層架構(gòu)實(shí)現(xiàn)。同時(shí)這篇博客也沉淀了筆者在游戲公司實(shí)踐五個(gè)月后對游戲架構(gòu)與設(shè)計(jì)的重新審視與思考。

  這款游戲自去年完成后筆者曾多次想寫篇博客來分享,也曾多次停筆,只因總覺得靈感還不夠積淀還不夠思考還不夠,現(xiàn)在終于可以跨過這一步和大家分享,希望可以帶來的是干貨與誠意滿滿。由于目前關(guān)于游戲服務(wù)端相關(guān)的介紹文章少之又少,而為數(shù)不多的幾篇也都是站在游戲服務(wù)端發(fā)展歷史和架構(gòu)的角度上進(jìn)行分享,很少涉及具體的實(shí)現(xiàn),這篇文章我將嘗試多從實(shí)現(xiàn)的層面上加以介紹,所附的代碼均有詳盡注釋,篇幅較長,可以關(guān)注收藏后再看。學(xué)習(xí)時(shí)期做的項(xiàng)目可能無法達(dá)到工業(yè)級,參考了github上開源的C#網(wǎng)絡(luò)框架,筆者在和小伙伴做這款游戲時(shí)農(nóng)藥還沒有現(xiàn)在這般火。  : ) 

  • 二、服務(wù)器架構(gòu)

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

  上圖為這款游戲的服務(wù)器架構(gòu)和主要邏輯流程圖,筆者將游戲的代碼實(shí)現(xiàn)分為三個(gè)主要模塊:Protocol通信協(xié)議、NetFrame服務(wù)器網(wǎng)絡(luò)通信底層的搭建以及LOLServer游戲的具體邏輯分層架構(gòu)實(shí)現(xiàn),下面將針對每個(gè)模塊進(jìn)行分別介紹。

  • 三、通信協(xié)議

  iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

  先從最簡單也最基本的通信協(xié)議部分說起,我們可以看到這部分代碼主要分為xxxProtocol、xxxDTO和xxxModel、以及xxxData四種類型,讓我們來對它們的作用一探究竟。

  • 1.Protocol協(xié)議

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)


 LOLServer\Protocol\Protocol.cs

using System; using System.Collections.Generic; using System.Text; namespace GameProtocol {    public class Protocol     {        public const byte TYPE_LOGIN = 0;//登錄模塊        public const byte TYPE_USER = 1;//用戶模塊        public const byte TYPE_MATCH = 2;//戰(zhàn)斗匹配模塊        public const byte TYPE_SELECT = 3;//戰(zhàn)斗選人模塊        public const byte TYPE_FIGHT = 4;//戰(zhàn)斗模塊     } }

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

  從上述的代碼舉例可以看到,在Protocol協(xié)議部分,我們主要是定義了一些常量用于模塊通信,在這個(gè)部分分別定義了用戶協(xié)議、登錄協(xié)議、戰(zhàn)斗匹配協(xié)議、戰(zhàn)斗選人協(xié)議以及戰(zhàn)斗協(xié)議。

  • 2.DTO數(shù)據(jù)傳輸對象

  DTO即數(shù)據(jù)傳輸對象,表現(xiàn)層與應(yīng)用層之間是通過數(shù)據(jù)傳輸對象(DTO)進(jìn)行交互的,需要了解的是,數(shù)據(jù)傳輸對象DTO本身并不是業(yè)務(wù)對象。數(shù)據(jù)傳輸對象是根據(jù)UI的需求進(jìn)行設(shè)計(jì)的,而不是根據(jù)領(lǐng)域?qū)ο筮M(jìn)行設(shè)計(jì)的。比如,User領(lǐng)域?qū)ο罂赡軙恍┲T如name, level, exp, email等信息。但如果UI上不打算顯示email的信息,那么UserDTO中也無需包含這個(gè)email的數(shù)據(jù)。

  簡單來說Model面向業(yè)務(wù),我們是通過業(yè)務(wù)來定義Model的。而DTO是面向界面UI,是通過UI的需求來定義的。通過DTO我們實(shí)現(xiàn)了表現(xiàn)層與Model之間的解耦,表現(xiàn)層不引用Model,如果開發(fā)過程中我們的模型改變了,而界面沒變,我們就只需要改Model而不需要去改表現(xiàn)層中的東西。

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

using System; using System.Collections.Generic; using System.Text; namespace GameProtocol.dto {     [Serializable]    public class UserDTO    {        public int id;//玩家ID 唯一主鍵        public string name;//玩家昵稱        public int level;//玩家等級        public int exp;//玩家經(jīng)驗(yàn)        public int winCount;//勝利場次        public int loseCount;//失敗場次        public int ranCount;//逃跑場次        public int[] heroList;//玩家擁有的英雄列表        public UserDTO() { }        public UserDTO(string name, int id, int level, int win, int lose, int ran,int[] heroList)        {            this.id = id;            this.name = name;            this.winCount = win;            this.loseCount = lose;            this.ranCount = ran;            this.level = level;            this.heroList = heroList;        }     } }

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

  • 3.Data屬性配置表

  這部分的實(shí)現(xiàn)主要是為了將程序功能與屬性配置分離,后面可以由策劃來配置這部分內(nèi)容,由導(dǎo)表工具自動(dòng)生成配表,從而減輕程序的開發(fā)工作量,擴(kuò)展游戲的功能。

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

using System; using System.Collections.Generic; using System.Text; namespace GameProtocol.constans {     /// <summary>     /// 英雄屬性配置表     /// </summary>    public class HeroData     {        public static readonly Dictionary<int, HeroDataModel> heroMap = new Dictionary<int, HeroDataModel>();        /// <summary>        /// 靜態(tài)構(gòu)造 初次訪問的時(shí)候自動(dòng)調(diào)用        /// </summary>        static HeroData() {            create(1, "西嘉迦[C++]", 100, 20, 500, 300, 5, 2, 30, 10, 1, 0.5f, 200,200, 1, 2, 3, 4);            create(2, "派森[Python]", 100, 20, 500, 300, 5, 2, 30, 10, 1, 0.5f, 200, 200, 1, 2, 3, 4);            create(3, "扎瓦[Java]", 100, 20, 500, 300, 5, 2, 30, 10, 1, 0.5f, 200, 200, 6, 2, 3, 4);            create(4, "琵欸赤貔[PHP]", 100, 20, 500, 300, 5, 2, 30, 10, 1, 0.5f, 200, 200, 3, 2, 3, 4);        }        /// <summary>        /// 創(chuàng)建模型并添加進(jìn)字典        /// </summary>        /// <param name="code"></param>        /// <param name="name"></param>        /// <param name="atkBase"></param>        /// <param name="defBase"></param>        /// <param name="hpBase"></param>        /// <param name="mpBase"></param>        /// <param name="atkArr"></param>        /// <param name="defArr"></param>        /// <param name="hpArr"></param>        /// <param name="mpArr"></param>        /// <param name="speed"></param>        /// <param name="aSpeed"></param>        /// <param name="range"></param>        /// <param name="eyeRange"></param>        /// <param name="skills"></param>        private static void create(int code,            string name,            int  atkBase,            int  defBase,            int  hpBase,            int  mpBase,            int  atkArr,            int  defArr,            int  hpArr,            int  mpArr,            float speed,            float aSpeed,            float range,            float eyeRange,            params int[] skills) {                HeroDataModel model = new HeroDataModel();                model.code = code;                model.name = name;                model.atkBase = atkBase;                model.defBase = defBase;                model.hpBase = hpBase;                model.mpBase = mpBase;                model.atkArr = atkArr;                model.defArr = defArr;                model.hpArr = hpArr;                model.mpArr = mpArr;                model.speed = speed;                model.aSpeed = aSpeed;                model.range = range;                model.eyeRange = eyeRange;                model.skills = skills;                heroMap.Add(code, model);        }     }        public partial class HeroDataModel        {            public int code;//策劃定義的唯一編號            public string name;//英雄名稱            public int atkBase;//初始(基礎(chǔ))攻擊力            public int defBase;//初始防御            public int hpBase;//初始血量            public int mpBase;//初始藍(lán)            public int atkArr;//攻擊成長            public int defArr;//防御成長            public int hpArr;//血量成長            public int mpArr;//藍(lán)成長            public float speed;//移動(dòng)速度            public float aSpeed;//攻擊速度            public float range;//攻擊距離            public float eyeRange;//視野范圍            public int[] skills;//擁有技能        }      }

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

  • 四、服務(wù)器通信底層搭建

  這部分為服務(wù)器的網(wǎng)絡(luò)通信底層實(shí)現(xiàn),也是游戲服務(wù)器的核心內(nèi)容,下面將結(jié)合具體的代碼以及代碼注釋一一介紹底層的實(shí)現(xiàn),可能會涉及到一些C#的網(wǎng)絡(luò)編程知識,對C#語言不熟悉沒關(guān)系,筆者對C#的運(yùn)用也僅僅停留在使用階段,只需通過C#這門簡單易懂的語言來窺探整個(gè)服務(wù)器通信底層搭建起來的過程,來到我們的NetFrame網(wǎng)絡(luò)通信框架,這部分干貨很多,我將用完整的代碼和詳盡的注釋來闡明其意。

  iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

  • 1.四層Socket模型

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

 

  將SocketModel分為了四個(gè)層級,分別為:

  (1)type:一級協(xié)議 用于區(qū)分所屬模塊,如用戶模塊

  (2)area:二級協(xié)議 用于區(qū)分模塊下的所屬子模塊,如用戶模塊的子模塊為道具模塊1、裝備模塊2、技能模塊3等

 ?。?)command:三級協(xié)議  用于區(qū)分當(dāng)前處理邏輯功能,如道具模塊的邏輯功能有“使用(申請/結(jié)果),丟棄,獲得”等,技能模塊的邏輯功能有“學(xué)習(xí),升級,遺忘”等;

 ?。?)message:消息體 當(dāng)前需要處理的主體數(shù)據(jù),如技能書

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace NetFrame.auto {    public class SocketModel     {        /// <summary>        /// 一級協(xié)議 用于區(qū)分所屬模塊        /// </summary>        public byte type {get;set;}        /// <summary>        /// 二級協(xié)議 用于區(qū)分 模塊下所屬子模塊        /// </summary>        public int area { get; set; }        /// <summary>        /// 三級協(xié)議  用于區(qū)分當(dāng)前處理邏輯功能        /// </summary>        public int command { get; set; }        /// <summary>        /// 消息體 當(dāng)前需要處理的主體數(shù)據(jù)        /// </summary>        public object message { get; set; }        public SocketModel() { }        public SocketModel(byte t,int a,int c,object o) {            this.type = t;            this.area = a;            this.command = c;            this.message = o;        }        public T GetMessage<T>() {            return (T)message;        }     } }

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

   同時(shí)封裝了一個(gè)消息封裝的方法,收到消息的處理流程如圖所示:

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

  • 2.對象序列化與反序列化為對象  

  序列化: 將數(shù)據(jù)結(jié)構(gòu)或?qū)ο筠D(zhuǎn)換成二進(jìn)制串的過程。

  反序列化:將在序列化過程中所生成的二進(jìn)制串轉(zhuǎn)

http://www.cnblogs.com/liumt/p/7222940.html