最近遇到一些类似3D FPS的game play。针对这里面Player摄像机的运动的问题,随便总结一些东西以备参考。首先,对问题做一些的假设:
- Player摄像机在多数情况下在场景中的位置是固定的,只在某些时候因为动画而发生平动(当然也会发生转动),多数时候只需要转动。
- Player摄像机不需要照射到Player的任何部分,也就是Player的角色根本不显示在场景中。
- 如果Player摄像机拍摄的方向是其自身坐标系的z方向,那么其转动只限于绕自身的x和y轴,且只在一定范围内(比如正负45度)。转动的原因有两种,一种是瞄准(在移动设备上,可以通过触摸、加速度传感器或者陀螺仪返回的数据进行控制,在此不详述),一种是收到攻击时的震动。
- 程序在Unity3D中开发。
Unity3D实现了基于组件的编程模型,一个GameObject上可以挂载很多组件,比如Transform、Animation、RigidBody、Camera、AudioSource等等,所以可以考虑把各个控制逻辑分成组件。由于动画而造成的运动直接由内置的Animation组件完成,而瞄准和被攻击可以用相应的脚本(也是组件)来完成。但是,这样做其实有很多问题,因为这三种组件都会影响摄像机的旋转。
Animation组件是不管其他组件,每一帧都直接给摄像机的旋转赋新值(当然对其位置和大小也如此)。Animation组件没有处于播放状态时,摄像机位置不变,瞄准脚本将作为控制摄像机旋转的主要组件,在Animation控制其旋转的时候,瞄准脚本的控制也就失效了。如果Animation组件起作用的时间不长,那还好说,如果Animation造成旋转的时间较长,那就麻烦了。另外,由于震动这个组件是后加上的,理想情况下它应该只是在被瞄准脚本控制的摄像机转动上面加上适当的扰动。
由于我现在只有瞄准、震动两个脚本直接控制摄像机角度,我可以在瞄准脚本的Update()方法里,根据传感器给出的值调整摄像机角度,而在震动脚本里,通过LateUpdate()加上扰动。(后者不能直接使用Update()方法,是因为不同脚本中Update()的调用顺序是不定的。)这样的实现不好看,而且难以扩展。如果再有一个脚本加进来怎么办?Unity3D肯定没有一个LateLateUpdate()方法了。
除此之外,在上面描述的实现方式中,不论我使用欧拉角还是四元数,都会有一些问题。原因我还没有查清,但有一点是肯定的:我不怎么会用四元数,并且忌惮它的复杂性。因此,我想了一种简单的方法来实现上面的功能,尚未实验,但我认为应该行得通。准备3个GameObject,分别设为A, B, C,使得A为B的父物体,B为C的父物体。将主控脚本和Animation组件挂在A上,瞄准脚本挂在B上,震动脚本挂在C上(不用LateUpdate()而也改用Update())。这样,瞄准只听从传感器的指示,叠加在Animation造成的运动上;而震动则根据相应算法生成扰动,直接设置C的local
rotation。这个实现应可既让三种运动相对独立,不受Update()调用顺序的困扰,又可以让这些运动叠加起来。
rotation。这个实现应可既让三种运动相对独立,不受Update()调用顺序的困扰,又可以让这些运动叠加起来。
现在能想见,这种实现有一个缺点,就是GameObject的树结构会随着控制脚本的增加越来越大。如果硬要将这个功能放在同一物体上,也许只能设置一个主控脚本,按顺序调用瞄准脚本和震动脚本的Update()方法,而后两者也就不能实现为MonoBehaviour了。但是,要克服Animation组件对旋转的霸道影响,还是要把Animation组件和Camera组件分别挂在两个GameObject上的。
不知道如果熟悉四元数的概念,这个问题会不会更加容易解决?找时间补补课。