基於ComblockEngine+Unity的聯機版坦克大戰(三)

階段目標

  • 利用KBE的Event事件系統解耦客戶端相應邏輯
  • 匹配和房間結構調整
  • 頂號和斷線重連回戰場

效果演示

在這裏插入圖片描述

進入戰場流程調整

之前版本中,大廳和戰場都是SpaceRoom,這會導致一個問題,就是我們需要給space加一個type字段,然後根據type來區分具體的戰場類型,而且大廳和戰場的進入邏輯並不一致,會導致在相應的接口裏面做一堆的if…else判斷,這明顯會增加代碼的複雜性和可讀性。

針對以上這些問題,server端將大廳space和戰場space進行拆分, 分別是GameHall和GameBattleSpace,同時匹配的功能也拆分出一個單獨的Entity來完成,叫做MatchStub。

同時調整匹配和進入戰場的流程,調整後的流程圖如下:
其中Cell_xxx代表的是cellapp部分的名爲xxx的entity實體,其餘都是baseapp上的實體。

clientPlayerAvatarMatchStubSpaceMgrbaseAppGameBattleSpaceCell_PlayerAvatarCell_GameBreqMatchbegin_match_notify_avatars_match_info_match_successenter_game_battle_space創建GameBattleSpaceonGetCell讓待進入的玩家進入spaceteleport_spaceenterenterBattleSpaceclientPlayerAvatarMatchStubSpaceMgrbaseAppGameBattleSpaceCell_PlayerAvatarCell_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地址:地址在這裏

發佈了48 篇原創文章 · 獲贊 10 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章