Godot的实现
本文将会使用Godot Engine来完成上一节中的客户端实现。
Godot版本
本教程中使用的版本为Godot 4.4(.NET/Mono版本),C#语言。
先附上客户端Godot的源代码:点我下载(里面包含了对应的服务端源代码)。
Godot Engine是一款功能强大的跨平台游戏引擎,支持2D和3D游戏开发。本教程使用其.NET版本,以C#作为脚本语言。
第一步:生成客户端SDK
KBEngine提供了专属的SDK生成器,它是为了方便开发者而专门制作的工具,使开发者面对不同的客户端引擎时都可以游刃有余。
引擎提供的SDK生成器会自动根据引擎开发过程中涉及的通讯协议、数据结构(包括自定义的数据结构)、Entity实体定义等方面与客户端SDK进行一一对应,保证高度一致性。
介绍完SDK生成器,我们来看看如何修改配置,使其对应Godot引擎。
1. 修改SDK生成路径:
在本项目的资产库"getstarted_assets"下,找到gensdk.bat,使用编辑工具或记事本打开,找到最后一行:
start %KBE_BIN_PATH%/kbcmd.exe --clientsdk=csharp --outpath=%curpath%/kbengine_csharp_plugins其中: clientsdk:指定输出SDK的客户端类型,这里填写csharp。Godot使用C#作为脚本语言,生成器会自动选择合适的生成模板和逻辑进行生成。
outpath:指定SDK的输出路径。请确保路径是在你Godot工程的对应目录下。
TIP
如果引擎脚本开发工程师和客户端工程师是同一人,或者为了立刻验证SDK的结果是否如预期,我们建议outpath设置成Godot工程下的对应目录,如:your_project/kbe_csharp_plugins。每次生成SDK后可直接切回客户端进行测试,减少了重复粘贴复制的操作。
2. 执行工具,生成SDK代码:
编辑完成后保存退出,双击执行gensdk.bat。
等待生成完毕,进入客户端对应的文件夹查看。
3. 文件夹结构介绍:
生成完成后,kbe_csharp_plugins文件夹下包含以下核心文件:
| 文件/模块 | 说明 |
|---|---|
KBEngine.cs | 核心引擎入口,KBEngineApp单例(约2500行),管理连接、实体、登录 |
GodotKBEMain.cs | Godot专用的SDK入口,作为Node子类运行,驱动_PhysicsProcess更新 |
NetworkInterfaceTCP.cs / NetworkInterfaceKCP.cs | TCP和KCP双网络后端 |
Entity.cs / EntityCall.cs | 实体基类和远程调用机制 |
EntityDef.cs | 数据类型、实体方法、属性的自动生成定义 |
Messages.cs | 协议消息ID和处理器映射 |
Event.cs | 事件系统,支持注册/注销 |
FirstEntityBase.cs | FirstEntity实体的抽象基类(自动生成) |
EntityCallFirstEntityBase.cs | FirstEntity的远程调用代理(自动生成) |
deps/KCP.cs | KCP可靠UDP协议实现 |
生成规则:
1、服务端定义了有客户端部分的实体(声明了hasClient="true"的),则会生成类似实体名+Base.cs的文件,它是一个抽象类,我们只需继承它、实现它,并使用实体名为类名即可,如这里的FirstEntityBase,该类中会包含def中声明的客户端方法,如本教程中的onEnter、onSay;
2、被生成的实体,会对应包含一个类似EntityCall+实体名+Base.cs的文件,该文件是对应实体的EntityCall的实现。该类中会包含该实体的被暴露给客户端的通讯方法(被设置了Exposed标签的),如FirstEntity在def中声明的say方法。
第二步:实现Client部分
1. 客户端设计概述
服务器回顾:
先来回顾下本教程的服务器设计,我们把FirstEntity与账户入口关联,使得客户端一旦连接服务器并通过登录认证后就会创建出FirstEntity实体,此时该实体的客户端部分也会被创建。一旦创建完毕后会被立即传送到FirstSpace所在空间中去,完成后会通过onEnter的远程方法通知客户端。接着,客户端向服务器发出say请求后,服务器会进行广播,并通过客户端的onSay方法告知所有在同一空间的客户端。
客户端设计:
我们分为两个场景,一个叫做Start.tscn启动场景,默认打开,负责与服务器连接、登录认证。一旦成功登录并进入空间后,服务器会调用FirstEntity的客户端远程方法onEnter,从该方法的实现中让客户端进入另一个场景World.tscn,其负责say的发送以及处理onSay的远程调用。
Godot中KBEngine SDK通过**Autoload(自动加载)**机制运行——App.tscn作为Autoload在所有场景加载前初始化,确保SDK在整个游戏生命周期中持续运行。
好了,让我们开始动手吧!
2. 实现登录场景
2.1 场景制作
Start.tscn场景结构如下:
根节点:Node类型,挂载
start.gd脚本用于初始化。MainUI:一个Control节点,包含登录界面的子节点:
Login面板:用户名输入框(LineEdit)、密码输入框(LineEdit)、登录按钮(Button)- 挂载
MainUi.cs脚本
UI效果图如下(参考Unity章节的登录界面设计,使用Godot的Control/Button/LineEdit实现)。
2.2 实现SDK入口(App.cs)
Godot中使用Autoload机制来运行SDK。首先创建App.tscn场景,根节点挂载App.cs脚本,然后在项目设置中将其设为Autoload:
using Godot;
using KBEngine;
public partial class App : GodotKBEMain
{
public override void _Ready()
{
// 初始化日志系统,使用Godot的GD.Print输出
KBELog.Init(new GodotLogProvider());
base._Ready();
// 注册场景切换事件
Event.registerOut(EventOutTypes.addSpaceGeometryMapping,
this, "addSpaceGeometryMapping");
}
public void addSpaceGeometryMapping(String path)
{
KBELog.INFO_MSG("addSpaceGeometryMapping: path=" + path);
}
}要点说明:
App继承自GodotKBEMain(SDK提供的Godot入口类),它会自动读取节点上配置的IP和端口参数来初始化KBEngineApp。GodotLogProvider将SDK日志桥接到Godot的GD.Print输出。addSpaceGeometryMapping是进入空间后服务器触发的回调,可以在此处理场景切换。
2.3 实现MainUI(登录界面)
using Godot;
using KBEngine;
public partial class MainUi : Control
{
private LineEdit NameEdit;
private LineEdit PasswordEdit;
public override void _Ready()
{
NameEdit = GetNode<LineEdit>("Login/UserNameEdit");
PasswordEdit = GetNode<LineEdit>("Login/PasswordEdit");
}
// 登录按钮点击信号回调
void _on_login_btn_button_up()
{
GD.Print("连接到服务端....");
KBEngineApp.app.login(
NameEdit.Text,
PasswordEdit.Text,
"kbengine_unity3d_demo"u8.ToArray()
);
}
}这里主要调用了API中的login方法进行了登录请求,该API会先进行服务器连接,成功后调用登录方法。
3. 实现FirstEntity
按照刚才的设计,登录成功后,FirstEntity的客户端部分会被创建,也就是说客户端上FirstEntity对象会被创建,接着进入空间后,会通过FirstEntity.onEnter通知客户端,所以我们来实现一下FirstEntity的客户端部分。
using Godot;
using KBEngine;
public class FirstEntity : FirstEntityBase
{
public override void onEnter()
{
KBELog.INFO_MSG("FirstEntity::onEnter");
// 当进入空间后,加载世界场景
// 注意:需要先暂停事件系统
KBEngineApp.app.eventPause();
// 切换到World场景
App.Instance.GetTree().ChangeSceneToFile("res://World.tscn");
KBEngineApp.app.eventResume();
}
public override void onSay(string content)
{
KBELog.INFO_MSG("FirstEntity::onSay: " + content);
// 找到UI Label并显示内容
var label = App.Instance.GetTree()
.CurrentScene.GetNode<Label>("Label");
if (label != null)
{
label.Text = label.Text + "\n" + content;
}
}
}onEnter:我们注意到,该函数名和服务端FirstEntity实体的DEF配置文件中的client部分定义的一模一样!对,这就是SDK生成器帮你做的事情。在生成的FirstEntityBase类中使用虚方法定义了该方法,并由SDK内部进行了通讯上的对应和处理,我们只需要在继承类FirstEntity中重写即可。
onSay:该方法也是和DEF配置文件中的client部分一样,并且连方法签名也是一致的!
注意
每个被指定有Client部分的实体,在客户端上必须要有实现类且类名和实体名字一致。比如这里的FirstEntity : FirstEntityBase,不能把类名修改成其他,如FirstEntity2就会在客户端SDK启动时报错提示。
4. 实现世界场景
4.1 场景制作
World.tscn场景中需要:
根节点:一个Node3D或Node节点。
HelloWorld按钮:一个Button节点和Label文本显示区域,挂载
WorldUi.cs脚本。
4.2 实现WorldUi
using Godot;
using KBEngine;
public partial class WorldUi : Control
{
private Label TextLabel;
public override void _Ready()
{
TextLabel = GetNode<Label>("Label");
}
// hello world按钮点击信号回调
void _on_hello_btn_button_up()
{
// 通过API:player()获得账户自己的实体
FirstEntity entity = KBEngineApp.app.player() as FirstEntity;
// 由于say方法是在cell上的远程方法,这边使用cellEntityCall属性来调用
entity.cellEntityCall.say("hello world");
}
}这里主要调用了API中的player()方法获取到客户端自身的账户实体,并转成了FirstEntity类型。同时调用了该实体上的cellEntityCall.say方法来调用服务器端的对应远程方法,完成向服务器的请求。
对!刚才的onEnter、onSay,包括这里的cellEntityCall.say,与服务端一一对应的这一切事情,都是由SDK生成器帮你完成的!
接下来,让我们迎来激动人心的时刻!服务器和客户端的联通验证!
第三步:验证
1. 启动引擎
在本项目的资产库"getstarted_assets"下,找到start_server.bat,并双击运行。
等待所有服务器组件的窗口都出现"Found all the components!"字样,就说明成功启动了。
2. 运行客户端
2.1 打开Godot编辑器,导入工程(确保已安装.NET SDK 8.0+)。
2.2 编译C#解决方案(编辑器会自动提示编译,或使用菜单 Build → Build Solution)。
2.3 点击编辑器右上角的运行项目按钮(或按F5),启动游戏。
3. Hello world
1、启动后,出现登录窗口,随意输入账号和密码(长度都要大于4位),点击Login按钮,就会向服务器发出登录请求。
Tips
本教程中,服务端没有对账户验证做处理,所以任意的账号密码都可以登录成功。 整个过程细节,可以查看客户端的Output窗口日志。
2、一旦登录成功,会切换到World.tscn场景,里面只有一个UI——HelloWorld按钮。
3、点击hello world按钮,会向服务端发起say的远程调用。
4、收到onSay的通知后,会在UI显示出文字。
TIP
显示内容格式为"Entity: " + self.id + content,这是服务端FirstEntity中定义好的格式。
恭喜你,Godot的客户端实现已完成!
通过Godot客户端的实现,我们利用FirstEntity实体的Client部分与服务器建立了连接,并立即进入了FirstSpace所在的空间中,接着,在空间内我们向第一个实体FirstEntity进行了say的操作,并收到了onSay的广播。
这是GetStarted章节中客户端Godot的源代码:点我下载(里面包含了对应的服务端源代码)
开发者肯定对整个的通讯过程、业务流程还存在一些疑问,让我们进入下一节《GetStarted总结》中进行回顾、梳理和总结吧。
