黑魂復刻遊戲的攝像機控制——Unity隨手記
今天實現的內容:
滑鼠輸入
在沒有引入手柄操作前,將由滑鼠來控制攝像機旋轉,首先我們要獲取到滑鼠輸入。
public class PlayerInput : MonoBehaviour { // 攝像機控制軸 public string cameraAxisX; public string cameraAxisY; // 攝像機控制訊號 public float cameraUp; public float cameraRight; // ... void Update() { // 攝像機訊號 cameraUp = Input.GetAxis(cameraAxisY); cameraRight= Input.GetAxis(cameraAxisX); // ... } }
攝像機方案設計
老師的方案為,將攝像機掛載到遊戲物件下作為子物體,同時在攝像機的上一級新增一個新物件CameraHandle作為攝像機的父物體。如果我們要旋轉,就要旋轉整個玩家的遊戲物件,上下旋轉只需要旋轉CameraHandle就行了。
這是新增指令碼CameraController ,掛載到Camera上。
using System.Collections; using System.Collections.Generic; using UnityEngine; public classCameraController : MonoBehaviour { // 靈敏度 public float horizontalSensitivity; public float verticalSensitivity; // 輸入模組 public PlayerInput pi; // PlayerController遊戲物件 private GameObject playerHandle; // CameraHandle遊戲物件 private GameObject cameraHandle; // 用於儲存CameraHandle的尤拉角X值private float temp_eulerX; void Awake() { cameraHandle = transform.parent.gameObject; playerHandle = cameraHandle.transform.parent.gameObject; pi = playerHandle.GetComponent<PlayerInput>(); } // Update is called once per frame void Update() { // 左右旋轉時旋轉PlayerHandle playerHandle.transform.Rotate(Vector3.up, pi.cameraRight * horizontalSensitivity * Time.deltaTime); // 上下旋轉時旋轉CameraHandle temp_eulerX -= pi.cameraUp * verticalSensitivity * Time.deltaTime; // 限制俯仰角 temp_eulerX = Mathf.Clamp(temp_eulerX, -40, 30); // 賦值localEulerAngles cameraHandle.transform.localEulerAngles = new Vector3(temp_eulerX , 0, 0); } }
旋轉攝像機時的角色模型控制
當攝像機旋轉時,角色模型不要跟著旋轉。辦法很簡單,將模型在攝像機旋轉前的尤拉角儲存下來,在攝像機旋轉後再將之前儲存的尤拉角賦值回去就行了。所以這兩個操作的位置千萬別弄錯。
// 角色模型遊戲物件 private GameObject model; void Awake() { // ... model = playerHandle.GetComponent<PlayerController>().model; } // Update is called once per frame void Update() { // 得到攝像機旋轉前的模型尤拉角 Vector3 temp_modelEuler = model.transform.eulerAngles; // 攝像機旋轉... // 攝像機旋轉後將模型原來的尤拉角再賦給模型 model.transform.eulerAngles = temp_modelEuler; }
當你這樣做以後,會有一個驚喜發現,當你轉動攝像機而不移動角色時,模型不會動,而當你移動時,模型會自動旋轉到攝像機的朝向。原因是因為當你旋轉攝像機時,PlayerController遊戲物件也進行了旋轉,這時整個玩家的前方已經變了。當移動時,會執行旋轉程式碼,這時我們往前走,前方就是攝像機的朝向。所以模型會轉到攝像機的朝向。
注意下面程式碼的dirVec為根據PlayerController遊戲物件的方向得到的玩家移動方向,所以前方是PlayerController的前方。當旋轉攝像機時,實際上是旋轉PlayerController物件,然後攝像機跟著旋轉。
// 計算玩家的方向 這個transform.forward是PlayerInput掛載的遊戲物件,也就是PlayerController的transform.forward // 所以dirVec得到的是以PlayerController的transform.forward為前方的方向 dirVec = m_dirAxis.x * transform.right + m_dirAxis.y * transform.forward; // ... // 只在有速度時能夠旋轉 防止原地旋轉 if (pi.dirMag > 0.1f) { // 運用旋轉 使用Slerp進行效果優化 model.transform.forward = Vector3.Slerp(model.transform.forward, pi.dirVec, 0.3f); }
攝像機延遲移動
為了更好的視覺效果,我們常常希望攝像機能相對於角色的移動有一個延遲移動。要實現這種效果,首先我們在攝像機上級再新增一個父物件CameraPos,用來作為攝像機的位置。好了,現在Camera是不是PlayerController的遊戲物件都可以了。但是CameraController指令碼要掛載到CameraPos身上了。
現在,我們要在CameraController中獲取到Camera遊戲物件,再通過CameraPos的位置資訊來控制Camera遊戲物件的位置。
// 攝像機遊戲物件的移動和旋轉 camera.transform.eulerAngles = this.transform.eulerAngles; // 攝像機的位置通過Lerp來實現一種延遲移動的效果 camera.transform.position = Vector3.SmoothDamp( camera.transform.position, this.transform.position, ref temp_dampValue, 0.1f);