基于ComblockEngine+Unity的联机版坦克大战(三)

阶段目标

  • 利用KBE的Event事件系统解耦客户端相应逻辑
  • 匹配和房间结构调整
  • 顶号和断线重连回战场

效果演示

在这里插入图片描述

进入战场流程调整

之前版本中,大厅和战场都是SpaceRoom,这会导致一个问题,就是我们需要给space加一个type字段,然后根据type来区分具体的战场类型,而且大厅和战场的进入逻辑并不一致,会导致在相应的接口里面做一堆的if…else判断,这明显会增加代码的复杂性和可读性。

针对以上这些问题,server端将大厅space和战场space进行拆分, 分别是GameHall和GameBattleSpace,同时匹配的功能也拆分出一个单独的Entity来完成,叫做MatchStub。

同时调整匹配和进入战场的流程,调整后的流程图如下:
其中Cell_xxx代表的是cellapp部分的名为xxx的entity实体,其余都是baseapp上的实体。

client PlayerAvatar MatchStub SpaceMgr baseApp GameBattleSpace Cell_PlayerAvatar Cell_GameB reqMatch begin_match _notify_avatars_match_info _match_success enter_game_battle_space 创建GameBattleSpace onGetCell 让待进入的玩家进入space teleport_space enter enterBattleSpace client PlayerAvatar MatchStub SpaceMgr baseApp GameBattleSpace Cell_PlayerAvatar Cell_GameB

具体代码,可以搜索流程图中的函数名进行查看。

断线重连进战场

断线重连主要就两个问题,一个是网络连接的问题,另一个是恢复战场数据的问题。

  • 对于已经在线的账号,我们再次登录该账号时,会触发脚本层的onLogOnAttempt方法,一些具体的业务逻辑可以在这里处理。
  • proxy对象是用于和client通信的对象,主要处理网络连接。可以通过giveClientTo将当前客户端连接的proxy转交给一个entity实体。
  • 玩家断线后PlayerAvatar是否需要销毁的问题。
    • 如果销毁,那么每次重连就需要重新创建一个PlayerAvatar,可以复用首次登陆游戏的逻辑,但这么做会多一次连接GameHall再跳转到GameBattleSpace的流程,同时需要保证其他客户端本地的对象索引不能是Entity.id,因为重新创建的PlayerAvatar对象的唯一id是不同的。
    • 如果不销毁,可以直接查找到之前的PlayerAvatar,然后将proxy进行更新即可。不过无法直接复用首次登陆流程。
  • 最终游戏采用的是不销毁PlayerAvatar的方式。

针对上面两点,其实就差不多可以完成断线重连的逻辑处理了。但是,在过程中,缺遇到了一个坑,坑了我好久,有坑的代码示例如下:

def onClientEnabled(self):
		"""
		KBEngine method.
		该entity被正式激活为可使用, 此时entity已经建立了client对应实体, 可以在此创建它的
		cell部分。
		"""
		INFO_MSG("333333333333333333333")
		self.become_player_avatar()
			
def onLogOnAttempt(self, ip, port, password):
	INFO_MSG("[Account], %i onLogOnAttempt: ip=%s, port=%i, selfclient=%s, %s" % (self.id, ip, port, self.client, self.active_avatar))

	if self.active_avatar is not None:
		INFO_MSG("111111111111111111111")
		if self.active_avatar.client is not None:
			self.active_avatar.giveClientTo(self)
		
		// 此处省略其他代码
		INFO_MSG("222222222222222222222")
	return KBEngine.LOG_ON_ACCEPT

上诉代码,模拟一次顶号重连,猜猜日志会输出啥?
我预期输出是:

111111111111111111111
222222222222222222222
333333333333333333333

而实际输出却是:

111111111111111111111
333333333333333333333
222222222222222222222

这个流程可真的是出乎我的意料,也就导致我最开始写的重连流程都是有问题的,折腾了许久,最后结合源码调试才发现了问题所在(KBEngine相关源码分析准备在后续文章中写写)。原因在于giveClientTo这个函数,会触发一次目标对象的onClientEnabled回调。

最后的顶号重连代码如下:

	def onClientEnabled(self):
		"""
		KBEngine method.
		该entity被正式激活为可使用, 此时entity已经建立了client对应实体, 可以在此创建它的
		cell部分。
		"""
		INFO_MSG("account[%i] entities enable. entityCall:%s" % (self.id, self.client))

		# 原账号在线
		if self.active_avatar:
			if self.active_avatar.reconnect_flag:
				INFO_MSG("[Account], %i re login avatar battle space id:%s" % (self.id, self.active_avatar.battle_space_id))
				self.giveClientTo(self.active_avatar)
				self.active_avatar.re_connect_to_battle_space()
			return

		self.become_player_avatar()
			
	def onLogOnAttempt(self, ip, port, password):
		"""
		KBEngine method.
		客户端登陆失败时会回调到这里reloginBaseapp
		"""
		INFO_MSG("[Account], %i onLogOnAttempt: ip=%s, port=%i, selfclient=%s, %s" % (self.id, ip, port, self.client, self.active_avatar))

		# 置空掉之前avatar的client
		if self.active_avatar:
			self.active_avatar.giveClientTo(self)
			self.active_avatar.reconnect_flag = True

		return KBEngine.LOG_ON_ACCEPT
	PlayerAvatar的重连战场
	def re_connect_to_battle_space(self):
		if self.battle_space_id > 0:
			_battle_space = KBEngine.entities.get(self.battle_space_id, None)
			_battle_space_cell = _battle_space.cell
			if _battle_space and _battle_space_cell:
				INFO_MSG("[PlayerAvatar], %s, %i, re_connect_to_battle_space" % (self, self.id))
				self.cell.teleport_space(_battle_space_cell, self.born_position, (0, 0, 0))

后续内容

到这里,关于游戏脚本层想写的其实我都写完了,对于所谓的战斗同步啥的,都是相应的游戏逻辑实现了,思想都类似,就不准备写了,以上三篇文章,完成了一个账号的创建、登录、重连回战场,以及战场内的移动同步。对于我来说,通过自己去实现一遍这个流程,对于KBEngine的基本使用目标已经达成。
后续,就开始慢慢剖析KBEngine的源码了,那才是我的主要学习和记录对象哈。以具体的游戏需求来引导自己入门上手一款引擎,然后开始剖析其背后的原理和实现。关于源码的学习记录,准备另外开个系列的标题文章来写,慢慢看,慢慢学,慢慢记录。

源码地址

github地址:地址在这里

发布了47 篇原创文章 · 获赞 8 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/it_wjw/article/details/103752831