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

階段目標

  • 從大廳進入戰場
  • 客戶端修改爲雙人版本
  • 戰場內移動同步的實現

效果演示

在這裏插入圖片描述

單機版的結構

在這裏插入圖片描述
GameManager作爲一個全局的遊戲管理器存在,每個具體的坦克是由TankManager類來進行管理,其中,坦克具體的移動和射擊邏輯是分別交由TankMovment和TankShooting去完成。

聯機版的結構

在這裏插入圖片描述
聯機版和單機版在類的設計實現上是一樣的,主要區別也就下面幾點:

  • TankManager交由PlayerAvatar來持有,因爲聯機時,我們每個角色的數據創建時由server來驅動的,與server對應的玩家實體是PlayerAvatar,這個PlayerAvatar相當於是一個真實的服務器一致的邏輯數據實體。
  • 區分出主玩家控制的坦克角色和其他非主玩家坦克

戰場的跳轉

這類開房間式的遊戲,都會涉及到一個從大廳跳轉戰場的流程。坦克大戰也是一樣,我們的戰場和大廳在這個版本里面都是SpaceRoom這一個類來完成,只是戰場和大廳的uType值不同。

class SpaceType(object):
	SPACE_TYPE_NONE = 0
	SPACE_TYPE_HALL = 1
	SPACE_TYPE_BATTLE = 2

在完成匹配後,我們會申請創建一個戰場space,並且在space創建完畢後,讓base上的playAvatar一個個enter進space,在enter到space上時,會先離開大廳這個space,然後調用playAvatar在cell部分的onTeleportSpaceCB方法,該方法主要做一件事情,就是讓玩家teleport到戰場所在的space。

跳轉戰場的時序圖如下:

clientbase_HallSpaceMgrbase_SpaceRoomPlayerAvatarcell_PlayerAvatarcell_SpaceRoomavatarReqMatch完成匹配enterSpace創建spaceaddWaitToEnteronGetCellenter通知跳轉onTeleportSpaceCBteleportonTeleportSuccesson_enter是否全部玩家進入onEnterBattleRoomclientbase_HallSpaceMgrbase_SpaceRoomPlayerAvatarcell_PlayerAvatarcell_SpaceRoom

說明下,base_Hall就是大廳,base_SpaceRoom是戰場,他們都是SpaceRoom這個類,只是圖裏面爲了區分,用了不同的名稱。


對應的基本代碼(git版本號 459eb4a3980f183d5e8b9b191d91a1845bd7bf1c):

base/SpaceRoom.py

def enter(self, avatar_entity_call):
	if self.uType == SpaceType.SPACE_TYPE_HALL:
		avatar_entity_call.createCellEntity(self.cell)
	else:
		# 從大廳進入戰場
		# 先從大廳離開
		avatar_entity_call.curHallSpace.leave(avatar_entity_call.id)
		avatar_entity_call.cell.onTeleportSpaceCB(self, self.cell, avatar_entity_call.born_position, (0, 0, 0))

	self._avatar_dict[avatar_entity_call.id] = avatar_entity_call

	avatar_entity_call.onEnterSpace(self.id, self.uType)
cell/PlayerAvatar.py

def onTeleportSpaceCB(self, spaceBaseEntityCall, spaceCellEntityCall, position, direction):
	"""
	defined.
	baseapp返回teleportSpace的回調
	"""
	DEBUG_MSG("PlayerAvatar::onTeleportSpaceCB. spaceBase:%s...spaceCell:%s" % (spaceBaseEntityCall, spaceCellEntityCall))

	self.curSpaceBaseEntityCall = spaceBaseEntityCall
	self.teleport(spaceCellEntityCall, position, direction)

移動同步

對於View範圍內的Entity,其基礎的屬性,Kbengine引擎底層會自動幫我們做好了屬性的同步,包括position和direction等。
但是,對於服務器自動同步下來的position,肯定都是一個個離散的點,同步數據到達客戶端的時間也受到當前網絡的影響,其次,服務端的tick頻率也往往會比客戶端低。如果客戶端每次收到position的位置,就直接更新模型到對應的點上,看上去的移動一定會是一卡一卡的。
爲了解決非主玩家的移動平滑同步,這裏採用了簡單的影子跟隨算法。影子跟隨簡單理解就是,模型(Gameobject)一直去追隨與之關聯的邏輯對象(PlayAvatar)位置。這個PlayAvatar是個Entity,也就是和服務端保持一直的一個實體對象,這是個邏輯的數據,其上的position會經由服務器自動同步下發。每個PlayAvatar都有一個與之綁定的Gameobject對象,Gameobject是個渲染模型對象。

追隨的核心算法在Scripts/Tank/TankMovement.cs裏面。

// 影子追隨
private float CalcNewValueByShadow(float curValue, float shadowValue, float deltaTime)
{
	if (curValue == shadowValue)
	    return curValue;
	
	float deltaValue = Mathf.Abs(curValue - shadowValue);
	int ratio = 1;
	if (curValue < shadowValue)
	{
	    // 大於一定閾值,加速
	    if (deltaValue > m_Speed * 40 * deltaTime)
	        ratio = 2;
	    
	    curValue += Mathf.Min(deltaValue, m_Speed * deltaTime * ratio);
	}
	else
	{
	    if (deltaValue > m_Speed * 5 * deltaTime)
	        ratio = 2;
	
	    curValue -= Mathf.Min(deltaValue, m_Speed * deltaTime * ratio);
	}
	
	return curValue;
}

說明: 代碼中有個40數值,這個僅僅是個保持相位差的最大閾值,可以根據需要自行調整,所謂相位差,就是每一幀下,實體和影子相對保持的距離。因爲我們設定的移速是m_Speed,一幀的時間是deltaTime,40相當於是我們允許模型和影子的最大位移差是40倍的m_Speed*deltaTime。

存在的問題

這個版本其實是相對粗糙的,無論是代碼結構還是遊戲功能,都還屬於功能驗證和引擎學習階段,對於戰場也是隻能每次只進入1輪便必須結束。這些問題會在下一篇文章裏統一解決掉。這裏只作爲KBEngine和Unity的入門練習準備。

後續內容

  • 戰鬥同步
  • 代碼結構調整,模塊解耦(比如space拆分、ui和邏輯拆分)
  • 斷線重連和頂號
  • 多房間創建(目前僅支持開一個戰場,不然會出問題)
  • KBEngine開發中遇到的坑
  • KBEngine中部分功能的源碼分析

源碼地址

github地址:地址在這裏

個人有點懶,再加上前端時間工作上比較忙,代碼和文章會不定時更新哦。得逼自己一把,爭取後續內容一個月內更完吧。

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