用EasyX实现的互动粒子仿真

PS1 : setfont ()函数是一个用于设置字体显示效果的方法,可以指定字体、风格和字号。

PS2 : BeginBatchDraw()函数 执行后,任何绘图操作都将暂时不输出到绘图窗口上,直到执行 FlushBatchDraw 或 EndBatchDraw 才将之前的绘图输出,以实现批绘图功能,可以消除闪烁。

PS3 : outtextxy 这个函数用于在指定位置输出字符串。 void outtextxy( int x, int y, LPCTSTR str ); void outtextxy( int x, int y, TCHAR c ); 参数 x 字符串输出时头字母的 x 轴的坐标值。 y 字字符串输出时头字母的 y 轴的坐标值, str待输出的字符串的指针。

PS4: clearrectangle 这个函数用于清空矩形区域。 void clearrectangle( int left, int top, int right, int bottom ); 参数 left 矩形左部 x 坐标。 top 矩形顶部 y 坐标。 right 矩形右部 x 坐标。 bottom矩形底部 y 坐标。

PS5:srand函数是随机数发生器的初始化函数。原型:void srand(unsigned seed);

用法:它初始化随机种子,会提供一个种子,这个种子会对应一个随机数,如果使用相同的种子后面的rand()函数会出现一样的随机数,如: srand(1); 直接使用1来初始化种子。不过为了防止随机数每次重复,常常使用系统时间来初始化,即使用 time函数来获得系统时间,它的返回值为从 00:00:00 GMT, January 1, 1970 到现在所持续的秒数,然后将time_t型数据转化为(unsigned)型再传给srand函数,即: srand((unsigned) time(&t)); 还有一个经常用法,不需要定义time_t型t变量,即: srand((unsigned) time(NULL)); 直接传入一个空指针,因为你的程序中往往并不需要经过参数获得的数据。

进一步说明下:计算机并不能产生真正的随机数,而是已经编写好的一些无规则排列的数字存储在电脑里,把这些数字划分为若干相等的N份,并为每份加上一个编号用srand()函数获取这个编号,然后rand()就按顺序获取这些数字,当srand()的参数值固定的时候,rand()获得的数也是固定的,所以一般srand的参数用time(NULL),因为系统的时间一直在变,所以rand()获得的数,也就一直在变,相当于是随机数了。只要用户或第三方不设置随机种子,那么在默认情况下随机种子来自系统时钟。如果想在一个程序中生成随机数序列,需要至多在生成随机数之前设置一次随机种子。 即:只需在主程序开始处调用srand((unsigned)time(NULL)); 后面直接用rand就可以了。不要在for等循环放置srand((unsigned)time(NULL));

#include<graphics.h>
#include<math.h>
#include<time.h>
#include <cmath> 

#define Width 450
#define High 450
#define NUM_MOVERS 800
#define FRICTION 0.96f		//摩擦力,阻尼系数
 
struct Mover
{
	COLORREF color;		//颜色
	float x, y;
	float vx, vy;
	float radius;

};

//定义全局变量
Mover movers[NUM_MOVERS];
int mouseX, mouseY;			//当前鼠标坐标
int isMouseDown;			//鼠标左键是否按下
int preMouseX, preMouseY;	//上次鼠标坐标		
int mouseVX, mouseVY;		//鼠标的速度



//绝对延时,是为在不同的计算机上实现同样的速度运行效果
void delay(DWORD ms)
{
	static DWORD oldtime = GetTickCount();

	while (GetTickCount() - oldtime < ms)
		Sleep(1);

	oldtime = GetTickCount();

}


void startup()
{

	//设置随机种子
	srand((unsigned int)time(NULL));

	for (int i = 0; i < NUM_MOVERS; i++)
	{
		movers[i].color = RGB(rand() % 256, rand() % 256, rand() % 256);
		movers[i].x = rand() % Width;
		movers[i].y = rand() % High;
		movers[i].vx = float(cos(float(i))) * (rand() % 34);
		movers[i].vy = float(sin(float(i))) * (rand() % 34);
		movers[i].radius = (rand() % 34) / 15.0;

	}

	//初始化 当前鼠标坐标 和 上次鼠标 都在画布中心
	mouseX =preMouseX=Width / 2;
	mouseY =preMouseY= High / 2;
	isMouseDown = 0;			//初始鼠标未按下

	initgraph(Width,High);
	BeginBatchDraw();

}

void show()
{
	clearrectangle(0, 0, Width - 1, High - 1);		//清空画面中全部矩形区域

	//画小球
	for (int i = 0; i < NUM_MOVERS; i++)
	{
		setcolor(movers[i].color);
		setfillstyle(movers[i].color);
		fillcircle(movers[i].x+0.5, movers[i].y+0.5, movers[i].radius+0.5);		//四舍五入

	}

	isMouseDown = 0;

	FlushBatchDraw();
	delay(20);		//类似于Sleep(5)的作用

}

