我们一起来学KBEngine吧


目录

第一部分安装与启动

第二部分项目Demo制作与启动

1.配置Entity

第一步:Entity声明

第二步:Entity的配置

1:新建def配置文件

2:添加方法声明

2. 实体的Python实现

第一步:创建文件

第二步:实现def中的say方法

第三步:使用账户入口对实体进行创建

恭喜你,第一个Entity已经创建完毕!

3.创建第一个Space空间

第一步:Entity声明

第二步:def配置

第三步:Python实现

1. base部分的实现

2. cell部分的实现

第四步:Baseapp就绪时,创建Space空间

修改kbemain.py

最后一步:让我们的FirstEntity进入空间

恭喜你,FirstEntity已经能进入到一个Space空间里了!

客户端的实现,我只会Unity3D

本节会分为如下几个部分进行讲解:

1. 修改SDK生成路径:

2. 执行工具,生成SDK代码:

3. 文件夹结构介绍:

第二步:实现Client部分

1. 客户端设计概述

2. 实现scene_login登录场景

3. 实现FirstEntityBase

4. 实现scene_world空间场景

第三步:验证

1. 启动引擎

2. 运行客户端

3. Hello world

恭喜你,Unity3D的客户端实现已完成!


第一部分安装与启动

1.下载服务源码 https://github.com/kbengine/kbengine/releases/tag/v2.3.3

2.安装和启动 https://www.comblockengine.com/docs/1.0/install/index/

2.安装和启动 https://kbengine.github.io//docs/build.html

3.检查MFC模块是否安装了,先编译sln,结果是这样的========== 全部重新生成: 成功 48 个,失败 0 个,跳过 0 个 ==========

4.查看失败的,重新编译

5.Error报错 找不到exe的话,用编辑器修改server_start.bat,把cd .. 修改成 cd ../../../../..,根据本地实际情况修改。

6.根据教程一步一步安装mysql,创建账号,创建仓库。

7.mysql 1045的话 在bin同级目录,新建my.ini 写下面,然后登录,要输入密码就直接回车

[mysqld]
skip-grant-tables

# 允许最大连接数
max_connections=200
# Remove leading # and set to the amount of RAM for the most important data
# cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%.
# innodb_buffer_pool_size = 128M

# Remove leading # to turn on a very important data integrity option: logging
# changes to the binary log between backups.
# log_bin

# These are commonly set, remove the # and set as required.
 basedir = D:\sw\MYSQL\mysql-5.7.1-m11-winx64\mysql-5.7.1-m11-winx64
 datadir = D:\sw\MYSQL\mysql-5.7.1-m11-winx64\mysql-5.7.1-m11-winx64\data
 port = 3306
 server_id = 127.0.0.1



# Remove leading # to set options mainly useful for reporting servers.
# The server defaults are faster for transactions and fast SELECTs.
# Adjust sizes as needed, experiment to find the optimal values.
# join_buffer_size = 128M
# sort_buffer_size = 2M
# read_rnd_buffer_size = 2M 

sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES 

 

8.创建系统账号,安装python

9.测试服务器启动,没问题才进行下一阶段。

10.点击new_assets.bat 后,改名字,把里面的server_start.bat打开,改回去,cd..这样子。真狗


第二部分项目Demo制作与启动

我们假设需要这么一个实体:它可以向服务器say hello,当服务器收到后广播给所有客户端。

我们把该Entity命名为FirstEntity

 

1.配置Entity

第一步:Entity声明

很简单,在{项目资产库}/scripts/entities.xml文件中,写入该Entity相关字段:

<root>
    <FirstEntity hasClient="true"></FirstEntity>
</root>

 本教程中的FirstEntity实体是有客户端部分的,所以这里,我们的FirstEntity实体是指明hasClient="true"

 

第二步:Entity的配置

Entity的配置,其实就是根据业务需求,对def配置文件的编写,让引擎和各个服务器组件知道该Entity有哪些方法和属性

1:新建def配置文件

