概述
这篇笔记记录研究其他射击游戏项目对一些问题的解决方案。主要有:
1.TPP如何改变人物仰角;
2.枪的挂载位置旋转,是每把枪各异还是用统一数据;
3.相机和人物的旋转,谁带动谁;
4.FPP和TPP的动作要求不同,是否用了两套动作;
5.使用的动画是否为humanoid;
FPS Animation Framework
Welcome! | FPS Animation Framework Legacy (gitbook.io)
动作条件判断测试
先对它做射击游戏笔记里的那一套动作条件判断测试。
1.跑步时按换弹被忽略;
2.换弹时按跑步被忽略;
3.瞄准状态按换弹,并没有解除瞄准,而是相机在瞄准位置换弹,但很明显相机不是枪的子物体,而是一直看前方;瞄准状态按跑步,退出瞄准开始跑步;
4.换弹中按瞄准,和3一样会进入瞄准换弹状态;跑步中按瞄准,被忽略;
7.趴下中按换弹,双手播放了换弹动画,M4的换弹动画没问题,另外几把枪趴下的换弹动画枪会直接消失,换完弹再回来;趴下中按跑步,趴下后开始往前爬;趴下中按瞄准,瞄准好像可以和任何动作叠加,就是把相机往前一点;
8.爬行中按瞄准,相机往前移了,双手是介于爬行和瞄准之间的动作;我感觉这个系统应该是用了混合树之类的技术;
另外发现
demo里一共5把枪,换到最后一把有可能会不指向中心,指向偏左的位置,换成下一把M4,也是偏左的,再换下一把AK就正常了。
趴下时不能换枪。
趴下时后面几把枪的换弹动画好像没做好。
这个框架自制的组件。看起来挺复杂。
瞄准相机位
侧向移动的时候枪相对于瞄准相机有偏移,说明瞄准相机不是简单的作为枪的子物体。
Third Person Shooter Bundle
1.改变人物仰角
在ShootBehaviour脚本。使用的是animator.SetBoneLocalRotation()。第一个对Spine设置是改变水平旋转,第二个对Chest设置是改变仰角。根据相机forward得到仰角,加一个修正值armsRotation,绕transform.right旋转,就是绕人物x轴旋转,先不看对Long weapons的特殊处理,后面的代码要怎么理解?
public void OnAnimatorIK(int layerIndex)
{
if (isAiming && activeWeapon > 0)
{
if (CheckForBlockedAim())
return;
// Orientate upper body where camera is targeting.
Quaternion targetRot = Quaternion.Euler(0, transform.eulerAngles.y, 0);
targetRot *= Quaternion.Euler(initialRootRotation);
targetRot *= Quaternion.Euler(initialHipsRotation);
targetRot *= Quaternion.Euler(initialSpineRotation);
// Set upper body horizontal orientation.
behaviourManager.GetAnim.SetBoneLocalRotation(HumanBodyBones.Spine, Quaternion.Inverse(hips.rotation) * targetRot);
// Keep upper body orientation regardless strafe direction.
float xCamRot = Quaternion.LookRotation(behaviourManager.playerCamera.forward).eulerAngles.x;
targetRot = Quaternion.AngleAxis(xCamRot + armsRotation, this.transform.right);
if (weapons[activeWeapon] && weapons[activeWeapon].type == InteractiveWeapon.WeaponType.LONG)
{
// Correction for long weapons.
targetRot *= Quaternion.AngleAxis(9f, this.transform.right);
targetRot *= Quaternion.AngleAxis(20f, this.transform.up);
}
targetRot *= spine.rotation;
targetRot *= Quaternion.Euler(initialChestRotation);
// Set upper body vertical orientation.
behaviourManager.GetAnim.SetBoneLocalRotation(HumanBodyBones.Chest, Quaternion.Inverse(spine.rotation) * targetRot);
}
}
对四元数做实验可知:一个物体的世界旋转是从它的根父级到它的局部旋转相乘的的积:这里相乘顺序不能错,必须从根父级到它本身,因为四元数乘法不满足交换律!
[ContextMenu("看看四元数")]
void PrintQuaternion(){
Debug.Log("这个物体的世界旋转是"+transform.rotation);
Debug.Log("这个物体和它父级的局部旋转的积是"+transform.parent.localRotation*transform.localRotation);
}
所以这个targetRot应该是chest在改变仰角之后的世界旋转。
SetBoneLocalRotation(HumanBodyBones.Chest, Quaternion.Inverse(spine.rotation) * targetRot);
那么SetBoneLocalRotation里的Quaternion.Inverse()作用好像是和targetRot里的世界旋转成分抵消,使结果是chest需要设置的局部旋转。总之应该可以用一句spine.InverseTransformDirection(transform.right)代替。
SetBoneLocalRotation(HumanBodyBones.Chest, Quaternion.Inverse(spine.rotation) * targetRot);
又看了一下Quaternion.Inverse()的作用:
[ContextMenu("旋转和Inverse相乘")]
void PrintInverse(){
Debug.Log(Quaternion.Inverse(transform.rotation)*transform.rotation);
}
果然Inverse和自己相乘是单位四元数:
学会了一种寻找人体部位的新方法animator.GetBoneTrasform(HumanBodyBones.XXX);比transform.Find("XXX/XXX/XXX")方便,不受人物骨架结构变化的影响。
2.武器挂载
挂载在右手,枪的局部旋转是90度的整数倍,位置不是(0,0,0),枪的脚本里记录了枪的局部位置和旋转。
3.人物和相机的旋转
研究1的时候已经知道,是把相机的仰角传给人物由于改变仰角,所以是玩家控制相机,相机带动人物。
Low Poly FPS Pack
2.武器挂载
这是一个人物只有手臂的纯FPS模板。从这个项目可以看出纯FPS游戏:
1.手臂和枪被打成一个fbx文件,属于一个模型(所以无需在游戏引擎里调整和记录枪的局部位置旋转),换枪的时候是手臂和枪的模型整体替换,扔枪的时候则是实例化了一把枪扔出去,把手臂模型替换成不拿枪的;
2.手臂和枪都由骨骼控制,且控制手臂、枪、弹匣的骨骼都是平级的,不存在靠父子关系带动;
3.由于手臂和枪是一个整体,手臂和枪互动的动画比如换弹、拉栓可以做得很细致,而如果是TPS,人物和枪是分离的物体,这就涉及到一个animator带动另一个animator转换状态的问题。
3.相机和人物的旋转
这里是把相机、人物手臂、UI共同作为一个空节点的子节点,对这个空节点做旋转。
瞄准镜实现
这个方案的瞄准镜是局部放大。是用双相机实现的。
一个相机渲染手臂和枪。
Clear Flags=Depth only:Clear Flags意思是画面里没有物体的部分画什么,Depth only我理解是画Depth小于这个相机的相机画的内容。但是我选Dont Clear也是一样的效果。
Culling Mask=Nothing:意思是什么都不渲染,但是为什么渲染了手臂和枪?我又选了一下Nothing,然后就都不渲染了。
手臂和枪的Layer是空,我只有选Everything或勾选所有层才能渲染手臂和枪。如果一个物体Layer是空,我在Culling Mask下拉框里就看不到它。
给一个物体指定Layer时不允许指定空,但是初始却可以是空。
写了个脚本打印这个物体的Layer:
[ContextMenu("看看Layer")]
void PrintLayerThis(){
Debug.Log(gameObject.layer);
}
显示是8.
Layer列表里第8层没名字。
又写了个设置Layer的方法:
[ContextMenu("设置Layer")]
void SetLayer(){
gameObject.layer=10;
}
可以设置成那个序号层,即使它是空的。
综上,可以用脚本gameObject.Layer=设置成空Layer,但是编辑器界面不能设置成空Layer。脑残的设计。
所以上面的Culling Mask=Nothing也不是真Nothing,而是渲染了一些空Layer。我把第8层改名叫Player,Culling Mask就变Player了。
Depth=1:先渲染Depth=0,再渲染这个。
前面设置玩家相机不渲染环境,但是瞄准镜里还是要看到远处的环境,这里用了第三个相机,输出到Texture,这个Texture放在瞄准镜后端,后面还有一个镜片物体,开镜的时候镜片使用透明贴图,没开镜时使用全黑贴图。
Unity - Manual: Camera component
渲染世界的相机
JU TPS
1.
使用了一些节点作为拿各种武器的IKGoal,好像根本没有持各种武器的动画,完全是用IK摆出来的。
2.武器挂载
枪挂在手上。
枪的位置旋转是不规则的数据,且每把枪不一样……那这些数据要怎么记录?
答案是:所有枪都在手上挂一份,身上挂一份,使用的时候把手上的枪激活,身上的枪隐藏。只存在枪的激活和隐藏,不存在转移……
3.相机和人物的关系
二者没有直接的父子关系。
通过自制的脚本跟随人物。也就是人物带动相机。
求生之路
调出了第三人称,发现这么几点:
1.第三人称的弹着点不在准星处,偏右下;
2.走路的时候枪并没有稳定指向前方,而发射子弹是朝前方,子弹的发射位置和向量并不是枪口、枪的正前方;
3.通过旋转手臂改变仰角,第三人称动作能达到的仰角范围小于实际的仰角范围;
4.步枪的换弹动作是同一个;
PUBG人物动作素材
在tb上买了一套吃鸡的动作素材。
把一个人物模型拖进来,可以看到:
1.身体和头分成两个网格,为了在第一人称隐藏头,防止挡第一人称相机;
2.有很多ik节点,用来动态调整人物的动作;
把骨架进一步展开:能看到近战武器、投掷物、背包、第一人称相机、主武器、副武器挂载点;