一個標准的第三人稱角色移動控制
- 按下W面向前移動; 按下S面向后移動; 按下A面向左移動; 按下D面向右移動
- 按下LeftShift加速移動
- Player靜止時攝像頭自由旋轉觀察角色
- 移動時角色rotation跟隨鼠標 , 也就是鼠標能控制角色移動方向
層級面板目錄結構
Player(PlayerControl掛載在此處)
模型 : 模型在Player的子目錄下
Target : Target是個空物體, 大概在角色頭部位置, 用於附着相機對象
Camera(CameraControl掛載在此處) : 就是游戲的主攝像機 , 在空物體Target的子目錄下
其中Player要有Animator組件, Rigidbody組件, Capsule Collider組件和自定義腳本PlayerControl組件
Camera要有自定義腳本CameraControl組件
之所以弄成這么個層級 , 主要是為了分離角色和攝像機 , 攝像機可以自由旋轉而不影響角色 , 那個Target大概就是為了能讓相機正沖角色后腦勺.
怎樣實現?理一下思路
PlayerControl腳本的內容
- 獲取WASD輸入 , 根據輸入改變整個Player父對象的rotation , 並且控制角色移動.
- 最后要做到角色平滑轉向 , 還要實現0速到移動速度的平滑轉向
需要在類中聲明的變量
//***變量聲明區域***/ float walkSpeed = 5; float runSpeed = 10; Animator animator;//創建一個animator變量以待賦值(在start()方法里賦值) public float turnSmoothTime = 0.13f;//設定角色轉向平滑時間 float turnSmoothVelocity;//平滑函數需要這么一個平滑加速度, 不需要為他賦值, 但是需要把這個變量當參數傳入 public float speedSmoothTime = 0.13f;//用於平滑速度 float speedSmoothVelocity; float currentSpeed; public Transform cameraT;
//cameraT這個本來是想用cameraT = Camera.main.transform;在start函數賦值的,但是不知道這個函數是不是過時了,賦值不上,所以還是采用public的方式,在編輯器拖動給變量賦值
最普通的WASD移動方式
void Update()
{
float hor = Input.GetAxis("Horizontal");
float ver = Input.GetAxis("Vertical"); Vector3 PlayerMovement = new vector3(hor,0f,ver)*targetSpeed*Time.deltaTime; transform.Translate(PlayerMovement,Space.Self);
}
威力加強版未加入平滑函數的WASD移動方式
void Update()
{ //***WASD輸入***// Vector2 input = new Vector2(Input.GetAxisRaw("Horizontal"),Input.GetAxisRaw("Vertical")); Vector2 inputDir = input.normalized;//反正就是返回一個單位長度的這個變量 if(inputDir != Vector2.zero)
//可能是因為鍵盤自動輸入0, 玩家不輸入的時候角色就會自動面向正方向, 所以加一個判定, 輸入為0的話就是沒輸入, 所以就不要轉向 {//未加入平滑函數的轉向 transform.eulerAngles =Vector3.up* Mathf.Atan2(inputDir.x,inputDir.y)*Mathf.Rad2Deg;
//關於上邊這句話詳情看unity坐標分析(還沒寫),這其實就是根據玩家鍵盤WASD算出來的旋轉角度 } transform.Translate(transform.forward*targetSpeed*Time.deltaTime,Space.World);
//因為上邊更改了transform的朝向,所以可以直接向transform的正方向前進就能達到按照鍵盤輸入前進的效果.
}
威力加強版加入平滑和鼠標干預的WASD移動方式
void Update()
{
Vector2 input = new Vector2(Input.GetAxisRaw("Horizontal"),Input.GetAxisRaw("Vertical")); Vector2 inputDir = input.normalized; bool running = Input.GetKey(KeyCode.LeftShift);//根據是否按下左shift設定一個布爾變量在下一句設定是奔跑速度還是走路速度
float targetSpeed = ((running)?runSpeed:walkSpeed)*inputDir.magnitude;
//根據布爾變量獲取速度后乘以輸入向量的長度,當沒有任何鍵盤輸入時,這個長度是0,有任何輸入時長度都是1,所以其實是用來在鍵盤沒有輸入時將目標速度置0用的 transform.Translate(transform.forward*targetSpeed*Time.deltaTime,Space.World); //***轉向部分代碼***// if(inputDir != Vector2.zero) {
//***加入鼠標干預***// float targetRotation = Mathf.Atan2(inputDir.x,inputDir.y)*Mathf.Rad2Deg+cameraT.eulerAngles.y;
//這個cameraT是引用的主攝像機(也不知道具體是不是引用,等回學校看看headfirstC#),傳入了主攝像機的y值,而在后面攝像機的控制代碼中,這個y值是受鼠標控制的 transform.eulerAngles = Vector3.up*Mathf.SmoothDampAngle(transform.eulerAngles.y,targetRotation,ref turnSmoothVelocity,turnSmoothTime); //上邊這個函數是角度漸變, 也可以叫平滑吧, 這個ref是什么意思, 以后再說, 還有這個turnSmoothVelocity也以后再說 }
}
動畫狀態機代碼控制
float animationSpeedPercent = ((running)?0.75f:0.25f)*inputDir.magnitude;//通過布爾變量給這個變量賦值,下一句要用 animator.SetFloat("speedPercent",animationSpeedPercent,speedSmoothTime,Time.deltaTime); //1. 通過調用animator變量的setfloat函數給混合狀態機的speedPercent參數賦值 //2. 這個函數有四個參數, 第三個是自帶的平滑參數, 也就是說這個aimator自帶平滑功能,直接輸入參數就可以調用
需要創建一個狀態機, 然后創建一個混合狀態圖層, 新建一個控制參數, 就是這個speedPercent, 導入角色動畫, 靜止動畫, 走路動畫, 跑步動畫啥的, 就能通過給這個speedPercent賦值來控制角色動畫
這個動畫狀態機后面還要專門整理, 包括動畫融合等內容
攝像機控制(還需要補充層級面板配置結構)
void LateUpdate()//攝像機控制代碼一般放在lateupdate函數里 { CameraControl(); } void CameraControl() { mouseX +=Input.GetAxis("Mouse X")*rotationSpeed; mouseY -=Input.GetAxis("Mouse Y")*rotationSpeed; mouseY = Mathf.Clamp(mouseY,-35,60);//限制攝像機旋轉角度 //transform.LookAt(Target);這句話沒啥用(我也不知道為啥就自動看着Target Target.rotation = Quaternion.Euler(mouseY,mouseX,0); //Player.rotation = Quaternion.Euler(0,mouseX,0);這句話會因為和PlayerControl內代碼同時改變Player角度而引發攝像機劇烈抖動
//現在PlayerControl腳本里旋轉那里加入了攝像機的y軸的值,所以不需要用這句話來畫蛇添足了
}
全部代碼保存(如果有一天我真要用這份代碼 , 最好還是能直接復制粘貼全部代碼)
PlayerController代碼
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerController : MonoBehaviour { /***變量聲明區域***/ float walkSpeed = 5; float runSpeed = 10; Animator animator;//創建一個animator變量以待賦值(在start()方法里賦值) public float turnSmoothTime = 0.13f;//設定角色轉向平滑時間 float turnSmoothVelocity;//平滑函數需要這么一個平滑加速度, 不需要為他賦值, 但是需要把這個變量當參數傳入 public float speedSmoothTime = 0.13f;//用於平滑速度 float speedSmoothVelocity; float currentSpeed; public Transform cameraT; // Start is called before the first frame update void Start() { animator = GetComponent<Animator>(); //cameraT = Camera.main.transform; } // Update is called once per frame void Update() { //***WASD輸入***// Vector2 input = new Vector2(Input.GetAxisRaw("Horizontal"),Input.GetAxisRaw("Vertical"));//獲取鍵盤輸入 Vector2 inputDir = input.normalized;//反正就是返回一個單位長度的這個變量 //***角色移動部分***// bool running = Input.GetKey(KeyCode.LeftShift);//按了左shift則bool變量running就是1 float targetSpeed = ((running)?runSpeed:walkSpeed)*inputDir.magnitude; //Vector3 PlayerMovement = new Vector3(hor,0f,ver)*targetSpeed*Time.deltaTime; transform.Translate(transform.forward*targetSpeed*Time.deltaTime,Space.World);//讓游戲角色位置移動 //transform.Translate(PlayerMovement,Space.Self); //***轉向部分***// if(inputDir != Vector2.zero) //可能是因為鍵盤自動收入0, 玩家不輸入的時候角色就會自動面向正方向, 所以加一個判定, 輸入為0的話就是沒輸入, 所以就不要轉向 {//平滑轉向代碼 float targetRotation = Mathf.Atan2(inputDir.x,inputDir.y)*Mathf.Rad2Deg+cameraT.eulerAngles.y;//這就是根據玩家鍵盤輸入算出來的目標轉向角度(y軸的) transform.eulerAngles = Vector3.up*Mathf.SmoothDampAngle(transform.eulerAngles.y,targetRotation,ref turnSmoothVelocity,turnSmoothTime); //上邊這個函數是角度漸變, 也可以叫平滑吧, 這個ref是什么意思還存疑, 以后肯定能解決, 還有這個turnSmoothVelocity是什么意思以后遲早能知道, 不急於一時
//這個ref就是引用參數 , 學點C#就知道了 , 用這個參數把方法內的數據給保存出來 } /***位置運動部分***/ //這個inputDir是個單位向量,inputDir.magnitude是他的長度,有任何輸入的時候單位向量的長度都是1, 在鍵盤沒有輸入的時候這個長度就是0 //其實之所以乘以這個長度就是為了能夠在玩家沒有輸入的時候把速度變成0 //currentSpeed = Mathf.SmoothDamp(currentSpeed,targetSpeed,ref speedSmoothVelocity,speedSmoothTime); //看起來這個函數SmoothDamp以及上邊的SmoothDampAngle一樣, 他們的第一個參數其實是被賦值的, 直接把空參數傳進去, 就能獲得合適的值 float animationSpeedPercent = ((running)?0.75f:0.25f)*inputDir.magnitude;//通過布爾變量給動畫狀態機控制變量賦值 animator.SetFloat("speedPercent",animationSpeedPercent,speedSmoothTime,Time.deltaTime); //1. 通過調用animator變量的setfloat函數給混合狀態機的speedPercent參數賦值 //2. 這個函數有四個參數, 第三個是自帶的平滑參數, 可以通過給這個參數賦值來實現動畫狀態機自動平滑動作 } }
CameraController代碼
using System.Collections; using System.Collections.Generic; using UnityEngine; public class ThirdPersonCamera : MonoBehaviour { //****變量聲明部分*** float yaw; //yaw的中文意思是航向, 也就是物體繞y軸旋轉角度 float pitch;//pitch是俯仰, 也就是物體繞x軸旋轉角度 public float mouseSensitivity = 10f;//鼠標靈敏度,因為鼠標要控制攝像機轉向 public Transform target,player; //1. 這里想要獲取一個對象的transform組件的實例, 先聲明一個transform以待賦值, 要獲取的是游戲角色子對象, 用於確定相機位置 //2. 這個target會通過編輯器賦值, 就是我創建的CameraPosition子對象, 但是其實直接把相機作為角色的子對象豈不是更好?? //public float dstFromTarget = 2;//就是相機就距離CameraPosition這個對象兩米遠 // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } void LateUpdate() { yaw+=Input.GetAxis("Mouse X")*mouseSensitivity; pitch-=Input.GetAxis("Mouse Y")*mouseSensitivity; Vector3 targetRotation = new Vector3(pitch,yaw,0);//顯然我們是不需要攝像機左右傾斜的, 所以z軸鎖定為0 Vector3 playerRotation = new Vector3(0,yaw,0);//顯然我們是不需要攝像機左右傾斜的, 所以z軸鎖定為0 //***設置相機旋轉*** //這句話必須要放在下邊那句給攝像機角度賦值的語句前邊才有用 //transform.LookAt(target.position); //transform.eulerAngles = targetRotation; // player.transform.eulerAngles = playerRotation; //***設置相機位置*** //transform.position = target.position-transform.forward*dstFromTarget; //總之是Vector3變量做加減, target.position說的是cameraPosition子對象的位置,是在編輯器里賦值的, 這個位置的x軸減兩米就是本對象位置(也就是相機位置) target.rotation = Quaternion.Euler(pitch,yaw,0);//設置攝像機旋轉 // if(Input.GetKey(KeyCode.W)) // { // player.rotation = Quaternion.Euler(0,yaw,0);//設置角色旋轉 // } } }