“震屏”作为一种游戏中的表现手段,被用来表达震撼的效果(例如用在技能的表现上)。
由于这类效果往往可以使用很小的开销达到不错的效果,所以在游戏中出现率较高。下面我将介绍一种在3D游戏中简单的震屏实现方法。
首先,之所以称之为“震屏”,是因为并不是真正的场景本身晃动,而是玩家所见的屏幕(摄像机)产生了震动。而摄像机所观察的方向(CamDir)作为一个三维的向量,改变CamDir即可产生屏幕晃动的效果,于是CamDir + dirOffset就是我们实现震屏效果的核心。
游戏中玩家主动旋转视角改变的主要内容也是CamDir,那么震屏与其不同之处在哪里呢?主要有以下两点:
1.相机不是随意旋转的,而是以玩家当前观察的视向为基准进行改变的。
2.相机在"震屏"效果结束后,需要保证初始的CamDir是不变的。
为了保证这两点,一个简单有效的方式是对CameraShake进行奇偶帧区分,奇偶帧指的是我们在游戏中每一帧需要运行的代码,例如Unity中的Update()。在奇偶帧中我们分别做一次相机偏移和一次相机归位,即:
void CameraShake()
{
++iOddTick;
iOddTick %= 2;
if(iOddTick)
{
// calculate offset;
}
else
{
offset = -offset;
}
m_CamDir += offset;
}
其次,屏幕的震动幅度往往应该与“震源”的距离成反比,引起震动的事物与我们越远,对摄像机的影响也就越小。
然后,offset是以CamDir为基础。那么震幅的取值应该是一个相对随机的值,这个核心取值我们可以采用一维的柏林噪声函数。于是整个震屏效果可以如下表述:
void CameraShake(float deltaTime)
{
++iOddTick;
iOddTick %= 2;
if(iOddTick)
{
// calculate offset;
offset = m_CamDir * (constantNum / distanceToCamera) * PerlinNoise1D(deltaTime);
}
else
{
offset = -offset;
}
m_CamDir += offset;
}
关于柏林函数的介绍,可以参见这篇博客。之所以采用柏林函数,一方面是因为其表现相对固定(一旦叠加的函数确定下来),对于震屏效果,我们期望的是一种相对稳定的效果,直接采用Rand()的方式可能会造成过度随机;另一方面,柏林函数是一种连续的函数,可以根据时间计算得到连续而且而非离散的值。