Unity第一人稱控制指令碼解析
阿新 • • 發佈:2019-01-09
一個第一人稱控制視角的指令碼,類似官方提供的指令碼格式,但是比官方的更通俗易懂。
沒什麼廢話了,直接上程式碼:
using UnityEngine; using System.Collections; //第一人稱控制需要剛體和碰撞器 [RequireComponent(typeof(Rigidbody))] [RequireComponent(typeof(CapsuleCollider))] public class PlayerController : MonoBehaviour { //把運動相關的引數,獨立出來 [System.Serializable] public class MoveSetting { public float ForwarSpeed = 5f; public float BackSpeed = 3f; public float HorizonSpeed = 4f; public float RunValue = 2f; public float JumpForce = 50f; } //把視角相關的獨立出來 [System.Serializable] public class MouseLook { public float XSensitive = 2f; public float YSensitive = 2f; } public MoveSetting moveSet; public MouseLook CameraSet; //當前速度 private float currentSpeed; //一段跳 private bool m_jump; //二段跳 private bool m_jump2; //第一人稱,膠囊碰撞 private CapsuleCollider m_capsule; //第一人稱,剛體 private Rigidbody m_rigidbody; private Camera m_camera; //相機的Transform(減少Update中transform的呼叫) private Transform m_camTrans; //主角的Transform private Transform m_chaTrans; ////攝像機尤拉角 //private Vector3 m_camRotate; ////主角的尤拉角 //private Vector3 m_chaRotate; //攝像機旋轉四元數 private Quaternion m_camQutation; //主角的旋轉四元數 private Quaternion m_chaQutation; //爬坡的速度曲線 public AnimationCurve SlopCurve; //是否在地面上 private bool m_isOnGround; //地面法線向量 private Vector3 curGroundNormal; // Use this for initialization void Start () { m_capsule = GetComponent<CapsuleCollider>(); m_rigidbody = GetComponent<Rigidbody>(); m_camera = Camera.main; m_camTrans = m_camera.transform; m_chaTrans = transform; //初始化引數 m_camQutation = m_camTrans.rotation; m_chaQutation = m_chaTrans.rotation; } // Update is called once per frame void Update () { //視角轉動 RotateView(); if (Input.GetKeyDown(KeyCode.Space)) { m_jump = true; } } //物理的運動,需要放到FixedUpdate中,固定幀率0.02秒,可在Edit.time中修改 void FixedUpdate() { DoMove(); } //檢視的旋轉 void RotateView() { float xRot = Input.GetAxis("Mouse Y") * CameraSet.YSensitive; float yRot = Input.GetAxis("Mouse X") * CameraSet.XSensitive; //尤拉角使用 //{ // m_camRotate += new Vector3(-xRot, 0f, 0f); //需要LocalEulerAnglers,否則攝像機和膠囊體會同時對相機旋轉起作用 // m_camTrans.localEulerAngles = m_camRotate; // m_camRotate += new Vector3(0f, yRot, 0f); // m_chaTrans.localEulerAngles = m_chaRotate; //} //四元數使用 { m_camQutation *= Quaternion.Euler(-xRot, 0f, 0f); //限制旋轉角度在【-90,90】內 m_camQutation = ClampRotation(m_camQutation); m_camTrans.localRotation = m_camQutation; m_chaQutation *= Quaternion.Euler(0f, yRot, 0f); m_chaTrans.localRotation = m_chaQutation; } } void DoMove() { //檢測地面 CheckGround(); //檢測前後左右輸入 Vector2 input = GetInput(); //更新當前速度,根據移動方向 CaculateSpeed(input); //判斷是否有移動的速度,沒有就不給剛體施加力 if ((Mathf.Abs(input.x) > float.Epsilon || Mathf.Abs(input.y) > float.Epsilon) && m_isOnGround) { //計算方向力 Vector3 desireMove = m_camTrans.forward * input.y + m_camTrans.right * input.x; //力在地面投影的向量的(單位向量) desireMove = Vector3.ProjectOnPlane(desireMove, curGroundNormal).normalized; desireMove.x = desireMove.x * currentSpeed; desireMove.y = 0; desireMove.z = desireMove.z * currentSpeed; //當前速度不能大於規定速度(Magnitude方法,需要開平方根,使用sqr節省運算) if (m_rigidbody.velocity.sqrMagnitude < currentSpeed * currentSpeed) { //給剛體施加(坡度計算後)的力 m_rigidbody.AddForce(desireMove * SlopeValue(), ForceMode.Impulse); } } if(m_isOnGround) { m_rigidbody.drag = 5f; jumpTime = 0; //一段跳 if (m_jump) { JumpUp(); } } else { if(m_jump) { jumpTime++; //二段跳 if (jumpTime < 2) { JumpUp(); } } } m_jump = false; } int jumpTime = 0; //跳躍方法 void JumpUp() { m_rigidbody.drag = 0f; //把剛體的上下方向的速度先歸零 m_rigidbody.velocity = new Vector3(m_rigidbody.velocity.x, 0f, m_rigidbody.velocity.z); m_rigidbody.AddForce(new Vector3(0, moveSet.JumpForce, 0), ForceMode.Impulse); } //檢測方向鍵輸入 Vector2 GetInput() { Vector2 input = new Vector2 ( Input.GetAxis("Horizontal"), Input.GetAxis("Vertical") ); //float horValue = Input.GetAxis("Horizontal"); //float verValue = Input.GetAxis("Vertical"); return input; } //計算 速度 void CaculateSpeed(Vector2 _input) { currentSpeed = moveSet.ForwarSpeed; //橫向移動 if(Mathf.Abs(_input.x)>float.Epsilon) { currentSpeed = moveSet.HorizonSpeed; } //前進後退 else if (_input.y < 0) { currentSpeed = moveSet.BackSpeed; } //Shift跑加速 if (Input.GetKey(KeyCode.LeftShift)) { currentSpeed *= moveSet.RunValue; } //Ctrl下蹲減速 } //爬坡引數 float SlopeValue() { float angle = Vector3.Angle(curGroundNormal,Vector3.up); float value = SlopCurve.Evaluate(angle); return value; } //檢測地面 void CheckGround() { RaycastHit hit; //球形碰撞檢測(第9個方法) if (Physics.SphereCast(m_capsule.transform.position,m_capsule.radius,Vector3.down,out hit,((m_capsule.height / 2 - m_capsule.radius)+0.01f))) { //獲取碰撞位置的發現向量 curGroundNormal = hit.normal; m_isOnGround = true; } else { curGroundNormal = Vector3.up; m_isOnGround = false; } } void CheckBuffer() { RaycastHit hit; float speed = m_rigidbody.velocity.y; if (speed < 0) { if (Physics.SphereCast(m_capsule.transform.position, m_capsule.radius, Vector3.down, out hit, ((m_capsule.height / 2 - m_capsule.radius) + 1f))) { speed *= 0.5f; m_rigidbody.velocity = new Vector3(m_rigidbody.velocity.x, speed, m_rigidbody.velocity.z); } } } //四元數俯角,仰角限制 Quaternion ClampRotation(Quaternion q) { //四元數的xyzw,分別除以同一個數,只改變模,不改變旋轉 q.x /= q.w; q.y /= q.w; q.z /= q.w; q.w = 1; /*給定一個尤拉旋轉(X, Y, Z)(即分別繞x軸、y軸和z軸旋轉X、Y、Z度),則對應的四元數為 x = sin(Y/2)sin(Z/2)cos(X/2)+cos(Y/2)cos(Z/2)sin(X/2) y = sin(Y/2)cos(Z/2)cos(X/2)+cos(Y/2)sin(Z/2)sin(X/2) z = cos(Y/2)sin(Z/2)cos(X/2)-sin(Y/2)cos(Z/2)sin(X/2) w = cos(Y/2)cos(Z/2)cos(X/2)-sin(Y/2)sin(Z/2)sin(X/2) */ //得到推導公式[尤拉角x=2*Aan(q.x)] float angle = 2 * Mathf.Rad2Deg * Mathf.Atan(q.x); //限制速度 angle = Mathf.Clamp(angle,-90f,90f); //反推出q的新x的值 q.x = Mathf.Tan(Mathf.Deg2Rad * (angle / 2)); return q; } }