void updateWithoutInput()
{
	//吸引距离,若小球和鼠标的距离在此范围内,则受到向内的吸引力
	float toDist = Width * 0.86;

	//击打距离,若小球与鼠标的距离在此范围内,则会受到向外的斥力。 
	float blowDist =Width * 0.5;

	//扰动距离,若小球与鼠标的距离在此范围内,则会受到向外的扰动。
	float stirDist = Width * 0.125;

	//前后两次运行间,鼠标移动的距离,及鼠标的速度
	mouseVX = mouseX - preMouseX;
	mouseVY = mouseY - preMouseY;

	//为记录这次鼠标坐标,更新上次鼠标坐标变量
	preMouseX = mouseX;
	preMouseY = mouseY;

	

	//对所有的小球遍历
	for (int i = 0; i < NUM_MOVERS; i++)
	{
		//计算当前鼠标和小球的位置差
		float dX = movers[i].x - mouseX;
		float dY = movers[i].y - mouseY;

		//当前小球和鼠标的位置差
		float d = sqrt(dX * dX + dY * dY);


		//将dX和dY归一化,仅反映方向,和距离无关
		if (d != 0)
		{
			dX /= d;
			dY /= d;

		}

		else
		{
			dX = 0;
			dY = 0;

		}
		//距离小于toDist 则小球受到鼠标吸引
		if (d < toDist)
		{
			//吸引力引起的加速度幅度,越近,加速度越大, 但吸引力的值任仍比上面斥力的值明显小很多。 
			float toAcc = (1 - (d /toDist)) * Width * 0.0014f;


			//由于归一化,加速度幅度值为toAcc,得到小球新速度
			movers[i].vx = movers[i].vx - dX * toAcc;
			movers[i].vy = movers[i].vy - dY * toAcc;


		}

		if (isMouseDown && d < blowDist)
		{
			//击打力引起的加速度幅度,此公式表明小球距鼠标越近,由击打斥力引起的加速度越大。 
			float blowAcc = (1 - (d / blowDist)) * 10;

			//由上面的归一化, 加速度赋值为blowACC, 得到新的小球速度。 
			movers[i].vx = movers[i].vx + dX * blowAcc + 0.5f - float(rand()) / RAND_MAX;
			movers[i].vy = movers[i].vy + dY * blowAcc + 0.5f - float(rand()) / RAND_MAX;
			//float (rand())/RAND_MAX		//随机产生0-1的数
			//0.5f-float (rand())/RAND_MAX产生-0.5到0.5的随机数,加入一些扰动

		}

		//若小球与鼠标的距离小于stirDist,则受到扰动
		if (d < stirDist)
		{
			//扰动力引起的加速度幅度,小球距鼠标越近,引起的加速度越大,扰动力越小
			float mAcc = (1 - (d / stirDist)) * Width * 0.00026f;

			//鼠标移动速度越快,扰动越大
			movers[i].vx = movers[i].vx + mouseVX * mAcc;
			movers[i].vy = movers[i].vy + mouseVY * mAcc;

		}

		//小球运动有一个阻尼(摩擦力),速度逐渐减小
		movers[i].vx *= FRICTION;
		movers[i].vy *= FRICTION;

		//速度的绝对值
		float avgvx = abs(movers[i].x);
		float avgvy = abs(movers[i].y);
		//两个方向的平均速度
		float avgv = (avgvx + avgvy) * 0.5f;

		//由于阻尼作用,若速度过小,则乘以一个0到3的随机数,会较大概率变大速度
		if (avgvx < 0.1)
		movers[i].vx = movers[i].vx * float((rand())) / RAND_MAX * 3;
			
		if (avgvy < 0.1)
			movers[i].vy = movers[i].vy * float(rand()) / RAND_MAX * 3;

		//小球的半径在0.4到3.5之间,速度越大,半径越大
		float sc = avgv * 0.45f;
		sc = max(min(sc, 3.5f), 0.4f);
		movers[i].radius = sc;


		//更新小球的坐标 
		float nextx = movers[i].x + movers[i].vx;
		float nexty = movers[i].y + movers[i].vy;

		//小球超过上下左右的边界,小球反弹
		if (nextx > Width)
		{
			nextx = Width;
			movers[i].vx = -movers[i].vx;
		}

		else if (nextx < 0)
		{
			nextx = 0;
			movers[i].vx = -movers[i].vx;
		}


		if (nexty > High)
		{
			nexty = High;
			movers[i].vy = -movers[i].vy;
		}

		else if (nexty < 0)
		{
			nexty = 0;
			movers[i].vy = -movers[i].vy;
		}


		//更新小球的位置速度的结构体数组
		movers[i].x = nextx;
		movers[i].y = nexty;


	}

}

void updateWithInput()
{
	//定义鼠标信息
	MOUSEMSG m;

	while (MouseHit())
	{
		m = GetMouseMsg();
		if (m.uMsg = WM_MOUSEMOVE)
		{
			mouseX = m.x;
			mouseY = m.y;
		}	
		else if (m.uMsg = WM_LBUTTONDOWN)	//鼠标左键按下
		{
			isMouseDown = 1;

		}

		else if (m.uMsg = WM_LBUTTONUP)		//鼠标左键松开
		{
			isMouseDown = 0;

		}

	}


}

void gameover()
{

	EndBatchDraw();
	closegraph();

}

int main()
{
	startup();

	while (1)
	{
		show();
		updateWithoutInput();
		updateWithInput();

	}

	gameover();
	return 0;

}

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值