“scripts/entity_defs下,新建一个名为FirstEntity.def的文件,该文件就是对应实体FirstEntity的def配置文件(不同实体需要根据其名字新增不同的def文件,如Monster实体必须在该文件夹下有Monster.def配置文件,否则启动服务器时会出错)。def文件与xml文件相似,编写的语法也和xml相似

2:添加方法声明

根据例子中的设计需求,我们需要增加say hello的方法,在FirstEntity.def文件中写入如下内容

<root>
    <!--cell上的方法声明-->
    <CellMethods>
        <!--say hello指令-->
        <say>
            <!--该参数可以让此方法暴露给客户端调用-->
            <Exposed/>
            <!--hello的内容-->
            <Arg>   UNICODE         </Arg>
        </say>
    </CellMethods>
    <!--客户端上回调的方法声明-->
    <ClientMethods>
        <!--当实体进入世界空间后,通知客户端-->
        <onEnter>
        </onEnter>
        <!--通知客户端,广播say hello-->
        <onSay>
            <Arg>   UNICODE         </Arg>
        </onSay>
    </ClientMethods>
</root>

其中,root是根节点,类似于xml的根节点。

CellMethods内是该实体在cell上声明的方法,这里我们声明一个叫做say的方法。标签名为方法名,标签Arg是方法参数的类型的声明区域,如UNICODE表示字符串,用来指明hello的内容;Exposed标签可以把该方法暴露给客户端调用。这样一来,我们的客户端可以向服务器say hello了。

ClientMethods内是该实体在客户端上的方法声明,这里我们声明一个叫做onEnter的方法,当实体进入世界空间后,通知客户端。同时我们声明了一个叫做onSay的方法,在收到say的时候,会广播给所有客户端。

OK,到这里,我们的FirstEntity def配置基本完工了

 

2. 实体的Python实现

第一步:创建文件

