ShoulderRotator (FinalIK)

在上一篇文章里,说明Full Body IK 有一个限制的因素,那就是当人物的手被拉的时候 FullBodyBipedIK 不会旋转他的肩膀
它将保持肩骨旋转相对于胸部,因为它在播放动画中。
演示中有一个解决脚本 叫ShoulderRotator ,让我们解析一下吧。
ShoulderRotator:
注意,它将要求FBBIK解决每帧两次,损害性能。对于更具体的、定制的用例,在FBBIK解决之前,您可以在LateUpdate中根据目标的高度或距离向目标旋转肩骨,从而更快地完成任务。

先看看两者有ShoulderRotator脚本和无ShoulderRotator脚本有什么不同:

ShoulderRotator脚本:
在这里插入图片描述

无ShoulderRotator脚本:
在这里插入图片描述

很明显有ShoulderRotator脚本人物动作比较真实,不僵硬。

using UnityEngine;
using System.Collections;
using RootMotion.FinalIK;

namespace RootMotion.FinalIK {

	/// <summary>
	/// Shoulder rotator is a workaround for FBBIK not rotating the shoulder bones when pulled by hands.
	///ShoulderRotator脚本是基于FBBIK工作的,当我们拉动手时可以选择是否旋转肩膀的骨骼
	/// 如果你需要,它就执行,但是需要2次迭代。
	/// </summary>
	public class ShoulderRotator : MonoBehaviour {

		[Tooltip("肩膀的旋转权重")]
		public float weight = 1.5f;
		[Tooltip("偏移量越大,使用肩部骨骼的范围就越大。")]
		public float offset = 0.2f;

		private FullBodyBipedIK ik;
		private bool skip;

		void Start() {
			ik = GetComponent<FullBodyBipedIK>();

			// 您可以只使用LateUpdate,但是请注意,当您打开角色的animatePhysics(动画物理)时,它不工作。
			ik.solver.OnPostUpdate += RotateShoulders;
			//OnPostUpdate 后更新: 应该先更新IK,再更新RotateShoulder脚本 (注:个人理解)
		}

		private void RotateShoulders () {
			if (ik == null) 
				return;
			if (ik.solver.IKPositionWeight <= 0f) 
				return;

			// 跳过第二个更新周期
			if (skip) {
				skip = false;
				return;
			}

			RotateShoulder(FullBodyBipedChain.LeftArm, weight, offset); // 旋转左肩
			RotateShoulder(FullBodyBipedChain.RightArm, weight, offset); // 旋转右肩

			skip = true;
			ik.solver.Update(); // 再次用旋转肩膀更新FBBIK
		}

		// 旋转人物FBBIK的肩膀
		private void RotateShoulder(FullBodyBipedChain chain, float weight, float offset) 
		{
			// Get FromToRotation from the current swing direction of the shoulder to the IK target direction
			//使用FromToRotation 从当前旋转方向到IK目标方向旋转
			Quaternion fromTo = Quaternion.FromToRotation
			(GetParentBoneMap(chain).swingDirection, ik.solver.GetEndEffector(chain).position -GetParentBoneMap(chain).transform.position);
			
			// IK目标的方向
			Vector3 toTarget = ik.solver.GetEndEffector(chain).position - ik.solver.GetLimbMapping(chain).bone1.position;

			// 肢体长度
			float limbLength = ik.solver.GetChain(chain).nodes[0].length + ik.solver.GetChain(chain).nodes[1].length;

			// 用肢体长度划分IK目标方向大小,知道肢体被拉了多少。
			float delta = (toTarget.magnitude / limbLength) - 1f + offset;
			delta = Mathf.Clamp(delta * weight, 0f, 1f);

			// 计算肩关节的旋转偏移量
			Quaternion rotationOffset = Quaternion.Lerp(Quaternion.identity, fromTo, delta * ik.solver.GetEndEffector(chain).positionWeight * ik.solver.IKPositionWeight);

			// 旋转肩部
			ik.solver.GetLimbMapping(chain).parentBone.rotation = rotationOffset * ik.solver.GetLimbMapping(chain).parentBone.rotation;
		}

		// 得到肩部骨
		private IKMapping.BoneMap GetParentBoneMap(FullBodyBipedChain chain) {
			return ik.solver.GetLimbMapping(chain).GetBoneMap(IKMappingLimb.BoneMapType.Parent);
		}

		//销毁时删除委托
		void OnDestroy() {
			if (ik != null) ik.solver.OnPostUpdate -= RotateShoulders;
		}
	}
}

代码的一些方法可以到官方网站这里去查:
http://www.root-motion.com/finalikdox/html/annotated.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值