一:环境搭建
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手册)。