base部分的脚本必须创建在scripts/base文件夹下。我们来创建一个名为FirstEntity.py的文件(注意:def中使用FirstEntity,这里也要用FirstEntity,且区分大小写

Tips:使用“创建资产库”工具时会默认生成kbemain.py和Account.py,其中kbemain.py是base的入口脚本,,在章节脚本篇-app的入口脚本 中会详细介绍。Account.py是用来控制账户实体的。这里我们先忽略它们。

在FirstEntity.py文件中写入:

# -*- coding: utf-8 -*-
import KBEngine
from KBEDebug import *


class FirstEntity(KBEngine.Proxy):
    """
    第一个实体的base部分的实现
    """
    def __init__(self):
        KBEngine.Entity.__init__(self)

不要抄错了(本人就是抄错了,这给我后面埋了个九天神雷,IDE又不报错,最后启动服务器也能成功,服务器红色报错一闪而过,最后在客户端发送hello的时候一直报错)

什么是Proxy?

Proxy是一种特殊的Entity。客户端要和服务端保持连接,就需要使用Proxy,在服务端上它代表一个客户端连接。

接着往下走,我们来实现def中声明的方法

第二步:实现def中的say方法

由于say方法声明在CellMethods中,需要在scripts/cell中也同样创建一个FirstEntity.py的文件,写入:

# -*- coding: utf-8 -*-
import KBEngine
from KBEDebug import *


class FirstEntity(KBEngine.Entity):
    """
    第一个实体的cell部分的实现
    """

    def __init__(self):
        KBEngine.Entity.__init__(self)
        # 通知客户端,本实体已进入
        self.client.onEnter()

    def say(self, callerID, content):
        """
        实现了CellMethods中的say方法,content即为申明的类型为UNICODE的参数
        :param callerID: 调用者ID
        :param content: say的内容
        :return:
        """
        INFO_MSG("FirstEntity::say")
        # 广播给所有客户端的onSay方法
        self.allClients.onSay("Entity: " + str(self.id) + " " + content)

 首先我们来看一下__init__中,执行了self.client.onEnter()。之前在def的ClientMethods中对onEnter进行了申明,所以这边只需简单的通过client.onEnter即可通知到本实体关联的客户端

我再来看say方法。其使用def中定义的名字作为方法名,由于该方法是可以给客户端调用,所以def声明时使用了<Exposed/>标签,而代码中也需要新增一个参数callerID与之对应,其由引擎来指明是谁在调用该远程方法。除此之外,其他参数的数量要和def中定义的一致,如def中申明了一个UNICODE的参数,这里参数content与之对应,表示接收到的say的内容。

这样,我们的客户端可以向FirstEntity实体进行say的请求了。

INFO_MSG,是日志输出。

self.allClients.onSay,是广播给所有客户端的onSay方法。之前在def的ClientMethods中也有对onSay进行了申明,所以可以通过allClients属性进行广播。

好了,至此,我们第一个Entity实体已经制作完成了。
之前的工作都是在进行一个Entity的制作,但还没有被引擎创建出来。下面介绍下该实体对象是如何被创建。

第三步:使用账户入口对实体进行创建

使用账户入口实体,是为了当客户端连接服务器时就可以创建出我们FirstEntity实体,并与客户端保持连接

之前在base部分创建FirstEntity脚本时,继承的是KBEngine.Proxy,是为了简化本教程,把账户入口实体与我们第一个实体合二为一。此处需对引擎的配置做出一些调整,方法如下:

1、打开{项目资产库}/res/server/kbengine.xml

2、找到dbmgr->account_system标签,在其下添加accountEntityScriptType,并设置为FirstEntity。结果如下图:

关于账户入口,详情请参见《用户入口实体(Account)》文档。
关于引擎配置,请参见《kbengine.xml配置》文档。

恭喜你,第一个Entity已经创建完毕!

做得好,我们第一个Entity已经完成。

那从客户端上如何发送say hello,并收到服务器的广播呢?首先,需要把该实体放入一个空间(实体只有被放置在一个空间内,才能进行空间内的交互,包括广播消息、位置移动等)。

下面会我们来看看如何创建一个空间,并把FirstEntity放入该空间。

 

3.创建第一个Space空间

上一小节中,我们成功创建了一个FirstEntity实体,但是它没有放入到某个空间中(不在一个空间时,在客户端上是无法和它进行互动、通讯的)。本文会介绍如何创建一个Space空间的实体,使我们的FirstEntity在空间内互动

Space空间是cell上的一个抽象的概念,每个Space相当于一个空间,它可以是一个场景、一个副本、一个房间等等等

步骤导读:

 

第一步:Entity声明

{项目资产库}/scripts/entities.xml文件中,添加FirstSpace:

<root>
    <FirstEntity hasClient="true"></FirstEntity>
    <FirstSpace></FirstSpace>
</root>

本教程中的FirstSpace实体是不需要客户端部分的,所以这里,没有指明hasClient字段

 

第二步:def配置

“scripts/entity_defs”下,新建一个名为FirstSpace.def的文件,写入:

<root>
    <!-- BaseApp上的远程方法 -->
    <BaseMethods>
    </BaseMethods>
    <!-- CellApp上的远程方法 -->
    <CellMethods>
    </CellMethods>
</root>

该实体下使用了最简单的申明方式,没有申明任何远程方法

 

第三步:Python实现

1. base部分的实现

“scripts/base”文件夹下创建FirstSpace.py文件。

class FirstSpace(KBEngine.Space):
    """
    FirstSpace的base部分,
    注意:它是一个实体,并不是真正的space,真正的space存在于cellapp的内存中,通过这个实体与之关联并操控space。
    """
    def __init__(self):
        KBEngine.Space.__init__(self)
        # 存储在globalData中,方便获取
        KBEngine.globalData["FirstSpace"] = self

1、空间实体会继承自KBEngine.Space

2、我们在__init__时直接调用KBEngine.globalData["FirstSpace"] = self,使用全局变量globalData字典来保存自己,方便在其他地方直接获取到自己。

2. cell部分的实现

scripts/cell文件夹下创建FirstSpace.py文件。

class FirstSpace(KBEngine.Space):
    """
    FirstSpace的cell部分。
    """
    def __init__(self):
        KBEngine.Space.__init__(self)
        pass

cell部分使用最简单的Space实体的实现方法

第四步:Baseapp就绪时,创建Space空间

因为需要把FirstEntity放入空间,所以必须先创建Space实体对象,本教程选择在Baseapp就绪时,对空间进行创建

修改kbemain.py

打开文件scripts/base/kbemain.py,在onBaseAppReady函数中,添加如下代码

​
# baseapp就绪时,创建一个Space空间实体
KBEngine.createEntityLocally("FirstSpace", {})

​

这里我们通过createEntityLocally的API,对FirstSpace实体进行了创建,第一个参数是Entity类型字符串即FirstSpace类型,第二个参数是Entity属性字典,这里使用空字典对象。当服务器启动时,就会创建它。

OK,我们启动引擎,验证一下空间是否会在开启服务器时创建吧

点击启动start_server.bat.当所有窗口都显示了[INFO]: Found all the components! 就证明服务器启动成功了!

关闭引擎!

最后一步:让我们的FirstEntity进入空间

有了Space空间,但是我们的FirstEntity还没有任何方式方法进入该空间。我们可以在客户端连接服务器并和FirstEntity关联时,就立马进入空间。

打开之前base部分代码,scripts/base/FirstEntity.py,添加onClientEnabled回调函数,最终如下

class FirstEntity(KBEngine.Proxy):
    """
    第一个实体的base部分的实现
    """

    def __init__(self):
        KBEngine.Entity.__init__(self)

    def onClientEnabled(self):
        """
        该entity被正式激活为可使用, 此时entity已经建立了client对应实体。
        """
        # 客户端一旦连接,就把他放入FirstSpace空间
        first_space = KBEngine.globalData["FirstSpace"]
        self.createCellEntity(first_space.cell)

可以看到,简单的从KBEngine.globalData["FirstSpace"]中获取到FirstSpace,并调用了createCellEntity方法,把我们的FirstEntity创建到FirstSpace的cell部分所在的空间中去。值得注意的是,这里的参数使用first_space.cell,该api需要提供的是first_space实体的cell部分

恭喜你,FirstEntity已经能进入到一个Space空间里了!

OK,我们的FirstSpace空间实体已经创建完毕。

当客户端登录进服务器时,FirstEntity就会被放入该空间,客户端向服务器进行say请求后,服务器会在该空间中广播,所有空间内的客户端都会收到。

至此,Get Started章节的服务端已全部介绍完毕

开发者应该已经迫不及待的想要让我们第一个实体能连接进服务器并进入空间了吧?那我们马上来看如何实现客户端部分的代码


客户端的实现,我只会Unity3D

本节会分为如下几个部分进行讲解:

  • 如何使用引擎提供的SDK生成器进行SDK代码的生成;
  • SDK的文件夹结构简述;
  • 实现FirstEntity实体的Client部分;
  • 利用SDK提供的入口代码,启动客户端,并连接服务器;
  • 登录账号后,进入FirstSpace实体所在空间;
  • 向服务器上的FirstEntity实体say hello。

 

官方使用的是Unity2017.2.0f3,我使用的2019.

ComblockEngine提供了专属的SDK生成器

引擎提供的SDK生成器会自动根据引擎开发过程中涉及的通讯协议、数据结构(包括自定义的数据结构)、Entity实体定义等方面与客户端SDK进行一一对应,保证高度一致性。

 

1. 修改SDK生成路径:

在本项目的资产库“getstarted_assets”下,找到gensdk.bat,使用编辑工具或记事本打开,找到最后一行:

修改输出路径到自己对应的Plugins里,把最后一行UE4加上REM 注释掉

start %KBE_BIN_PATH%/kbcmd.exe --clientsdk=unity --outpath=%curpath%/kbengine_unity3d_plugins
start %KBE_BIN_PATH%/kbcmd.exe --clientsdk=ue4 --outpath=%curpath%/kbengine_ue4_plugins

clientsdk:指定输出SDK的客户端类型,这里填写unity。生成器会自动选择合适的生成模板和逻辑进行生成。

outpath:指定SDK的输出路径。请确保路径是在Plugins文件夹下,并且修改成你的客户端工程所在目录下的对应路径

这里我们选择直接输出到Unity3D客户端对应的路径中

 

2. 执行工具,生成SDK代码:

编辑完成后保存退出,双击执行gensdk.bat

3. 文件夹结构介绍:

接下来,我们看看生成的文件有哪些

11

生成规则:

1、服务端定义了有客户端部分的实体(声明了hasClient=true的),则会生成类似实体名+Base.cs的文件,它是一个抽象类,我们只需继承它、实现它,并使用实体名为类名即可,如这里的FirstEntityBase,该类中会包含def中声明的客户端方法,如本教程中的onEnteronSay

2、被生成的实体,会对应包含一个类似EntityCall+实体名+Base.cs的文件,该文件是对应实体的EntityCall的实现。该类中会包含该实体的被暴露给客户端的通讯方法(被设置了Exposed标签的),如FirstEntity在def中声明的say方法

第二步:实现Client部分

 

1. 客户端设计概述

服务器回顾:

先来回顾下本教程的服务器设计,我们把FirstEntity与账户入口关联,使得客户端一旦连接服务器并通过登录认证后就会创建出FirstEntity实体,此时该实体的客户端部分也会被创建。一旦创建完毕后会被立即传送到FirstSpace所在空间中去,完成后会通过onEnter的远程方法通知客户端。接着,客户端向服务器发出say请求后,服务器会进行广播,并通过客户端的onSay方法告知所有在同一空间的客户端。

客户端设计:

我们分为两个场景,一个叫做scene_login登录场景,默认打开,负责与服务器连接、登录认证。一旦成功登录并进入空间后,服务器会调用FirstEntity的客户端远程方法onEnter,从该方法的实现中让客户端进入另一个场景scene_world,其负责say的发送以及处理onSay的远程调用。

1

2. 实现scene_login登录场景

2.1 场景制作

场景列表视图如下

1

其中:

client_app: 是引擎sdk的入口继承KBEMain,附上脚本clientapp.cs,并保持默认配置

1

panel_login:是使用UGUI制作的一个简单的登录界面,附上脚本UILogin.cs,且登录按钮绑定了UILogin的OnLoginClick事件

1

1

UI效果图如下

1

2.2 实现UILogin

直接上代码块:

/// <summary>
/// 登录界面的绘制
/// </summary>
public class UILogin : MonoBehaviour
{
    /// <summary>
    /// 响应登录按钮,该按钮在Unity的UGUI系统中被绑定到按钮事件上。
    /// </summary>
    public void OnLoginClick()
    {
        //账号密码都要大于6位
        //得到账户输入框的文本
        string account = GameObject.Find("account").GetComponent<InputField>().text;
        //得到密码输入框的文本
        string password = GameObject.Find("psw").GetComponent<InputField>().text;
        //调用API的登录接口。最后一个参数可暂时无视,具体请参考API手册
        KBEngineApp.getSingleton().login(account, password, System.Text.Encoding.UTF8.GetBytes("kbengine_unity3d_demo"));
    }
}

这里主要调用了API中的login方法进行了登录请求,该API会先进行服务器连接,成功后调用登录方法。

 

3. 实现FirstEntityBase

按照刚才的设计,登录成功后,FirstEntity的客户端部分会被创建,也就是说客户端上FirstEntity对象会被创建,接着进入空间后,会通过FirstEntity.onEnter通知客户端,所以我们来实现一下FirstEntity的客户端部分。

FirstEntityBase是对应服务端的FirstEntity实体的,我们实现它即可完成其客户端部分

我们来看一下代码:

/// <summary>
/// FirstEntity的客户端实现
/// </summary>
public class FirstEntity : FirstEntityBase
{
    public override void onEnter()
    {
        //日志
        Dbg.INFO_MSG("FirstEntity::onEnter");
        //当进入后,加载某个场景
        SceneManager.LoadScene("scene_world");      
    }

    public override void onSay(string content)
    {
        Dbg.INFO_MSG(content);
        //找到UI Text对象
        Text text = GameObject.Find("Canvas/Text").GetComponent<Text>();
        //增加一行say的内容
        text.text = text.text + "\n" + content;
    }
}

onEnter:我们注意到,该函数名和服务端FirstEntity实体的DEF配置文件中的client部分定义的一模一样!对,这就是SDK生成器帮你做的事情。在生成的FirstEntityBase类中使用抽象函数public abstract void onEnter();定义了该方法,并由SDK内部进行了通讯上的对应和处理,我们只需要在继承类FirstEntity中实现即可

onSay:该方法也是和DEF配置文件中的client部分一样,并且连方法签名也是一致的(我的方法签名不一致也没影响的)

每个被指定有Client部分的实体,在客户端上必须要有实现类且类名和实体名字一致。比如这里的FirstEntity : FirstEntityBase,不能把类名修改成其他,如FirstEntity2就会在客户端SDK启动时报错提示。

 

4. 实现scene_world空间场景

4.1 场景制作

场景列表视图如下:

1

其中:

helloworld:是使用UGUI制作的一个简单的发送hello world的界面,附上脚本HelloWorld.cs,且hello world按钮绑定了HelloWorld的OnClick事件

1

UI效果图如下:

1

4.2 实现HelloWorld

直接上代码块:

/// <summary>
/// 进入空间后,HelloWorld交互界面的绘制
/// </summary>
public class HelloWorld : MonoBehaviour
{
    /// <summary>
    /// 响应Helloworld按钮,该按钮在Unity的UGUI系统中被绑定到按钮事件上。
    /// </summary>
    public void OnClick()
    {
        //通过API:player()获得账户自己的实体,在本例中账户自己的实体就是FirstEntity
        FirstEntity entity = KBEngineApp.getSingleton().player() as FirstEntity;
        //由于say方法是在cell上的远程方法,这边使用cellEntityCall属性来调用。
        //如果是一个base上的远程方法,则使用baseEntityCall进行调用
        entity.cellEntityCall.say("hello world");
    }
}

这里主要调用了API中的player()方法获取到客户端自身的账户实体,并转成了FirstEntity类型。同时调用了该实体上的cell的say方法来调用服务器端的对应远程方法,完成向服务器的请求。

对!刚才的onEnteronSay,包括这里的cellEntityCall.say,与服务端一一对应的这一切事情,都是由SDK生成器帮你完成的!

接下来,让我们迎来激动人心的时刻!服务器和客户端的联通验证!

第三步:验证

1. 启动引擎

在本项目的资产库“getstarted_assets”下,找到start_server.bat,并双击运行。

等待所有服务器组件的窗口都出现“Found all the components!”字样,就说明成功启动了。

 

2. 运行客户端

2.1 确保两个场景都在Build Settings中

1

2.2 打开scene_login场景

2.3 点击Play,启动客户端。

1

3. Hello world

1、启动后,出现登录窗口,随意输入账号和密码(长度都要大于4位),点击Login按钮,就会向服务器发出登录请求。

Tips:本教程中,服务端没有对账户验证做处理,所以任意的账号密码都可以登录成功。
整个过程细节,可以查看客户端中Console窗口的日志

2、一旦登录成功,会跳转至scene_world场景,里面只有一个UI,即HelloWorld

 

3、点击hello world按钮,会向服务端发起say的远程调用。

4、收到onSay的通知后,会在UI显示出文字

 上图中的“2”是代表实体的id,在服务器的FirstEntity的cell部分实现代码中,onSay输出的content格式:“Entity: ” + self.id + content

恭喜你,Unity3D的客户端实现已完成!

通过Unity3D客户端的实现,我们利用FirstEntity实体的Client部分与服务器建立了连接,并立即进入了FirstSpace所在的空间中,接着,在空间内我们向第一个实体FirstEntity进行了say的操作,并收到了onSay的广播。


我跟着教程一步步做,是没问题的!!!

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