功能需求
- 根据瞄准仰角改变人物上半身姿势;
- 给枪或者右手添加约束,使枪朝向瞄准的方向;
- 上半身在各方向移动中保持稳定,同时有轻微晃动;
- 持枪时左手跟随枪,且左手位置根据不同枪变化;
- 枪绑定的物体在双手、背之间的切换;
- 约束开启和关闭(如跑步)状态平滑过渡;
1.改变瞄准仰角
这个功能有多种方案实现,总体可以分为几类:
1.改变腰的旋转;
2.改变手臂和头的旋转;
3.腰、双臂、头的旋转都改变;
4.只旋转右手腕;
一类方案:
只要转动腰一个骨骼,比较简单;
使用这种方案的游戏:
尘白禁区
二类方案:
转动3个骨骼,比较复杂(因为改变仰角时双臂的旋转是一个极不规则的旋转,写代码使用正向动力学控制基本不可能,要么用IK,要么动画混合);
使用这种方案的游戏:
原神
武装突袭1
因为枪相对头和第一人称相机的位置不固定,第一人称看到的枪的位置和远近也不固定,仰视时枪可能和头、第一人称相机穿模。
三类方案:
基本上只能用动画混合树。
例子:绝地求生、和平精英。仰角比较小时转动双臂,达到一个仰角后转动腰。
吃鸡里俯视是转动手腕,造成枪托不抵肩。
方案4
荒野行动
代码转动Spine(简单但是僵硬)
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骨骼。
效果
约束:RotationConstraint(或AimConstraint)
在Spine添加旋转约束,Source是控制仰角的物体。
预览人物端枪的动画。
在旋转约束点Is Activate,组件会计算出Spine当前的旋转偏移。点Lock。
在端枪状态的动画里加上开启旋转约束的关键帧,其他状态不开启。
这个方案也满足了需求3。
在LateUpdate()里修改Spine的旋转
AvatarMask+一维混合树实现(双臂和腰都可以转动)
在3d动画软件里做出人物仰视俯视的动画,然后用一维混合树。
持枪状态在body层是一个移动的二维混合树,arm层是控制仰角的一维混合树。
第一人称效果(枪相对头的位置变化了,所以看到枪的位置不一样):
问题:这个方案必须用AvatarMask覆盖上半身,如果走路动画的Hips的旋转在晃动,那么上半身也会跟着晃动。可以把走路动画改成Hips旋转不晃动的,然后走路时上半身和双臂都一动不动,动作更僵硬。
使用IK(转动双臂和头)
使用IK需要面对的问题是IKGoal的父级Axis绑定在哪里。绑定在根节点,双手就没有走路时的晃动效果;绑定在Chest,位置和旋转都晃动;可以使用位置约束只让Axis的位置跟随Chest。
轴-枪-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);
}
需要手调的变量有:
1.Axis的位置;
2.枪的位置;
3.双手IK的位置和旋转;
效果
第一人称效果:仰视的时候枪穿到了相机的Near clip plane后面,没有显示全。
看上去好像不错。这个方案的问题:
1.仰视的时候枪和头、第一人称相机穿模了。
2.持枪跑步、换弹、收枪、取枪的动画需要给枪的节点K帧;
3.Axis是绑定在根节点的,走路身体晃动的时候枪不会晃动,被枪约束的双手也不会晃动。
轴-IK-双手-枪
这种架构左手和枪没有约束关系,可能出现左手脱离护木的问题。
使用IK方案的问题:
1.换弹动画需要关闭IK,然后无法改变仰角,如果做第一人称,换弹时可能看不到双手的动画
2.保持枪指向瞄准方向
如果把枪绑定在右手,枪的局部旋转极有可能是个不规则的值,且在移动中手部的旋转可能变化。不适合直接由右手控制枪的旋转。可以给枪的容器节点加旋转约束,由指向瞄准方向的物体控制它的旋转。
3.上半身在移动中保持稳定
方法就是给spine或chest加旋转约束。在1.里已经讨论过。
4.左手跟随枪
如果枪绑定在右手上,那么左手就要使用IK跟随枪。否则就要用双手跟随枪的架构。
5.枪跟随的物体在右手、左手、背之间转换
要么使用动画事件,要么给枪的容器节点加Parent Constraint,把上述物体加入Source,由动画剪辑的关键帧控制各Source的权重。这个方法的好处是:
1.代码量减少;
2.预览动画就能看到效果,动画事件则必须运行才能看到。