一:環境搭建
1. 確保已經下載過KBEngine服務端引擎,若是沒有下載請先下載
下載服務端源碼(KBEngine):
https://github.com/kbengine/kbengine/releases/latest
編譯(KBEngine):
http://www.kbengine.org/docs/build.html
安裝(KBEngine):
http://www.kbengine.org/docs/installation.html
2. 下載unity3d demo源碼(kbengine_unity3d_demo)
https://github.com/kbengine/kbengine_unity3d_demo/releases/latest
3. 下載kbengine客戶端插件與服務端Demo資產庫:
* 使用git命令行,進入到kbengine_unity3d_demo目錄執行:
git submodule update --init --remote
* 或者使用 TortoiseGit(選擇菜單): TortoiseGit -> Submodule Update:
* 也能夠手動下載kbengine客戶端插件與服務端Demo資產庫
客戶端插件下載:
https://github.com/kbengine/kben ... /archive/master.zip
下載後請將其解壓縮,插件源碼請放置在: Assets/plugins/kbengine/kbengine_unity3d_plugins
服務端資產庫下載:
https://github.com/kbengine/kbengine_demos_assets/releases/latest
下載後請將其解壓縮,並將目錄文件放置於服務端引擎根目錄"kbengine/"之下,以下圖:
4. 拷貝服務端資產庫"kbengine_demos_assets"到服務端引擎根目錄"kbengine/"之下,以下圖:
二:配置Demo(可選):
改變登陸IP地址與端口(注意:關於服務端端口部分參看http://www.kbengine.org/cn/docs/installation.html):
kbengine_unity3d_demo\Scripts\kbe_scripts\clientapp.cs -> ip
kbengine_unity3d_demo\Scripts\kbe_scripts\clientapp.cs -> port
三:啓動服務器:
確保「kbengine_unity3d_demo\kbengine_demos_assets」已經拷貝到KBEngine根目錄:
參考上方章節:開始使用啓動腳本啓動服務端:
Windows:
kbengine\kbengine_demos_assets\start_server.bat
Linux:
kbengine\kbengine_demos_assets\start_server.sh
檢查啓動狀態:
若是啓動成功將會在日誌中找到"Components::process(): Found all the components!"。
任何其餘狀況請在日誌中搜索"ERROR"關鍵字,根據錯誤描述嘗試解決。
(更多參考: http://www.kbengine.org/docs/startup_shutdown.html)
四:啓動客戶端:
直接在Unity3D編輯器啓動或者編譯後啓動
(編譯客戶端:Unity Editor -> File -> Build Settings -> PC, MAC & Linux Standalone.)
五:生成導航網格(可選):
服務端使用Recastnavigation在3D世界尋路,recastnavigation生成的導航網格(Navmeshs)放置於:
kbengine\demo\res\spaces\*
在Unity3D中使用插件生成導航網格(Navmeshs):
https://github.com/kbengine/unity3d_nav_critterai
六:演示截圖:
七:服務端資產庫文件夾結構
http://kbengine.org/cn/docs/concepts/directorys.html
看assets, 注意:demo使用的不是默認的assets資產目錄,而是上面章節下載的kbengine_demos_assets,但文件夾結構與意義是一致的。
八:客戶端文件夾結構
kbengine_unity3d_demo
-> Assets // Unity3d資產庫
-> Plugins
-> kbengine // KBEngine插件層(包含了網絡消息處理、客戶端實體維護、與服務端對接層)
-> Scripts
-> kbe_scripts // 客戶端邏輯腳本層(https://github.com/kbengine/kben ... e_scripts/README.md)
-> Account.cs // 對應於服務端的帳號實體的客戶端部分實現
-> Avatar.cs // 對應於服務端的角色實體的客戶端部分實現
-> clientapp.cs // 按照服務端的概念cellapp、baseapp、etc,這裏咱們抽象出一個clientapp
-> Combat.cs // 對應於服務端的def interfaces/Combat的客戶端部分實現
-> GameObject.cs // 對應於服務端的def interfaces/GameObject的客戶端部分實現
-> Gate.cs // 對應於服務端的Gate實體的客戶端部分實現
-> Monster.cs // 對應於服務端的Monster實體的客戶端部分實現
-> NPC.cs // 對應於服務端的NPC實體的客戶端部分實現
-> Skill.cs // 一個簡單的不能再簡單的技能執行類,服務端cell/skill下面也有,而客戶端主要是進行一些檢查
-> SkillBox.cs // 玩家的技能列表,對應於服務端的def interfaces/Skillbox的客戶端部分實現
-> SkillObject.cs // 技能對象(施法者、目標、受術者等),服務端cell/skill下面也有
-> u3d_scripts // 客戶端UI等表現層
-> UI.cs // 處理UI部分
-> World.cs // 處理場景世界部分
-> GameEntity.cs // 全部服務端同步過來的實體在表現層都必須繼承該類,完成統一的表現(頭頂名稱、血條等)與控制(實體狀態、移動)
------------------------------------------
基本設計結構:
-遊戲-
| |
表現層u3d_scripts(UI && 世界) KBE層kbe_scripts(插件 && 邏輯)
1: 表現層與KBE層能夠配置爲不一樣線程也能配置爲同一個線程跑(單線程)
2: 表現層與KBE層使用事件交互, 向KBE層觸發的事件使用fireIn(...),KBE層向外部觸發的事件使用fireOut(...)。 那麼表現層想要監聽KBE觸發的Out事件,須要註冊監聽Event.registerOut, KBE須要監聽外部觸發進來的事件則反之。
3: 使用unity3D插件與服務端配套則服務端中的scripts/client文件夾能夠忽略(https://github.com/kbengine/kben ... e_scripts/README.md)html
九:遊戲配置
服務端demo全部的配置都存放於kbengine_demos_assets\scripts\data之下。
scripts\data\
d_avatar_inittab.py // 角色初始化表, 用於新創建的角色設置初始值, 由kbengine\kbe\tools\xlsx2py\rpgdemo\avatar_init.bat導出。
d_dialogs.py // NPC對話表, 其中'menu1'對於的是一個對話協議的ID,服務端根據不一樣的協議ID執行不一樣的對話功能, 由kbengine\kbe\tools\xlsx2py\rpgdemo\dialogs.bat導出。
d_entities.py // 實體類型表,描述某類型怪移動速度,攻擊力等,由kbengine\kbe\tools\xlsx2py\rpgdemo\NPC.bat導出。
d_skills.py // 技能表,描述某類型技能斷定條件,輸出等,由kbengine\kbe\tools\xlsx2py\rpgdemo\skils.bat導出。
d_spaces.py // 場景副本表,描述space是大地圖仍是副本,以及地圖名稱等,由kbengine\kbe\tools\xlsx2py\rpgdemo\spaces.bat導出。
d_spaces_spawns.py // NPC、Monster等出生點信息,目前是手填的,也能夠採用工具布點導出。
spawnpoints\
xinshoucun_spawnpoints.xml // 這個出生點信息主要用於warring這個demo,(NPC、Monster等出生點信息,採用Unity3d布點導出, 能夠在unity打開warring這個demo,
// 在unity3d(菜單上)->ublish->Build Publish AssetBundles(打包全部須要動態加載資源),而後在Assets->StreamingAssets目錄下會獲得 "場景名稱_spawnpoints.xml"的出生點表)。
十:建立帳號
客戶端部分:
1: kbengine_unity3d_demo\Assets\Scripts\u3d_scripts\UI.cs
1.1 點擊登陸按鈕致使createAccount()被調用, createAccount中向KBE層觸發了一個建立帳號事件,參數是帳號名與密碼。
注意:KBEngine插件kbengine_unity3d_demo\Assets\Plugins\kbengine\kbengine_unity3d_plugins\KBEngine.cs中已經註冊了這個「createAccount」事件,對應於KBEngineApp.createAccount函數。git
2. 事件在KBE插件中kbengine_unity3d_demo\Assets\Plugins\kbengine\kbengine_unity3d_plugins\KBEngine.cs, KBEngineApp.process()中被正式處理github
3. 建立帳號函數被調用, createAccount_loginapp函數表示請求向服務端loginapp進程要求建立一個帳號,而此時可能尚未鏈接服務器,須要先鏈接,若是已經鏈接上了則向loginapp發送一個包「bundle.send」。
能夠看到向Bundle中寫入了相關須要的數據,而Bundle會將數據序列化成二進制流,服務端會採用相同的協議將其歸原並將調用服務端協議所綁定的方法(後面會講到服務端具體方法)。數據庫
建立返回結果:
UI.cs -> onCreateAccountResult
服務端部分:
1. 經過上面能夠得知客戶端向服務端發送了一條建立帳號的協議, 協議名稱爲「Loginapp_reqCreateAccount」(注意,全部的協議名稱都能在服務端找到對應的方法, Loginapp_表明了協議的做用域僅爲Loginapp, 方法名稱爲reqCreateAccount)服務器
服務端解析出了帳號名與密碼,在_createAccount函數中會將這條請求最終送到dbmgr,dbmgr檢查以後決定是否建立數據庫帳號,並最終將結果返回到loginapp,而後由loginapp將結果中轉至客戶端。
十一:登陸帳號
1: kbengine_unity3d_demo\Assets\Scripts\u3d_scripts\UI.cs, 向KBE層觸發了登錄事件網絡
2: kbengine_unity3d_demo\Assets\Plugins\kbengine\kbengine_unity3d_plugins\KBEngine.cs, 插件觸發登錄函數,並最終向loginapp發送了一個登錄包「Loginapp_login」數據結構
服務端部分:
1:服務端loginapp.cpp中「void Loginapp::login(Network::Channel* pChannel, MemoryStream& s)」被觸發, 這個函數進行了一系列的檢查,
肯定合法後向dbmgr發送一個登錄請求包「(*pBundle).newMessage(DbmgrInterface:nAccountLogin);」, dbmgr也會進行一系列的檢查並將登錄結果返回到loginapp。多線程
1.1: loginapp獲得dbmgr的登陸合法結果後向baseappmgr發送了分配網關(baseapp)請求(registerPendingAccountToBaseapp), 一般是負載較低的一個baseapp進程.app
1.2:baseappmgr最終返回所分配的baseapp的ip地址等信息,loginapp將其轉發給客戶端(登陸成功協議onLoginSuccessfully,包含baseapp的ip和端口信息)dom
2: 客戶端插件獲得返回結果後調用KBEngineApp.cs->login_baseapp()函數開始正式登陸到baseapp。
3:baseapp收到登陸請求
進行了一系列的檢查,包括:帳號是否已經在線,是否能夠在這裏登陸等等。
當檢查合法後,向dbmgr發送了一個查詢帳號信息的請求「DbmgrInterface::queryAccount」,dbmgr將查詢到的帳號數據(包括屬性等)返回到baseapp, Baseapp:nQueryAccountCBFromDbmgr
當函數結果爲合法時,根據配置中定義的帳號實體腳本名稱「g_serverConfig.getDBMgr().dbAccountEntityScriptType」建立了Account實體, 同時還建立了一個clientMailbox,帳號實體中調用clientMailbox->方法()便可與客戶端通信了。
Account實體被建立後, 首先__init__被調用, 接着onEntitiesEnabled被調用, 此時實體正式可用了。
帳號登錄成功後, 客戶端Account.cs中會調用__init__() -> baseCall("reqAvatarList");來請求得到角色列表,
UI.cs中onReqAvatarList獲得結果。
十二:建立角色與選擇角色進入遊戲
1. 建立角色UI.cs -> void onSelAvatarUI()中
account.reqCreateAvatar(1, stringAvatarName);
UI.cs中onCreateAvatarResult獲得結果。
2.選擇角色進入遊戲
UI.cs -> onSelAvatarUI()中
account.selectAvatarGame(selAvatarDBID);
這裏使用角色的數據庫ID做爲標識,服務端上Account實體有角色列表屬性,角色列表的數據結構大概爲
AvatarList <Dict<AvatarDBID(UINT64), INFOS>>
十三:建立世界(大地圖與副本)
1. 建立世界管理器服務端啓動以後,baseapp與cellapp準備完畢、準備關閉等事件都會通知到kbengine_defs.xml配置中指定的個性化腳本。kbe默認個性化腳本爲kbengine.py, baseapp進程準備好以後會調用kbengine.py的onBaseAppReady回調函數, demo在這個函數中斷定是否爲第一個啓動的baseapp(假如啓動了不少baseapps),
若是是第一個baseapp,腳本建立了一個世界管理實體「spaces」:
2. 世界管理器建立出全部的場景
在spaces.py中, spaces經過initAlloc函數根據配置中scripts/data/d_spaces.py建立出space實體,space實體描述的是一個抽象空間,一個空間能夠被邏輯定義爲大地圖、場景、房間、宇宙等等。
SpaceAlloc: 普通地圖,能夠理解爲大地圖,但整個世界中只能有一個這樣類型的地圖。
SpaceAllocDuplicate:副本地圖,能夠複製出不少個
上面函數註冊了一個定時器, 這裏是定時器的回調, 每一秒回調一次。
self._spaceAllocs[spaceUType].init(), 這裏真正開始建立這些space實體, 裏面調用的createBaseAnywhere函數來建立實體, 若是啓動了多個baseapp這個函數根據負載狀況將實體選擇到合適的進程中建立。
Space實體建立出來以後,此時尚未真正建立出空間, 這個實體僅僅是將要與某個真正空間關聯的實體, 能夠經過它來操控那個空間。
但空間只能在cellapp上存在, 所以咱們須要調用API讓實體在cell上建立出一個空間,並在cell上建立出一個實體與空間關聯, 這個實體就像一個空間的句柄。
此功能由createInNewSpace完成, __init__能夠理解爲Space的構造函數。
3. 爲這個抽象的空間增長几何數據
有指定幾何數據的空間能夠被看作是一個特定的場景, 這些幾何數據與客戶端對應的場景表現相關聯, 例如:導航網格(navmesh), 服務端經過這些數據讓NPC進行正確的移動,碰撞等。
上面Space建立cell部分以後, cell上的Space._init__也會被調用, 其中addSpaceGeometryMapping API接口完成幾何數據加載工做
(注意:爲了加載大量數據不讓進程卡頓,這個數據加載是多線程的,它會經過一些回調來告訴開發者加載狀態,具體參考API手冊)。