本文实现在上篇文章《游戏中的运动框架设计》中的游戏画面中的抖动或震地的算法,只实现核心算法,并对该算法在实现上也做了优化,所以代码量少了很多,其它细节,大家根据自己的游戏需求,自我完善。
抖动:故名思议,至少包含以下两点:
1:应该有个最大的抖动幅度;
2: 多少时间内完成或渲染完这个抖动特效
剩下的就是:在这个抖动时间内,利用算法,改变每次抖动的幅度。具体什么改变,就要看算法中设计的衰减抖动幅度的速度等参数来控制和调整。
下面是具体的抖动实现
********************************************************************
** 创建人: 蔡国武
** 日 期: 2011/3/25 19:32
** 版 本: 1.0
** 描 述: 抖动或振动算法
** 应 用: 用于背景抖动、震地等特效
**************************** 修改记录
******************************
** 修改人:
** 日 期:
** 描 述:
********************************************************************/
template
class cVector3D // 点乘,叉乘,距离,旋转等封装
{
public:
GetDistance();
// 等等操作
private
T x;
T y;
T z;
}
typedef cVector3D
vector3d;
const float EPSINON = 1e - 5; //
用判断浮点数是否为0
// 判断向量长度是否近似0 技巧:不为0的情况更多,故“或运算”更有优势
bool IsVector3DNearZero(const vector3d & v)
{
if (fabs(v.x) > EPSINON ||
fabs(v.y)> EPSINON ||
fabs(v.z)> EPSINON )
return false;
return true;
}
class cAlgorithms_Shake : public
iAnimator_Shake // 抖动、振动算法,用于(游戏)等画面背景抖动
{
public:
cAlgorithms_Shake();
~cAlgorithms_Shake();
// 初始原始位置,用于结束振动时,恢复
void cAlgorithms_Shake::SetIniPoint(vector3d
vPos) { m_vIniPoint = vPos;
}
// 当前振动距离:相对m_vIniPoint
const vector3d& GetCurPoint(){
return m_vIniPoint + m_vCurRange; }
// 设置运动参数,自己传进去,并验证数据的合法性
bool SetParam(
)
{
// 赋值并验证数据的合法性
return true;
}
protected:
// 衰减振幅:根据指定衰减值对指定振幅进行衰减。 成功发生衰减返回true
bool AttenuationAmplitude (const vector3d vWeek,
vector3d & vRange)
{ if
(IsVector3DNearZero(vWeek)) // 检测衰减
return
false;
if
(IsVector3DNearZero(vRange)) // 检测振幅是还接受衰减
return
false;
if (fabs(vRange.x)
> EPSINON ) //
如果指定振幅接近为0,则不再发生变化,否则继续衰减
{
vRange.x =
vRange.x > 0 ? (vRange.x - vWeek.x) : (vRange.x +
vWeek.x);
}
if (fabs(vRange.y)
> EPSINON)
{
vRange.y =
vRange.y >= 0 ? (vRange.y - vWeek.y) : (vRange.y +
vWeek.y);
}
if (fabs(vRange.z)
> EPSINON)
{
vRange.z =
vRange.z >= 0 ? (vRange.z - vWeek.z) : (vRange.z +
vWeek.z);
}
return true;
}
public:
//
每渲染一次,就要更新振幅的衰减情况,并更新得到当前的振动距离m_vCurRange
eTaskState OnTaskRun()
{
eTaskState eTS=
TaskState_Continue;
unsigned int uCurTime =
GetTime(); // 获取当前时间
if (m_uPeriodTime != 0)
{
int
nPeriodCount = (uCurTime - m_uIniCycleTime) / m_uPeriodTime; //
经历的周期数
if
(nPeriodCount >= 1)
{ m_uIniCycleTime
+= nPeriodCount*m_uPeriodTime; // 确定当前周期的开始时间
//
依据当前时间、结束振动时间及周期,计算当前的衰减幅度的变化,若总时间为0(默认),则衰减幅度不变。
if
(m_uTotalTime != 0)
{ m_vWeakValue.x
= m_vWeakValue.x * m_vAcceleration.x *
(float)(m_uEndTime-uCurTime)/m_uTotalTime;
m_vWeakValue.y
= m_vWeakValue.y * m_vAcceleration.y *
(float)(m_uEndTime-uCurTime)/m_uTotalTime;
m_vWeakValue.z
= m_vWeakValue.z * m_vAcceleration.z *
(float)(m_uEndTime-uCurTime)/m_uTotalTime;
}
}
//
确定当前运行到的时间点;根据所属阶段,确定当前的震动距离
unsigned
int uTime = uCurTime -
m_uIniCycleTime;
//
根据当前时间和总时间,确定当前时间,在整个时间周期中,属于哪一个阶段
if (uTime
< m_uPeriodTime) //
{
int Param1[4] = {1, -1, 1, -1}; // 算法参数1
float
Param2[4] = {0.0f, 0.5f, 0.5f, 1.0f }; // 算法参数2
int
nIndexPeriod = = 4 * uTime / m_uPeriodTime; //
当前处于第几个1/4之一的震动周期内
//
振动距离 = 振幅 * 当前经历时间(按1/4周期计算,原因是每1/4周期都有一个波峰)
m_vCurRange
= m_vOneSide * ( Param1[nIndexPeriod] * (uTime * 4.0f/m_uPeriodTime
-
Param2[nIndexPeriod])
); // 这里用了点数学小技巧,所以少写代码和分支判断
}
if
(nPeriodCount >= 1) // 更新 振幅和周期
{
//
上次的这一边的振幅,为提高效率,就以这边的振幅为标准
float
fDisLast =
m_vOneSide.GetDistance();
bool bAttenuation1 = AttenuationAmplitude(m_vWeakValue, m_vOneSide); //
更新衰减振幅
bool bAttenuation2 = AttenuationAmplitude(m_vWeakValue, m_vOtherSide);
// 更新衰减振幅
float
fDisUpdate = m_vOneSide.GetDistance(); //
更新衰减后的振幅
if
(bAttenuation1 && bAttenuation2
&& fabs(fDisIni) >
EPSINON)
{
m_uPeriodTime
= m_uPeriodTime * fDisUpdate / fDisLast;
}
}
}
// 两边振幅都为0,则振动结束
if
(IsVector3DNearZero(m_vOneSide) &&
IsVector3DNearZero(m_vOtherSide)) eTS =TaskState_Exit;
//
周期为0时,震动结束
if (m_uPeriodTime ==
0) eTS=
TE_Exit;
if (m_pObj)
m_pObj->OnAniRun(this);
return eTS;
}
private:
int m_nSpeed=0; // 速度
unsigned int m_uTotalTime; //
若为0,则没有衰减,即一直运动
unsigned int m_uPeriodTime; // 振动周期
vector3d m_vOneSide; // 往一边振动幅度
vector3d m_vOtherSide; // 往另一边震动幅度
vector3d m_vWeakValue; //
幅度值每次变化的依据值
vector3d m_vAcceleration;//
加速度<1.0为减速 >1.0为减速的数
vector3d m_vIniPoint; // 振动物体的初始点
vector3d m_vCurRange; // 当前振动距离:相对m_vIniPoint
unsigned int m_uEndTime; // 结束时间
unsigned int m_uIniCycleTime;// 当前周期的开始时间
};