文章目錄
- 今天實作的內容:
- 新增鎖定輸入
- 鎖定和解鎖的代碼邏輯和攝像機代碼邏輯
- 鎖定的提示UI
- 鎖定的控制器代碼邏輯
- 鎖定的影片
- 鎖定狀態下的翻滾和跳躍
- 自動解除鎖定
- BUG以及缺陷:
- 值得注意的:
今天實作的內容:
新增鎖定輸入
為輸入模塊添加鎖定鍵和鎖定信號,更新信號,
--- IPlayerInput
public bool lockOn; //鎖定信號
--- JoystickInput
public MyButton buttonLockOn = new MyButton(); //鎖定鍵
// Update is called once per frame
void Update()
{
// 更新按鍵
buttonLockOn.Tick(Input.GetButton(btnRS));
// 鎖定信號
lockOn = buttonLockOn.onPressed;
鎖定和解鎖的代碼邏輯和攝像機代碼邏輯
首先,要鎖定目標,先要確定要鎖定的目標是什么,我們使用Physics.OverlapBox來得到指定盒子區域內的碰撞體,

鎖定需要始終將攝像機對準目標,所以我們在CameraController中添加新方法LockOn_or_Unlock用來處理鎖定解鎖邏輯,然后在PlayerController中呼叫該方法,
// 攝像機鎖定/解除鎖定
public void LockOn_or_Unlock()
{
// 嘗試去鎖定一個
Vector3 tmp_modelCenter = modelGO.transform.position + Vector3.up; //獲得模型的中心
Vector3 tmp_boxCenter = tmp_modelCenter + modelGO.transform.forward * 5.0f; //得到OverlapBox的中心
Collider[] cols = Physics.OverlapBox(tmp_boxCenter, //通過OverlapBox嘗試獲取范圍內Enemy標簽的碰撞體
new Vector3(1.0f, 1.0f, 5.0f),
modelGO.transform.rotation,
LayerMask.GetMask("Enemy"));
if (cols.Length != 0 && lockTarget == null)
{
//如果得到碰撞體并且沒有鎖定目標 將第一個賦值給lockTarget
lockTarget = cols[0].gameObject;
lockonIcon.enabled = true;
// 設定LockonIcon的圖片位置
lockonIcon.rectTransform.position = Camera.main.WorldToScreenPoint(lockTarget.transform.position);
isLockon = true;
}
else
{
// 如果沒有檢測到任何東西或者已經鎖定了目標 將lockTarget設定為null
lockTarget = null;
lockonIcon.enabled = false;
isLockon = false;
}
}
LockOn_or_Unlock會使用OverlapBox查看給定范圍內是否有碰撞體并且是否已經鎖定目標,如果找到了碰撞體,將陣列中的第一個給lockTarget,如果沒找到或已經鎖定了目標則設定為null,
如果攝像機沒有鎖定,則在FixedUpdate中將playerController按輸入設定旋轉,如果鎖定了,則攝像機要計算模型和鎖定目標的方向向量,把這個方向向量交給攝像機,在我們的架構中,直接用來設定PlayerController的forward就行,記得要將該向量的y軸設定為0,最后,我們會在鎖定時讓攝像機始終看向目標的“腳底”來讓視角更貼近黑魂,
void FixedUpdate()
{
if (lockTarget == null) //如果沒有鎖定目標 按輸入控制PlayerController旋轉
{
// 得到攝像機旋轉前的模型歐拉角
Vector3 temp_modelEuler = modelGO.transform.eulerAngles;
// 左右旋轉時直接旋轉PlayerHandle 攝像機也會跟著
playerController.transform.Rotate(Vector3.up, current_pi.cameraRight * horizontalSensitivity * Time.fixedDeltaTime);
// 攝像機旋轉后將模型原來的歐拉角再賦給模型 保證模型不動
modelGO.transform.eulerAngles = temp_modelEuler;
// 上下旋轉時旋轉CameraHandle
temp_eulerX -= current_pi.cameraUp * verticalSensitivity * Time.fixedDeltaTime;
// 限制俯仰角
temp_eulerX = Mathf.Clamp(temp_eulerX, -40, 30);
// 賦值localEulerAngles
cameraHandle.transform.localEulerAngles = new Vector3(temp_eulerX, 0, 0);
}
else //如果鎖定了目標 計算從模型到鎖定目標的方向向量 將PlayerController的forward設定為該向量
{
// 計算方向向量
Vector3 temp_forward = lockTarget.transform.position - modelGO.transform.position;
// 將向量的y軸設定為0 PlayerController的Y軸不需要旋轉
temp_forward.y = 0;
// 設定PlayerController的forward
playerController.transform.forward = temp_forward;
// 鎖定攝像機看目標的“腳底”
cameraHandle.transform.LookAt(lockTarget.transform.parent.position);
}
// 攝像機的位置通過SmoothDamp來實作一種延遲移動的效果
cameraGO.transform.position = Vector3.SmoothDamp(
cameraGO.transform.position, this.transform.position, ref temp_dampValue, 0.1f * current_pi.dirMag);
// 讓攝像機保持看向一個位置 防止位置進行SmoothDamp時的抖動
cameraGO.transform.LookAt(cameraHandle.transform);
}
鎖定的提示UI
為了在游戲中提示玩家當前鎖定了哪個目標,我們要加入一個UI,

需要在沒有進行鎖定時將其enabled設定為false,只在鎖定時將其設定為true,最后將UI的位置放到鎖定的物件身上,
private void Update()
{
if(isLockon)
{
// 更新LockonIcon的圖片位置
lockonIcon.rectTransform.position = Camera.main.WorldToScreenPoint(lockTarget.transform.position);
}
}
鎖定的控制器代碼邏輯
鎖定時控制器中的代碼和攝像機類似,一個是設定攝像機對準目標,一個是設定模型對準目標,只有當模型對準目標,我們才能引入鎖定時相應的影片,同樣重要的還有鎖定狀態下的移動計算,由于鎖定時模型和PlayerController的方向都已鎖死,此時方向的判斷不根據模型了,而是直接來自輸入的產生方向,我們在輸入模塊中用dirVec來表示,
// -- PlayerController --
// Update is called once per frame
void Update()
{
// ...
// 觸發鎖定
if (current_pi.lockOn)
{
camCon.LockOn_or_Unlock();
}
if (camCon.isLockon) //攝像機已鎖定 將模型旋轉設定為朝向目標
{
// 由于已經在CameraController設定了PlayerController物件的旋轉所以直接給模型就行
model.transform.forward = this.transform.forward;
// 計算鎖定時的移動
if (!lockPlanar)
{
m_planarVec = current_pi.dirVec * walkSpeed * (current_pi.run ? runMultiplier : 1.0f);
}
}
else //攝像機沒有鎖定 根據輸入控制模型旋轉
{
// 只在有速度時能夠旋轉 防止原地旋轉
if (current_pi.dirMag > 0.1f)
{
// 運用旋轉 使用Slerp進行效果優化
model.transform.forward = Vector3.Slerp(model.transform.forward, current_pi.dirVec, 0.3f);
}
// 計算沒有鎖定時的移動量
if (!lockPlanar)
{
m_planarVec = current_pi.dirMag * model.transform.forward * walkSpeed * (current_pi.run ? runMultiplier : 1.0f);
}
}
}
總結一下,由于我們水平旋轉攝像機是靠旋轉PlayerController游戲物件來實作,所以鎖定目標將PlayerController游戲物件的forward指向目標就行,同樣的,由于已經設定了PlayerController在鎖定時指向目標,要將模型指向目標只需要將PlayerController物件的forward給模型就行,移動的方向判斷需要通過輸入產生的方向,也就是我們之前做輸入模塊時寫的dirVec來判斷,
鎖定的影片
之前說要將模型始終對準目標才能引入鎖定對應的影片,鎖定對應的影片就是始終朝向一個方向時的前進后退和左右側步,要加入這些影片,我們需要將ground混合樹修改為2D Freeform型別,原來的移動依然只由forward引數操控,我們要額外加入right引數,在鎖定時,我們將通過修改right引數操控左右側步,以及調整forward為負值來操控后退,這樣還不需要加入新混合樹就可以實作原來的移動和鎖定時的移動了,

至于影片引數的代碼,結構和之前的邏輯類似,在鎖定狀態下要調整的是forward和right,非鎖定狀態下只調整forward就行,具體代碼如下:
// Update is called once per frame
void Update()
{
// --------------------- 影片引數 ---------------------
if (camCon.isLockon)
{
Vector3 localDirVec = this.transform.InverseTransformDirection(current_pi.dirVec);
anim.SetFloat("forward", localDirVec.z * ((current_pi.run) ? 2.0f : 1.0f));
anim.SetFloat("right", localDirVec.x * ((current_pi.run) ? 2.0f : 1.0f));
}
else
{
anim.SetFloat("forward", current_pi.dirMag * Mathf.Lerp(anim.GetFloat("forward"), (current_pi.run) ? 2.0f : 1.0f, 0.5f));
anim.SetFloat("right", 0);
}
// ...
}
鎖定狀態下的翻滾和跳躍
由于鎖定狀態下模型方向判斷和之前不一樣了,所以我們需要重新設計鎖定狀態下的翻滾和后跳,設計方案是,設定一個新的bool值trackDirection,當該bool為true時,將追蹤m_planarVec,提供給翻滾后跳作為方向,當我們開始跳躍或翻滾時,將trackDirection設定為true,落地時設定為false,
// 進入Base層的影片節點roll時執行的方法
// 通過PlayerController影片機中的roll節點上掛載的FSMOnEnter呼叫
public void OnRollEnter()
{
// 關閉輸入模塊
current_pi.inputEnabled = false;
// 鎖定平臺移動計算
lockPlanar = true;
// 運用翻滾沖量
m_planarVec = m_planarVec.normalized * rollThrust;
// 追蹤dirVec
trackDirection = true;
}
當鎖定時,要旋轉模型的時候,如果trackDirection為true,則按照m_planarVec作為方向,
// --------------------- 模型旋轉和位移 ---------------------
if (camCon.isLockon) //攝像機已鎖定 將模型旋轉設定為朝向目標
{
if(trackDirection) //當前是否追蹤方向
{
model.transform.forward = m_planarVec.normalized;
}
else
{
// 由于已經在CameraController設定了PlayerController物件的旋轉所以直接給模型就行
model.transform.forward = this.transform.forward;
}
最后,將影片機調整一下,將right引數考慮進翻滾和跳躍,

自動解除鎖定
到目前為止,我們的鎖定功能基本刊用了,但是每當玩家想要解除鎖定時必須再次按下鎖定鍵,否則無論跑出多遠都會鎖定到目標上,從游戲設計的角度來講,玩家跑出很遠的距離一般都是希望取消鎖定的,而且如果在很遠距離都能鎖定敵人會導致一些追蹤魔法能打很遠,所以最后我們再做一個自動解除鎖定功能,
通過計算玩家和目標的距離,如果距離大于某個值,則自動執行解鎖,
private void Update()
{
// 如果鎖定了目標
if(isLockon)
{
// 更新LockonIcon的圖片位置
lockonIcon.rectTransform.position = Camera.main.WorldToScreenPoint(lockTarget.transform.position);
// 超出某個距離自動解除鎖定
if (Vector3.Distance(lockTarget.transform.position, modelGO.transform.position) > 10f)
{
lockTarget = null;
lockonIcon.enabled = false;
isLockon = false;
}
}
}
BUG以及缺陷:
還是老問題,我們使用的影片在放入影片機混合樹之后產生了一些不協調,還是需要美術大大們幫忙才行,如果硬是要解決,下圖展示了老師提供的方案,聊勝于無,

黑魂的鎖定機制還是比我們現在做的這個復雜的,比如鎖定目標的選取是選取離你螢屏中間最近的目標而不是模型正前方目標找到的所有目標組成佇列的第一個,還有就是在鎖定狀態時推動視角搖桿會將鎖定的目標切換到相應方向的最近的其他目標,
值得注意的:
無
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/286492.html
標籤:其他
