HeadLookAtTarget.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. /* For documentation please refer to this address:
  2. http://peyman-mass.blogspot.com/2017/12/using-multiple-bones-to-look-at-target.html */
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using UnityEngine;
  6. [System.Serializable]
  7. public class HeadLookAtData
  8. {
  9. /// <summary>
  10. /// 默认旋转
  11. /// </summary>
  12. private Quaternion m_DefaultRotation;
  13. /// <summary>
  14. /// 骨骼
  15. /// </summary>
  16. public Transform m_Bone;
  17. /// <summary>
  18. /// 旋转的极限角度
  19. /// </summary>
  20. public float m_RotationLimit = 90.0f;
  21. /// <summary>
  22. /// 旋转向上向量权重
  23. /// </summary>
  24. public float m_RotateAroundUpVectorWeight = 0.0f;
  25. /// <summary>
  26. /// 是否重置为默认旋转
  27. /// </summary>
  28. //public bool m_ResetToDefaultRotation = false;
  29. public void SetDefaultRotation(Quaternion rot)
  30. {
  31. m_DefaultRotation = rot;
  32. }
  33. /// <summary>
  34. /// 检测极限旋转数值有效性
  35. /// </summary>
  36. public void CheckJointRotation()
  37. {
  38. if (m_RotationLimit < Mathf.Epsilon)
  39. {
  40. Debug.LogError("极限旋转为零或负。 没有生效");
  41. }
  42. }
  43. }
  44. /// <summary>
  45. /// 头看向目标
  46. /// </summary>
  47. public class HeadLookAtTarget : MonoBehaviour
  48. {
  49. /// <summary>
  50. /// 游戏对象缩放
  51. /// </summary>
  52. //private Vector3 m_gameObjectScale;
  53. /// <summary>
  54. /// 目标对象
  55. /// </summary>
  56. public GameObject m_TargetObject;
  57. public Vector3 m_UpVector = Vector3.up;
  58. /// <summary>
  59. /// 权重
  60. /// </summary>
  61. public float m_Weight = 1.0f;
  62. /// <summary>
  63. /// 看向的混合速度
  64. /// </summary>
  65. public float m_LookAtBlendSpeed = 5.0f;
  66. public bool m_DrawDebugLookAtLines = false;
  67. /// <summary>
  68. /// 骨骼数据列表
  69. /// </summary>
  70. public HeadLookAtData[] m_LookAtBones;
  71. /// <summary>
  72. /// 骨骼旋转列表
  73. /// </summary>
  74. private Quaternion[] m_BlendedRotations;
  75. /// <summary>
  76. /// 上一帧骨骼旋转
  77. /// </summary>
  78. private Quaternion[] m_LastFrameRotations;
  79. /*************************************************************************/
  80. public static Quaternion GetWorldLookAtRotation(Vector3 targetVector, Vector3 fwdVectorInWorldSpace)
  81. {
  82. Quaternion worldLookAtRotation;
  83. Vector3 rotationAxis;
  84. float angle = Vector3.Angle(fwdVectorInWorldSpace, targetVector) * Mathf.Deg2Rad;
  85. if (Mathf.Abs(angle - Mathf.PI) < Mathf.Epsilon || angle <= Mathf.Epsilon)
  86. {
  87. rotationAxis = Vector3.up;
  88. }
  89. else
  90. {
  91. rotationAxis = Vector3.Cross(fwdVectorInWorldSpace, targetVector);
  92. }
  93. worldLookAtRotation = QuaternionFromAngleAxis(ref rotationAxis, angle);
  94. return worldLookAtRotation;
  95. }
  96. /*************************************************************************/
  97. private Quaternion PerfectLookAtSlerp(Quaternion a, Quaternion b, float weight)
  98. {
  99. if (Mathf.Abs(weight - 1.0f) < Mathf.Epsilon)
  100. {
  101. return b;
  102. }
  103. if (weight < Mathf.Epsilon)
  104. {
  105. return a;
  106. }
  107. return Quaternion.Slerp(a, b, weight);
  108. }
  109. /*************************************************************************/
  110. public static Quaternion QuaternionFromAngleAxis(ref Vector3 rotationAxis, float angleRad)
  111. {
  112. float halfAngleRad = angleRad * 0.5f;
  113. rotationAxis = rotationAxis.normalized * Mathf.Sin(halfAngleRad);
  114. float quatW = Mathf.Cos(halfAngleRad);
  115. return new Quaternion(rotationAxis.x, rotationAxis.y, rotationAxis.z, quatW);
  116. }
  117. /*************************************************************************/
  118. private float GetAngleFromQuaternionRad(Quaternion inputQuat)
  119. {
  120. float ret = Mathf.Acos(inputQuat.w) * 2.0f;
  121. return ret;
  122. }
  123. /*************************************************************************/
  124. private float GetAngleFromQuaternionRad(ref Quaternion inputQuat)
  125. {
  126. float ret = Mathf.Acos(inputQuat.w) * 2.0f;
  127. return ret;
  128. }
  129. ///*************************************************************************/
  130. //private Vector3 GetForwardVector( ref Transform inputTr, FwdDirection inputAxis )
  131. //{
  132. // switch ( inputAxis )
  133. // {
  134. // case FwdDirection.X_AXIS:
  135. // return inputTr.right;
  136. // case FwdDirection.Y_AXIS:
  137. // return inputTr.up;
  138. // case FwdDirection.Z_AXIS:
  139. // return inputTr.forward;
  140. // case FwdDirection.MINUS_X_AXIS:
  141. // return -inputTr.right;
  142. // case FwdDirection.MINUS_Y_AXIS:
  143. // return -inputTr.up;
  144. // case FwdDirection.MINUS_Z_AXIS:
  145. // return -inputTr.forward;
  146. // default:
  147. // return inputTr.forward;
  148. // }
  149. //}
  150. /*************************************************************************/
  151. void Start()
  152. {
  153. //新建骨骼旋转列表
  154. m_BlendedRotations = new Quaternion[m_LookAtBones.Length];
  155. m_LastFrameRotations = new Quaternion[m_LookAtBones.Length];
  156. for (int i = 0; i < m_LookAtBones.Length; i++)
  157. {
  158. HeadLookAtData lookAtBoneData = m_LookAtBones[i];
  159. if (lookAtBoneData.m_Bone != null)
  160. {
  161. lookAtBoneData.SetDefaultRotation(lookAtBoneData.m_Bone.localRotation);//设置默认旋转
  162. m_LastFrameRotations[i] = lookAtBoneData.m_Bone.localRotation;//设置上一帧旋转
  163. lookAtBoneData.CheckJointRotation();//检测极限旋转数据有效性
  164. }
  165. }
  166. }
  167. /*************************************************************************/
  168. void Update()
  169. {
  170. if (m_TargetObject == null)
  171. {
  172. return;
  173. }
  174. if (m_LookAtBones.Length > 0)//骨骼列表存在
  175. {
  176. if (m_DrawDebugLookAtLines)//如果画线
  177. {
  178. Vector3 destination = m_TargetObject.transform.position - m_LookAtBones[0].m_Bone.position;//目标与跟骨骼的向量
  179. Debug.DrawLine(m_LookAtBones[0].m_Bone.position, m_LookAtBones[0].m_Bone.position + destination);//画线
  180. }
  181. }
  182. }
  183. /*************************************************************************/
  184. void LateUpdate()
  185. {
  186. if (m_TargetObject == null)
  187. {
  188. return;
  189. }
  190. if (m_Weight < Mathf.Epsilon)
  191. {
  192. //即使权重为零,也更新最后一帧旋转
  193. for (int i = 0; i < m_LookAtBones.Length; i++)
  194. {
  195. if (m_LookAtBones[i].m_Bone == null)
  196. {
  197. continue;
  198. }
  199. HeadLookAtData lookAtBoneData = m_LookAtBones[i];
  200. m_LastFrameRotations[i] = lookAtBoneData.m_Bone.localRotation;
  201. }
  202. return;
  203. }
  204. //更新骨骼旋转
  205. if (m_LookAtBones.Length > 0)
  206. {
  207. if (m_LookAtBones[0].m_Bone == null)
  208. {
  209. return;
  210. }
  211. Vector3 rotatedInitFwdVec = m_LookAtBones[0].m_Bone.up;//初始向量
  212. Vector3 currentFwdVec;
  213. Vector3 parentFwdVec;
  214. Vector3 targetVector = m_TargetObject.transform.position - m_LookAtBones[0].m_Bone.position; //current vector is being updated in UpdateCurrentTargetVector
  215. Vector3 firstBoneRotatedInitFwdVec = rotatedInitFwdVec;
  216. byte numBonesToRotate = 1;
  217. for (int i = 0; i < m_LookAtBones.Length; i++)
  218. {
  219. if (m_LookAtBones[i].m_Bone == null)
  220. {
  221. continue;
  222. }
  223. Transform currentBone = m_LookAtBones[i].m_Bone;
  224. Transform parentBone = currentBone.parent; ;
  225. float rotationLimit = m_LookAtBones[i].m_RotationLimit;
  226. bool parentExists = true;
  227. currentFwdVec = currentBone.up;
  228. parentFwdVec = parentBone.up;
  229. if (parentBone != null)
  230. {
  231. currentFwdVec = currentBone.up; ;
  232. parentFwdVec = parentBone.up;
  233. float diffAngleFromAnim = Vector3.Angle(currentFwdVec, parentFwdVec);
  234. //in case animation already has a bone with relative rotation higher than the limit specified by user
  235. if (diffAngleFromAnim > rotationLimit)
  236. {
  237. rotationLimit = diffAngleFromAnim;
  238. }
  239. }
  240. else
  241. {
  242. parentExists = false;
  243. }
  244. Quaternion lookAtRot = GetWorldLookAtRotation(targetVector, rotatedInitFwdVec);
  245. if (m_LookAtBones[i].m_RotateAroundUpVectorWeight > 0.0f)
  246. {
  247. Vector3 currentRotationAxis;
  248. currentRotationAxis.x = lookAtRot.x;
  249. currentRotationAxis.y = lookAtRot.y;
  250. currentRotationAxis.z = lookAtRot.z;
  251. //checking the up vector direction for rotation
  252. float rotationSign = Mathf.Sign(Vector3.Cross(firstBoneRotatedInitFwdVec, targetVector).y);
  253. Vector3 finalUpVector = rotationSign * m_UpVector;
  254. finalUpVector = Vector3.Lerp(currentRotationAxis, finalUpVector, m_LookAtBones[i].m_RotateAroundUpVectorWeight);
  255. lookAtRot = QuaternionFromAngleAxis(ref finalUpVector, GetAngleFromQuaternionRad(lookAtRot));
  256. }
  257. Quaternion childRotation = lookAtRot * currentBone.rotation;
  258. if (parentExists)
  259. {
  260. //checking the angle difference
  261. float diffAngle = Mathf.Abs(Vector3.Angle(parentFwdVec, lookAtRot * currentFwdVec)) - rotationLimit;
  262. if (diffAngle > 0)
  263. {
  264. {
  265. Vector3 rotationAxis = new Vector3(lookAtRot.x, lookAtRot.y, lookAtRot.z);
  266. float limitAngleRad = GetAngleFromQuaternionRad(ref lookAtRot) + Mathf.Deg2Rad * (-diffAngle);
  267. lookAtRot = QuaternionFromAngleAxis(ref rotationAxis, limitAngleRad);
  268. childRotation = lookAtRot * currentBone.rotation;
  269. Quaternion currentBoneRotationLS = currentBone.localRotation;
  270. m_LookAtBones[i].m_Bone.rotation = childRotation;
  271. m_BlendedRotations[i] = PerfectLookAtSlerp(currentBoneRotationLS, m_LookAtBones[i].m_Bone.localRotation, m_Weight);
  272. }
  273. //
  274. if (i != m_LookAtBones.Length - 1)
  275. {
  276. Vector3 currentBoneToParentPosDiff = m_LookAtBones[0].m_Bone.position - m_LookAtBones[i + 1].m_Bone.position;
  277. Vector3 firstBoneToTargetDiff = m_TargetObject.transform.position - m_LookAtBones[0].m_Bone.position;
  278. rotatedInitFwdVec = m_LookAtBones[0].m_Bone.up;
  279. rotatedInitFwdVec.Normalize();
  280. rotatedInitFwdVec = firstBoneToTargetDiff.magnitude * rotatedInitFwdVec;
  281. rotatedInitFwdVec = currentBoneToParentPosDiff + rotatedInitFwdVec;
  282. targetVector = currentBoneToParentPosDiff + firstBoneToTargetDiff;
  283. numBonesToRotate++;
  284. if (m_DrawDebugLookAtLines)
  285. {
  286. Debug.DrawLine(m_LookAtBones[i + 1].m_Bone.position, m_LookAtBones[i + 1].m_Bone.position + rotatedInitFwdVec, Color.green);
  287. Debug.DrawLine(m_LookAtBones[i + 1].m_Bone.position, m_LookAtBones[i + 1].m_Bone.position + targetVector, Color.red);
  288. }
  289. }
  290. }
  291. else
  292. {
  293. Quaternion currentBoneRotationLS = currentBone.localRotation;
  294. m_LookAtBones[i].m_Bone.rotation = childRotation;
  295. m_BlendedRotations[i] = PerfectLookAtSlerp(currentBoneRotationLS, m_LookAtBones[i].m_Bone.localRotation, m_Weight);
  296. break;
  297. }
  298. }
  299. else
  300. {
  301. Quaternion currentBoneRotationLS = currentBone.localRotation;
  302. m_LookAtBones[i].m_Bone.rotation = childRotation;
  303. m_BlendedRotations[i] = PerfectLookAtSlerp(currentBoneRotationLS, m_LookAtBones[i].m_Bone.localRotation, m_Weight);
  304. if (i < m_LookAtBones.Length - 1)
  305. {
  306. Debug.LogWarning("Warning Bone name doesn't have a parent. The rest of the PerfectLookAt bone chain won't work after this bone!", this);
  307. break;
  308. }
  309. }
  310. }
  311. //Updating last frame rotations. Two loops are separated to have less jumps in memory and be more cache friendly.
  312. bool isBlending = Mathf.Abs(m_Weight - 1.0f) > Mathf.Epsilon;
  313. // Calculating the blend weight to blend between the current frame and last frame to achieve smooth rotations in dynamic animations with large range of movements.
  314. float smoothingWeight = Mathf.Clamp(m_LookAtBlendSpeed * Time.deltaTime, 0.0f, 1.0f);
  315. smoothingWeight = (smoothingWeight - 1.0f) * m_Weight + 1.0f;
  316. for (int k = 0; k < m_LookAtBones.Length; k++)
  317. {
  318. HeadLookAtData lookAtBoneData = m_LookAtBones[k];
  319. Quaternion boneLocalRotation;
  320. if (isBlending && k < numBonesToRotate)
  321. {
  322. boneLocalRotation = m_BlendedRotations[k];
  323. }
  324. else
  325. {
  326. boneLocalRotation = lookAtBoneData.m_Bone.localRotation;
  327. }
  328. Quaternion localRotationFromAnim = lookAtBoneData.m_Bone.localRotation;
  329. lookAtBoneData.m_Bone.localRotation = PerfectLookAtSlerp(m_LastFrameRotations[k], boneLocalRotation, smoothingWeight);
  330. m_LastFrameRotations[k] = lookAtBoneData.m_Bone.localRotation;
  331. }
  332. }
  333. }
  334. }