天天看點

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

文章目錄

    • 階段目标
    • 效果示範
    • 單機版的結構
    • 聯機版的結構
    • 戰場的跳轉
    • 移動同步
    • 存在的問題
    • 後續内容
    • 源碼位址

階段目标

  • 從大廳進入戰場
  • 用戶端修改為雙人版本
  • 戰場内移動同步的實作

效果示範

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

單機版的結構

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

GameManager作為一個全局的遊戲管理器存在,每個具體的坦克是由TankManager類來進行管理,其中,坦克具體的移動和射擊邏輯是分别交由TankMovment和TankShooting去完成。

聯機版的結構

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

聯機版和單機版在類的設計實作上是一樣的,主要差別也就下面幾點:

  • 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。

跳轉戰場的時序圖如下:

說明下,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位址:位址在這裡

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