潛江網(wǎng)站建設(shè)如何提高搜索引擎優(yōu)化
文章目錄
- 功能簡(jiǎn)介
- 實(shí)現(xiàn)步驟
- 獲取看向的位置
- 獲取頭部的位置
- 修改頭部的朝向
- 限制旋轉(zhuǎn)角度
- 超出限制范圍時(shí)自動(dòng)回正
- 如何讓指定動(dòng)畫不受影響
功能簡(jiǎn)介
如圖所示,當(dāng)相機(jī)的視角轉(zhuǎn)動(dòng)時(shí),Avatar角色的頭部會(huì)同步轉(zhuǎn)動(dòng),看向視角的方向。
實(shí)現(xiàn)步驟
獲取看向的位置
Avatar看向的位置即相機(jī)前方一定距離的某個(gè)坐標(biāo),該距離偏大于相機(jī)與Avatar角色的距離即可,可以取100來代表:
//獲取看向的位置
private Vector3 GetLookAtPosition()
{//主相機(jī)前方100個(gè)單位的位置return mainCamera.transform.position + mainCamera.transform.forward * 100f;
}
獲取頭部的位置
頭部位置可以通過Animator
組件中的GetBoneTransform
接口來獲取
示例如下:
using UnityEngine;namespace SK.Framework.Avatar
{public class HeadTrack : MonoBehaviour{//動(dòng)畫組件[SerializeField] private Animator animator; private Camera mainCamera; //主相機(jī)private Transform head; //頭部private void Start(){mainCamera = Camera.main ?? FindObjectOfType<Camera>();head = animator.GetBoneTransform(HumanBodyBones.Head);}//獲取看向的位置private Vector3 GetLookAtPosition(){//主相機(jī)前方100個(gè)單位的位置return mainCamera.transform.position + mainCamera.transform.forward * 100f;}}
}
有了頭部的位置后,就可以計(jì)算頭部的高度,聲明一個(gè)變量headHeight
來記錄頭部高度:
headHeight = Vector3.Distance(transform.position, head.position);
修改頭部的朝向
有了看向的坐標(biāo)和頭部的坐標(biāo),就取得了看向的朝向,在LateUpdate
中賦值該頭部朝向,注意一定要使用LateUpdate
,因?yàn)锳nimator動(dòng)畫組件在控制Avatar各骨骼的朝向,使用LateUpdate
可以確保我們的旋轉(zhuǎn)值修改起作用。
using UnityEngine;namespace SK.Framework.Avatar
{public class HeadTrack : MonoBehaviour{//動(dòng)畫組件[SerializeField] private Animator animator; private Camera mainCamera; //主相機(jī)private Transform head; //頭部private float headHeight; //頭部的高度private void Start(){mainCamera = Camera.main ?? FindObjectOfType<Camera>();head = animator.GetBoneTransform(HumanBodyBones.Head);headHeight = Vector3.Distance(transform.position, head.position);}/// <summary>/// 看向某點(diǎn)/// </summary>/// <param name="position"></param>public void LookAtPosition(Vector3 position){//頭部位置Vector3 headPosition = transform.position + transform.up * headHeight;//朝向Quaternion lookRotation = Quaternion.LookRotation(position - headPosition);head.rotation = lookRotation;}private void LateUpdate(){Debug.DrawLine(transform.position + transform.up * headHeight, GetLookAtPosition());LookAtPosition(GetLookAtPosition());}//獲取看向的位置private Vector3 GetLookAtPosition(){//主相機(jī)前方100個(gè)單位的位置return mainCamera.transform.position + mainCamera.transform.forward * 100f;}}
}
如圖所示,我們已經(jīng)實(shí)現(xiàn)了頭部的轉(zhuǎn)向,但是旋轉(zhuǎn)值過大會(huì)導(dǎo)致反人類現(xiàn)象,因此需要將旋轉(zhuǎn)值進(jìn)行限制。
限制旋轉(zhuǎn)角度
//水平方向上的角度限制
[SerializeField] private Vector2 horizontalAngleLimit = new Vector2(-100f, 100f); //垂直方向上的角度限制
[SerializeField] private Vector2 verticalAngleLimit = new Vector2(-60f, 60f);
封裝一個(gè)角度標(biāo)準(zhǔn)化的函數(shù),當(dāng)角度大于180度時(shí)減360度,當(dāng)角度小于180度時(shí)加360度:
//角度標(biāo)準(zhǔn)化
private float NormalizeAngle(float angle)
{if (angle > 180) angle -= 360f;else if (angle < -180) angle += 360f;return angle;
}
封裝看向某點(diǎn)的函數(shù):
/// <summary>
/// 看向某點(diǎn)
/// </summary>
/// <param name="position"></param>
public void LookAtPosition(Vector3 position)
{//頭部位置Vector3 headPosition = transform.position + transform.up * headHeight;//朝向Quaternion lookRotation = Quaternion.LookRotation(position - headPosition);Vector3 eulerAngles = lookRotation.eulerAngles - transform.rotation.eulerAngles;float x = NormalizeAngle(eulerAngles.x);float y = NormalizeAngle(eulerAngles.y);x = Mathf.Clamp(x, verticalAngleLimit.x, verticalAngleLimit.y);y = Mathf.Clamp(y, horizontalAngleLimit.x, horizontalAngleLimit.y);Quaternion rotY = Quaternion.AngleAxis(y, head.InverseTransformDirection(transform.up));head.rotation *= rotY;Quaternion rotX = Quaternion.AngleAxis(x, head.InverseTransformDirection(transform.TransformDirection(Vector3.right)));head.rotation *= rotX;
}
超出限制范圍時(shí)自動(dòng)回正
當(dāng)角度超出限制的范圍時(shí),將頭部自動(dòng)回正,可以在GetLookAtPosition
函數(shù)中加入判斷,聲明autoTurnback
變量標(biāo)識(shí)是否自動(dòng)回正:
//獲取看向的位置
private Vector3 GetLookAtPosition()
{Vector3 position = mainCamera.transform.position + mainCamera.transform.forward * 100f;if (!autoTurnback) return position;Vector3 direction = position - (transform.position + transform.up * headHeight);Quaternion lookRotation = Quaternion.LookRotation(direction, transform.up);Vector3 angle = lookRotation.eulerAngles - transform.eulerAngles;float x = NormalizeAngle(angle.x);float y = NormalizeAngle(angle.y);bool isInRange = x >= verticalAngleLimit.x && x <= verticalAngleLimit.y&& y >= horizontalAngleLimit.x && y <= horizontalAngleLimit.y;return isInRange ? position : (transform.position + transform.up * headHeight + transform.forward);
}
加入插值運(yùn)算,使自動(dòng)回正時(shí)有過渡過程,代碼如下:
using UnityEngine;namespace SK.Framework.Avatar
{public class HeadTrack : MonoBehaviour{[Tooltip("動(dòng)畫組件"), SerializeField] private Animator animator; [Tooltip("水平方向上的角度限制"), SerializeField] private Vector2 horizontalAngleLimit = new Vector2(-70f, 70f); [Tooltip("垂直方向上的角度限制"), SerializeField] private Vector2 verticalAngleLimit = new Vector2(-60f, 60f);[Tooltip("超出限制范圍時(shí)自動(dòng)回正"), SerializeField] private bool autoTurnback = true;[Tooltip("插值速度"), SerializeField] private float lerpSpeed = 5f;private Camera mainCamera; //主相機(jī)private Transform head; //頭部private float headHeight; //頭部的高度private float angleX;private float angleY;private void Start(){mainCamera = Camera.main ?? FindObjectOfType<Camera>();head = animator.GetBoneTransform(HumanBodyBones.Head);headHeight = Vector3.Distance(transform.position, head.position);}/// <summary>/// 看向某點(diǎn)/// </summary>/// <param name="position"></param>public void LookAtPosition(Vector3 position){//頭部位置Vector3 headPosition = transform.position + transform.up * headHeight;//朝向Quaternion lookRotation = Quaternion.LookRotation(position - headPosition);Vector3 eulerAngles = lookRotation.eulerAngles - transform.rotation.eulerAngles;float x = NormalizeAngle(eulerAngles.x);float y = NormalizeAngle(eulerAngles.y);angleX = Mathf.Clamp(Mathf.Lerp(angleX, x, Time.deltaTime * lerpSpeed), verticalAngleLimit.x, verticalAngleLimit.y);angleY = Mathf.Clamp(Mathf.Lerp(angleY, y, Time.deltaTime * lerpSpeed), horizontalAngleLimit.x, horizontalAngleLimit.y);Quaternion rotY = Quaternion.AngleAxis(angleY, head.InverseTransformDirection(transform.up));head.rotation *= rotY;Quaternion rotX = Quaternion.AngleAxis(angleX, head.InverseTransformDirection(transform.TransformDirection(Vector3.right)));head.rotation *= rotX;}//角度標(biāo)準(zhǔn)化private float NormalizeAngle(float angle){if (angle > 180) angle -= 360f;else if (angle < -180) angle += 360f;return angle;}private void LateUpdate(){LookAtPosition(GetLookAtPosition());}//獲取看向的位置private Vector3 GetLookAtPosition(){Vector3 position = mainCamera.transform.position + mainCamera.transform.forward * 100f;if (!autoTurnback) return position;Vector3 direction = position - (transform.position + transform.up * headHeight);Quaternion lookRotation = Quaternion.LookRotation(direction, transform.up);Vector3 angle = lookRotation.eulerAngles - transform.eulerAngles;float x = NormalizeAngle(angle.x);float y = NormalizeAngle(angle.y);bool isInRange = x >= verticalAngleLimit.x && x <= verticalAngleLimit.y && y >= horizontalAngleLimit.x && y <= horizontalAngleLimit.y;return isInRange ? position : (transform.position + transform.up * headHeight + transform.forward); }}
}
如何讓指定動(dòng)畫不受影響
如果我們想要在播放某個(gè)動(dòng)畫時(shí)不讓頭部轉(zhuǎn)動(dòng),可以通過Tag標(biāo)簽來解決,如下圖所示,為Hi動(dòng)畫增加IgnoreHeadTrack
標(biāo)簽:
在代碼中加入判斷:
//獲取看向的位置
private Vector3 GetLookAtPosition()
{AnimatorStateInfo animatorStateInfo = animator.GetCurrentAnimatorStateInfo(0);if (animatorStateInfo.IsTag("IgnoreHeadTrack"))return transform.position + transform.up * headHeight + transform.forward;Vector3 position = mainCamera.transform.position + mainCamera.transform.forward * 100f;if (!autoTurnback) return position;Vector3 direction = position - (transform.position + transform.up * headHeight);Quaternion lookRotation = Quaternion.LookRotation(direction, transform.up);Vector3 angle = lookRotation.eulerAngles - transform.eulerAngles;float x = NormalizeAngle(angle.x);float y = NormalizeAngle(angle.y);bool isInRange = x >= verticalAngleLimit.x && x <= verticalAngleLimit.y&& y >= horizontalAngleLimit.x && y <= horizontalAngleLimit.y;return isInRange ? position : (transform.position + transform.up * headHeight + transform.forward);
}
完整代碼:
using UnityEngine;namespace SK.Framework.Avatar
{public class HeadTrack : MonoBehaviour{[Tooltip("動(dòng)畫組件"), SerializeField] private Animator animator; [Tooltip("水平方向上的角度限制"), SerializeField] private Vector2 horizontalAngleLimit = new Vector2(-70f, 70f); [Tooltip("垂直方向上的角度限制"), SerializeField] private Vector2 verticalAngleLimit = new Vector2(-60f, 60f);[Tooltip("超出限制范圍時(shí)自動(dòng)回正"), SerializeField] private bool autoTurnback = true;[Tooltip("插值速度"), SerializeField] private float lerpSpeed = 5f;private Camera mainCamera; //主相機(jī)private Transform head; //頭部private float headHeight; //頭部的高度private float angleX;private float angleY;private void Start(){mainCamera = Camera.main ?? FindObjectOfType<Camera>();head = animator.GetBoneTransform(HumanBodyBones.Head);headHeight = Vector3.Distance(transform.position, head.position);}/// <summary>/// 看向某點(diǎn)/// </summary>/// <param name="position"></param>public void LookAtPosition(Vector3 position){//頭部位置Vector3 headPosition = transform.position + transform.up * headHeight;//朝向Quaternion lookRotation = Quaternion.LookRotation(position - headPosition);Vector3 eulerAngles = lookRotation.eulerAngles - transform.rotation.eulerAngles;float x = NormalizeAngle(eulerAngles.x);float y = NormalizeAngle(eulerAngles.y);angleX = Mathf.Clamp(Mathf.Lerp(angleX, x, Time.deltaTime * lerpSpeed), verticalAngleLimit.x, verticalAngleLimit.y);angleY = Mathf.Clamp(Mathf.Lerp(angleY, y, Time.deltaTime * lerpSpeed), horizontalAngleLimit.x, horizontalAngleLimit.y);Quaternion rotY = Quaternion.AngleAxis(angleY, head.InverseTransformDirection(transform.up));head.rotation *= rotY;Quaternion rotX = Quaternion.AngleAxis(angleX, head.InverseTransformDirection(transform.TransformDirection(Vector3.right)));head.rotation *= rotX;}//角度標(biāo)準(zhǔn)化private float NormalizeAngle(float angle){if (angle > 180) angle -= 360f;else if (angle < -180) angle += 360f;return angle;}private void LateUpdate(){LookAtPosition(GetLookAtPosition());}//獲取看向的位置private Vector3 GetLookAtPosition(){AnimatorStateInfo animatorStateInfo = animator.GetCurrentAnimatorStateInfo(0);if (animatorStateInfo.IsTag("IgnoreHeadTrack"))return transform.position + transform.up * headHeight + transform.forward;Vector3 position = mainCamera.transform.position + mainCamera.transform.forward * 100f;if (!autoTurnback) return position;Vector3 direction = position - (transform.position + transform.up * headHeight);Quaternion lookRotation = Quaternion.LookRotation(direction, transform.up);Vector3 angle = lookRotation.eulerAngles - transform.eulerAngles;float x = NormalizeAngle(angle.x);float y = NormalizeAngle(angle.y);bool isInRange = x >= verticalAngleLimit.x && x <= verticalAngleLimit.y && y >= horizontalAngleLimit.x && y <= horizontalAngleLimit.y;return isInRange ? position : (transform.position + transform.up * headHeight + transform.forward); }}
}