c语言抖动算法,游戏中抖动(振动)算法的实现

本文实现在上篇文章《游戏中的运动框架设计》中的游戏画面中的抖动或震地的算法,只实现核心算法,并对该算法在实现上也做了优化,所以代码量少了很多,其它细节,大家根据自己的游戏需求,自我完善。

抖动:故名思议,至少包含以下两点:

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;// 当前周期的开始时间

};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是Floyd-Steinberg抖动算法C语言实现源码讲解: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" int main(int argc, char *argv[]) { if(argc < 2) { printf("Usage: %s <input-file>\n", argv[0]); return 1; } char* input_file = argv[1]; int width, height, channels; unsigned char* image_data = stbi_load(input_file, &width, &height, &channels, 0); if(image_data == NULL) { printf("Failed to load image: %s\n", input_file); return 1; } int** pixels = (int**)malloc(sizeof(int*) * width); for(int i = 0; i < width; i++) { pixels[i] = (int*)malloc(sizeof(int) * height); memset(pixels[i], 0, sizeof(int) * height); } for(int y = 0; y < height; y++) { for(int x = 0; x < width; x++) { int index = (y * width + x) * channels; int oldPixel = (int)floor(0.2126 * image_data[index] + 0.7152 * image_data[index+1] + 0.0722 * image_data[index+2]); int newPixel = oldPixel > 127 ? 255 : 0; pixels[x][y] = newPixel; int error = oldPixel - newPixel; if(x < width - 1) { pixels[x+1][y] += (int)(error * 7 / 16.0); } if(x > 0 && y < height - 1) { pixels[x-1][y+1] += (int)(error * 3 / 16.0); } if(y < height - 1) { pixels[x][y+1] += (int)(error * 5 / 16.0); } if(x < width - 1 && y < height - 1) { pixels[x+1][y+1] += (int)(error * 1 / 16.0); } } } stbi_image_free(image_data); unsigned char* result_data = (unsigned char*)malloc(sizeof(unsigned char) * width * height); for(int y = 0; y < height; y++) { for(int x = 0; x < width; x++) { result_data[y * width + x] = (unsigned char)pixels[x][y]; } } for(int i = 0; i < width; i++) { free(pixels[i]); } free(pixels); char* output_file = "output.png"; stbi_write_png(output_file, width, height, 1, result_data, width); free(result_data); printf("Image saved to: %s\n", output_file); return 0; } ``` 以上代码,使用了stb_image和stb_image_write库来读取和保存图片文件。在实现过程,首先使用二维数组存储每个像素点的值。对于每个像素点,根据其RGB值计算灰度值,并判断是否大于阈值,若大于则将其像素值设为255,否则设为0。然后计算误差并将其传递给周围的像素点,具体地,将误差按照一定的比例分配给周围的4个像素点,以实现误差扩散的效果。最后将处理后的像素点重新转换为unsigned char类型并保存为PNG格式的图片文件。 注意,在实现过程需要注意防止越界访问,以及内存的释放等问题。 希望这份源码讲解对您有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值