PerfectLookAtAdon.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  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 PerfectLookAtDataAdon
  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 PerfectLookAtAdon : 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 PerfectLookAtDataAdon[] 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. PerfectLookAtDataAdon lookAtBoneData = m_LookAtBones[ i ];
  159. lookAtBoneData.SetDefaultRotation( lookAtBoneData.m_Bone.localRotation );//设置默认旋转
  160. m_LastFrameRotations[ i ] = lookAtBoneData.m_Bone.localRotation;//设置上一帧旋转
  161. lookAtBoneData.CheckJointRotation();//检测极限旋转数据有效性
  162. }
  163. }
  164. /*************************************************************************/
  165. void Update()
  166. {
  167. if ( m_LookAtBones.Length > 0 )//骨骼列表存在
  168. {
  169. if ( m_DrawDebugLookAtLines )//如果画线
  170. {
  171. Vector3 destination = m_TargetObject.transform.position - m_LookAtBones[ 0 ].m_Bone.position;//目标与跟骨骼的向量
  172. Debug.DrawLine( m_LookAtBones[ 0 ].m_Bone.position, m_LookAtBones[ 0 ].m_Bone.position + destination );//画线
  173. }
  174. }
  175. }
  176. /*************************************************************************/
  177. void LateUpdate ()
  178. {
  179. if ( m_TargetObject == null )
  180. {
  181. Debug.LogWarning( "No target object set for the component. Component won't work without a target object", this );
  182. return;
  183. }
  184. if( m_Weight < Mathf.Epsilon )
  185. {
  186. //即使权重为零,也更新最后一帧旋转
  187. for ( int i = 0; i < m_LookAtBones.Length; i++ )
  188. {
  189. PerfectLookAtDataAdon lookAtBoneData = m_LookAtBones[ i ];
  190. m_LastFrameRotations[ i ] = lookAtBoneData.m_Bone.localRotation;
  191. }
  192. return;
  193. }
  194. //更新骨骼旋转
  195. if ( m_LookAtBones.Length > 0 )
  196. {
  197. Vector3 rotatedInitFwdVec = m_LookAtBones[0].m_Bone.up;//初始向量
  198. Vector3 currentFwdVec;
  199. Vector3 parentFwdVec;
  200. Vector3 targetVector = m_TargetObject.transform.position - m_LookAtBones[ 0 ].m_Bone.position; //current vector is being updated in UpdateCurrentTargetVector
  201. Vector3 firstBoneRotatedInitFwdVec = rotatedInitFwdVec;
  202. byte numBonesToRotate = 1;
  203. for ( int i = 0; i < m_LookAtBones.Length; i++ )
  204. {
  205. Transform currentBone = m_LookAtBones[ i ].m_Bone;
  206. Transform parentBone = currentBone.parent; ;
  207. float rotationLimit = m_LookAtBones[ i ].m_RotationLimit;
  208. bool parentExists = true;
  209. currentFwdVec = currentBone.up;
  210. parentFwdVec = parentBone.up;
  211. if ( parentBone != null )
  212. {
  213. currentFwdVec = currentBone.up; ;
  214. parentFwdVec = parentBone.up;
  215. float diffAngleFromAnim = Vector3.Angle( currentFwdVec, parentFwdVec );
  216. //in case animation already has a bone with relative rotation higher than the limit specified by user
  217. if ( diffAngleFromAnim > rotationLimit )
  218. {
  219. rotationLimit = diffAngleFromAnim;
  220. }
  221. }
  222. else
  223. {
  224. parentExists = false;
  225. }
  226. Quaternion lookAtRot = GetWorldLookAtRotation( targetVector, rotatedInitFwdVec );
  227. if ( m_LookAtBones[ i ].m_RotateAroundUpVectorWeight > 0.0f )
  228. {
  229. Vector3 currentRotationAxis;
  230. currentRotationAxis.x = lookAtRot.x;
  231. currentRotationAxis.y = lookAtRot.y;
  232. currentRotationAxis.z = lookAtRot.z;
  233. //checking the up vector direction for rotation
  234. float rotationSign = Mathf.Sign( Vector3.Cross( firstBoneRotatedInitFwdVec, targetVector ).y );
  235. Vector3 finalUpVector = rotationSign * m_UpVector;
  236. finalUpVector = Vector3.Lerp( currentRotationAxis, finalUpVector, m_LookAtBones[ i ].m_RotateAroundUpVectorWeight );
  237. lookAtRot = QuaternionFromAngleAxis( ref finalUpVector, GetAngleFromQuaternionRad( lookAtRot ) );
  238. }
  239. Quaternion childRotation = lookAtRot * currentBone.rotation;
  240. if ( parentExists )
  241. {
  242. //checking the angle difference
  243. float diffAngle = Mathf.Abs( Vector3.Angle( parentFwdVec, lookAtRot * currentFwdVec ) ) - rotationLimit;
  244. if ( diffAngle > 0 )
  245. {
  246. {
  247. Vector3 rotationAxis = new Vector3( lookAtRot.x, lookAtRot.y, lookAtRot.z );
  248. float limitAngleRad = GetAngleFromQuaternionRad( ref lookAtRot ) + Mathf.Deg2Rad * ( -diffAngle );
  249. lookAtRot = QuaternionFromAngleAxis( ref rotationAxis, limitAngleRad );
  250. childRotation = lookAtRot * currentBone.rotation;
  251. Quaternion currentBoneRotationLS = currentBone.localRotation;
  252. m_LookAtBones[ i ].m_Bone.rotation = childRotation;
  253. m_BlendedRotations[ i ] = PerfectLookAtSlerp( currentBoneRotationLS, m_LookAtBones[ i ].m_Bone.localRotation, m_Weight );
  254. }
  255. //
  256. if ( i != m_LookAtBones.Length - 1 )
  257. {
  258. Vector3 currentBoneToParentPosDiff = m_LookAtBones[ 0 ].m_Bone.position - m_LookAtBones[ i + 1 ].m_Bone.position;
  259. Vector3 firstBoneToTargetDiff = m_TargetObject.transform.position - m_LookAtBones[ 0 ].m_Bone.position;
  260. rotatedInitFwdVec = m_LookAtBones[0].m_Bone.up;
  261. rotatedInitFwdVec.Normalize();
  262. rotatedInitFwdVec = firstBoneToTargetDiff.magnitude * rotatedInitFwdVec;
  263. rotatedInitFwdVec = currentBoneToParentPosDiff + rotatedInitFwdVec;
  264. targetVector = currentBoneToParentPosDiff + firstBoneToTargetDiff;
  265. numBonesToRotate++;
  266. if ( m_DrawDebugLookAtLines )
  267. {
  268. Debug.DrawLine( m_LookAtBones[ i + 1 ].m_Bone.position, m_LookAtBones[ i + 1 ].m_Bone.position + rotatedInitFwdVec, Color.green );
  269. Debug.DrawLine( m_LookAtBones[ i + 1 ].m_Bone.position, m_LookAtBones[ i + 1 ].m_Bone.position + targetVector, Color.red );
  270. }
  271. }
  272. }
  273. else
  274. {
  275. Quaternion currentBoneRotationLS = currentBone.localRotation;
  276. m_LookAtBones[ i ].m_Bone.rotation = childRotation;
  277. m_BlendedRotations[ i ] = PerfectLookAtSlerp( currentBoneRotationLS, m_LookAtBones[ i ].m_Bone.localRotation, m_Weight );
  278. break;
  279. }
  280. }
  281. else
  282. {
  283. Quaternion currentBoneRotationLS = currentBone.localRotation;
  284. m_LookAtBones[ i ].m_Bone.rotation = childRotation;
  285. m_BlendedRotations[ i ] = PerfectLookAtSlerp( currentBoneRotationLS, m_LookAtBones[ i ].m_Bone.localRotation, m_Weight );
  286. if( i < m_LookAtBones.Length - 1 )
  287. {
  288. Debug.LogWarning( "Warning Bone name doesn't have a parent. The rest of the PerfectLookAt bone chain won't work after this bone!", this );
  289. break;
  290. }
  291. }
  292. }
  293. //Updating last frame rotations. Two loops are separated to have less jumps in memory and be more cache friendly.
  294. bool isBlending = Mathf.Abs( m_Weight - 1.0f ) > Mathf.Epsilon;
  295. // 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.
  296. float smoothingWeight = Mathf.Clamp( m_LookAtBlendSpeed * Time.deltaTime, 0.0f, 1.0f );
  297. smoothingWeight = ( smoothingWeight - 1.0f ) * m_Weight + 1.0f;
  298. for ( int k = 0; k < m_LookAtBones.Length; k++ )
  299. {
  300. PerfectLookAtDataAdon lookAtBoneData = m_LookAtBones[ k ];
  301. Quaternion boneLocalRotation;
  302. if ( isBlending && k < numBonesToRotate )
  303. {
  304. boneLocalRotation = m_BlendedRotations[ k ];
  305. }
  306. else
  307. {
  308. boneLocalRotation = lookAtBoneData.m_Bone.localRotation;
  309. }
  310. Quaternion localRotationFromAnim = lookAtBoneData.m_Bone.localRotation;
  311. lookAtBoneData.m_Bone.localRotation = PerfectLookAtSlerp( m_LastFrameRotations[ k ], boneLocalRotation, smoothingWeight );
  312. m_LastFrameRotations[ k ] = lookAtBoneData.m_Bone.localRotation;
  313. }
  314. }
  315. }
  316. }