本帖最后由 不会游泳的鱼 于 2024-11-9 22:28 编辑
前言
实现目标
👀️ 众所周知,当前口袋方舟编辑器的代码开发方式是基于模块的,存档是基于DataCenter,对于刚刚接触编程的新手来说,理解起来成本很大,服务器和客户端常常搞的很混乱,各个模块的功能划分也很困难。
为了解决这个问题,设计了一套基于角色的服务端客户端编程套件,新手用户不用关心模块,数据存储,数据的读取和同步等大部分理解成本过高的问题。该套件会自动的完成以上功能,用户只需要关心代码需要在服务端还是客户端运行就好了🚀️
设计思路
设计分为世界上下文和玩家上下文两个部分
整体结构
世界上下文
世界上下文负责世界(整个房间)的事件,客户端/服务端通讯,效果表演以及消息通知。设想一个场景,你需要驱动整个世界的光照在所有玩家的屏幕里按照时间来运行,你可以实现一个世界光照上下文,在这个系统里更改所有用户的光照
@SystemType.WorldSystem()
export class WorldLighterSystem extends BaseWorldSubSystem {
private currentClientTime: number = 0;
public onStart() {
this.bTicked = SystemUtil.isClient();
}
onUpdate(dt: number) {
if (this.currentClientTime != this.system.Attribute_WorldtimeMinute) {
this.currentClientTime = this.system.Attribute_WorldtimeMinute;
this.changeLighter();
}
}
private changeLighter() {
const minuteOffset = Math.abs(this.currentClientTime - 720);//720-0-720
mw.Lighting.directionalLightIntensity = 6 - minuteOffset * 0.007;
mw.Lighting.skyLightIntensity = 1 - minuteOffset * 0.001;
}
}
以上代码就是实现光照系统所需要关注的所有代码,通过“SystemType.WorldSystem”装饰器,系统会自动运行起来。
玩家上下文
玩家上下文属性的使用场景在于管理玩家的状态和持久化数据、玩家的服务端和客户端通讯。甚至玩家之间的通讯也可使用。时刻记住,每个玩家都有一套单独的玩家上下文系统,并且这个系统包括玩家的所有子系统
一个常见的例子,我们在游戏中需要管理玩家的外观,可以设计一个外观子系统来实现这个目的。
/**
* 玩家装扮系统
*/
@SystemType.CharacterSystem()
export class AppearanceSubSystem extends BaseCharacterSubSystem {
public onStart() {
this.system.addAttributeChangeEvent("Appearace_RoleId", this.onAppearaceRoleIdChanged, this);
}
private onAppearaceRoleIdChanged(roldId: string) {
this.system.character.setDescription([roldId]);
}
@SystemType.Client()
public OpenAppearaceUI() {
UIService.show(AppearaceMainUI);
}
@SystemType.Server()
changeDress(id: string) {
this.system.Appearace_RoleId = id;
}
}
以上就是一个外观系统的基本实现代码,包括打开UI,更换玩家的外观以及同步给所有其他人该玩家外观,其中有几个@SystemType
开头的装饰器,后文用户手册部分会详细说明。
玩家存档
😄 玩家存档已经融于玩家上下文系统,下面一段话为运行原理,一般使用只需参考用户手册中的保存玩家属性部分。
目前存档只存在于玩家身上,通过在玩家上下文(CharacterContext
)中使用@SystemType.Save
装饰器来保存字段,你可以随意定义装饰器来保存你需要保存的属性,这些属性在玩家下线的时候会自动保存,再次上线的时候也会自动读取,如果有属性同步标识,也将会自动在所有玩家的客户端自动触发属性同步,这些都是完全自动的。😕 需要注意的是存档数据不能保存类对象和Map类型。
属性系统
在游戏中,常常需要知道别的玩家身上或者房间内的一些属性。比如敌对玩家的血量,世界BOSS的血量等等。上面例子中的世界时间,玩家身上的装扮都是通过这套系统来实现的,结合玩家属性系统,可以很方便的做出持久化的表现数据。
上下文系统中玩家属性定义为3种
- 保存且需要同步的属性
- 保存但不需要同步的属性
- 不保存但需要同步的属性
用户手册部分会一一介绍这几种属性。需要牢记,更改属性的函数必须运行在服务端才能真实生效。🚀️
用户手册
通用函数
@SystemType.Server()
这个装饰器表示该函数是在服务端运行
@SystemType.Server()
changeDress(id: string) {
this.system.Appearace_RoleId = id;
}
@SystemType.Multicast()
这个装饰器表示该函数在所有客户端运行
//给所有客户端发个消息
@SystemType.Multicast()
sendMessage(msg: string) {
console.log(msg);
}
玩家上下文函数
@SystemType.CharacterSystem()
将子系统注册到玩家上下文,同时激活该系统
@SystemType.CharacterSystem()
export class AppearanceSubSystem extends BaseCharacterSubSystem {
}
@SystemType.Client()
这个装饰器表示该函数在当前玩家的客户端运行
@SystemType.Client()
public OpenAppearaceUI() {
UIService.show(AppearaceMainUI);
}
@SystemType.ToClient()
这个装饰器表示该函数需要在指定玩家的客户端运行,第一个参数必须为mw.Player,表示运行这个函数的客户端,最后一个可选参数为发起者
//给指定玩家发个消息
@SystemType.ToClient()
sendMessageTo(player:mw.Player,msg: string,from?:mw.Player) {
console.log(`${from.userId}对${player.userId}说`,msg);
}
世界上下文函数
@SystemType.WorldSystem()
将子系统注册到世界上下文,同时激活该系统
@SystemType.WorldSystem()
export class LighterSystem extends BaseWorldSubSystem {
public onStart() {
}
onUpdate(dt: number) {
}
}
属性系统
玩家属性
玩家属性仅能在CharacterContext
类中定义,针对上文提到的3种不同类型的属性,使用如下装饰器可以很容易的进行定义。
保存且需要同步的属性
//经验值
@SystemType.Save("exp")
public Attribute_Exp: number = 0;
保存但不需要同步的属性
@SystemType.Save("role",false)
public Appearace_RoleId: string = "";
不保存但需要同步的属性
@mw.Property({replicated:true})
public Appearace_face: string = "";
世界属性
目前世界属性没有设计保存接口,故只有一种世界属性,即不保存但需要同步的属性,使用@mw.Property({ replicated: true })
即可,示例如下
/**
* 当前世界时间-以秒计算
*/
@mw.Property({ replicated: true })
Attribute_WorldtimeSecond: number = 0;
存档函数
存档的读取和写入都是自动进行,系统会自动管理存档,不需要进行任何操作,Enjoy!
性能优化
对于单帧的多次调用,玩家上下文系统会合并成一次调用来减少RPC消耗,防止RPC溢出导致的断线。有经验的使用者可以结合项目实际做更多的优化。
RPC优化在Demo项目的CharacterContext
类中实现,可以看到是将远程调用做了合批处理。
DEMO
由于帖子无法插入附件,DEMO见第一条回帖
此DEMO包含上文提到的所有用法,不懂的地方可以运行DEMO,随意更改代码看看效果即可明白
后言
该方案为临时兴趣所致,后续不保证对方案的扩展性维护,同时方案中可能的一些BUG也需要一定代码理解能力,遇到问题可以尝试自己先解决,同时可以在帖子后留言,有时间我会解答,但不保证时效性。你可以参照玩家上下文和世界上下文的实现,设计自己的上下文,做出更多易用效果。同时,也欢迎改进DEMO中上下文系统本体的性能和易用性,帮助更多用户👍