这个功能有多种方案实现,总体可以分为两类:
1.改变腰的旋转;
2.改变手臂和头的旋转;
其中方案1:
只要转动腰一个骨骼,比较简单;
但是因为腰以上的部分都不动,第三人称比较僵硬;
又因为腰以上的部分保持稳定,所以适合在第一人称使用。
方案2:
转动3个骨骼,比较复杂;
调好了第三人称效果好一些;
因为枪相对头和第一人称相机的位置不固定,第一人称看到的枪的位置和远近也不固定,仰视时枪可能和头、第一人称相机穿模。
animator.SetBoneLocalRotation()实现(只转动腰,最简单的方案)
需要把相应层打开IK Pass,在OnAnimatorIK()里执行animator.SetBoneLocalRotation(),可以直接改变某个骨骼的旋转。这似乎是官方推荐的方法:
Quaternion.AngleAxis()输入的轴是物体的局部坐标系的,所以要获得Spine坐标系下人物x轴的向量。Quaternion.AngleAxis()返回一个旋转变化,把它乘给spine.localRotation。这里angleX是仰角,是个会变大变小的值,怎么能做变化量?
public Animator animator;
HumanBodyBones boneToLookUpDown=HumanBodyBones.Spine;
Transform spine;
[Range(-90,90)]
public float angleX=0;
void Start(){
animator = GetComponent<Animator>();
spine=animator.GetBoneTransform(boneToLookUpDown);
}
void OnAnimatorIK(){
Quaternion spineRot=Quaternion.AngleAxis(angleX,spine.InverseTransformDirection(transform.right));
animator.SetBoneLocalRotation(boneToLookUpDown,spine.localRotation*spineRot);
}
这里animator.SetBoneLocalRotation()之前我打印了一下spine.localEulerAngles,结果是:
animator.SetBoneLocalRotation()之前spine的旋转一直不变,会被动画系统掰回原来的旋转,所以animator.SetBoneLocalRotation(xxx,spine.localRotation*deltaRot);里面虽然有spine的当前旋转,但这个旋转一直是人物姿势被改变前的旋转,这句其实是设置spine的局部绝对旋转。
如果不运行,只在编辑模式看一下效果,可以把人物的旋转归零,选Global,Rotate Tool,直接旋转Spine骨骼。
效果
动画一维混合树实现(转动双臂和头,对动画要求高)
如果有人物仰视俯视的动画,可以用一维混合树实现。
我试了从持枪平视做持枪仰视,修改chest、upper chest、spine的Front-Back,结果上下半身都动了,我不会做只动上半身,但是可以只修改arm,做成持枪仰视俯视的动画,上半身保持直立;
以上原因导致这个方案只能改变双臂做仰视俯视的效果。所以持枪状态在body层是一个移动的二维混合树,arm层是控制仰角的一维混合树。
第一人称效果(枪相对头的位置变化了,所以看到枪的位置不一样):
因为没有严格K动画,改变仰角的时候左手的位置是变化的。
Unity使用一维混合树改变人物仰角_单机游戏热门视频 (bilibili.com)
枪通过IK带动双手(转动双臂和头。调整很繁琐)
人物改变仰角的时候枪是绕右肩旋转的,所以在瞄准姿势在人物根节点下右肩的位置放一个节点作为转轴Axis。
把枪挂载到Axis下,在枪下面建左手右手的IKGoal节点。枪应该使Axis在枪托尾部的位置。IKGoal的位置和旋转需要运行后手调。
Axis前方很远的地方建一个target,作为头看向的目标。
配置双手IK:
public Transform rightHandIK,leftHandIK;
void HandsIK(){
animator.SetIKPosition(AvatarIKGoal.RightHand,rightHandIK.position);
animator.SetIKPositionWeight(AvatarIKGoal.RightHand,1);
animator.SetIKRotation(AvatarIKGoal.RightHand,rightHandIK.rotation);
animator.SetIKRotationWeight(AvatarIKGoal.RightHand,1);
animator.SetIKPosition(AvatarIKGoal.LeftHand,leftHandIK.position);
animator.SetIKPositionWeight(AvatarIKGoal.LeftHand,1);
animator.SetIKRotation(AvatarIKGoal.LeftHand,leftHandIK.rotation);
animator.SetIKRotationWeight(AvatarIKGoal.LeftHand,1);
}
配置头看向:
public Transform target;
void HeadLookIK(){
animator.SetLookAtPosition(target.position);
animator.SetLookAtWeight(1);
}
总结起来,节点的决定关系是:
Axis->枪->双手IK->双手
需要手调的变量有:
1.Axis的位置;
2.枪的位置;
3.双手IK的位置和旋转;
效果
看上去好像不错,其实仰视的时候枪和头、第一人称相机穿模了。所以这个适用于第三人称,第一人称适合方案1.
Aim Constraint组件实现(只转动腰,和方案1效果一样但是麻烦得多,已经废弃)
在人物的chest上挂载Aim Constraint组件,人物上挂载一个控制俯仰的轴targetAxis,targetAxis前方很远处挂一个空物体target,该组件把物体的Aim Vector方向朝向Sources物体。sources物体选target。Aim Vector不能选z轴正方向,因为持枪状态人的胸朝向右前方,就是chest的z轴正方向朝向右前方,应该指向准星的是枪口的方向。所以需要得到枪口方向在chest坐标系下的向量。
1.选中人物;
2.来到Animation窗口找到持枪动画,点预览;
3.在脚本里加带有[ContextMenu()]的方法,执行
chest.InverseTransformDirection(character.forward);
4.在脚本的...里执行
得到的就是枪口方向在chest坐标系下的向量,把这个Vector3抄下来,写给Aim Vector。
如果给人物趴着的动画制作这个效果,需要在趴着的状态执行上述代码,再得到一个Aim Vector。
为什么必须用AimConstraint?直接用代码旋转chest不行吗?
运行状态下,对于有Animator的物体,在Scene窗口移动旋转人物的骨骼,无效,因为人物正受Animator操控,播放指定的动画,Animator的优先级高于改变Transform。但是AimConstraint的优先级高于Animator。
控制优先级AimConstraint>Animator>Transform
AimConstraint开启关闭的代码放在哪里
因为瞄准约束影响上半身的效果,而状态机Body层和Arm层没有做同步,可能出现一些难以预料的状态组合,最稳的方法是放在Arm层的各状态脚本里。当然趴着瞄准的代码要放在Body层的状态脚本了。
持步枪的子状态机。瞄准约束打开放在Rifle Stand的OnStateEnter(),关闭根据想在哪些状态关闭放在相应的OnStateEnter()。因为
1.一个状态无法知道下一个状态是什么,放在OnStateExit()里会帮下一个状态决定了AimConstraint开启或关闭,造成一刀切的效果;
2.且OnStateExit()在下一个状态的OnStateEnter()之后,会覆盖OnStateEnter()的效果;
所以AimConstraint的开启关闭不宜放在OnStateExit()
Constraint Settings
enabled勾选,Lock不勾选状态下,勾选isActive时,会把当前的旋转填入Rotation At Rest.
没有运行时,enabled,isActive,Lock全部勾选时,约束会实时生效。
如果不想用Constraint Settings的功能就关闭isActive,把两个Rotation设0,一直勾选Lock。
LookAtConstraint是加给第一人称相机还是头部?
第一人称相机是头部的子物体,人物不拿枪,鼠标前后移动时,需要相机仰视俯视,这时不需要人物的整个上半身仰起或俯下,胸部的Aim Constraint关闭。问题是这个组件加给头还是第一人称相机?如果加给头,鼠标前后移动的时候人物会仰头低头,很生动,但是人物跑步的时候身体会晃,但是头一直稳定,很僵硬。如果加给相机,人物仰头低头的效果就没了,但是跑步的时候头随身体摇晃,很生动。
观察了绝地求生,跑步的时候第三人称看人物的头摇晃很厉害,第一人称看影子头也摇晃很厉害,但是第一人称相机很稳定,说明应该是第一人称相机加了约束;另外在第三人称仰视俯视的时候好像只有相机在仰视俯视,人物的头没有动。这个可以观察比赛开始之前,集合的玩家一定有在俯视仰视的,但是人物的头都在平视。