#include <graphics.h>
#include <math.h>
#include <time.h>
#define WIDTH 1000 // 屏幕宽度
#define HEIGHT 800 // 屏幕高度
#define NUM_BALLS 1024 // 小球数目
#define FRICTION 0.95f // 摩擦力/阻尼系数
// 定义小球结构
struct Ball
{
COLORREF color; // 颜色
float x, y; // 坐标
float vx, vy; // 速度
float radius; // 半径
};
// 定义全局变量
Ball balls[NUM_BALLS]; // 小球数组
int mouseX, mouseY; // 当前鼠标坐标
int prevMouseX, prevMouseY; // 上次鼠标坐标
int mouseVX, mouseVY; // 鼠标移动速度
int isMouseDown; // 鼠标左键是否按下
// 绝对延时
void delay(DWORD ms)
{
static DWORD oldtime = GetTickCount();
while (GetTickCount() - oldtime < ms)
Sleep(1);
oldtime = GetTickCount();
}
void startup() // 初始化函数
{
// 初始化随机种子
srand(time(0)); // 随机种子函数
initgraph(WIDTH, HEIGHT); // 新建画布
// 初始化小球数组
for (int i = 0; i < NUM_BALLS; i++)
{
balls[i].color = RGB(rand() % 255, rand() % 255, rand() % 255);
balls[i].x = rand() % WIDTH;
balls[i].y = rand() % HEIGHT;
balls[i].vx = float(cos(float(i))) * (rand() % 35);
balls[i].vy = float(sin(float(i))) * (rand() % 35);
balls[i].radius = (rand() % 35) / 18.0;
}
// 初始化鼠标变量,当前鼠标坐标、上次鼠标坐标都在画布中心
mouseX = prevMouseX = WIDTH / 2;
mouseY = prevMouseY = HEIGHT / 2;
isMouseDown = 0; // 初始鼠标未按下
BeginBatchDraw(); // 开始批量绘制
}
void show() // 绘制函数
{
cleardevice(); // 清屏
for (int i = 0; i < NUM_BALLS; i++)
{
// 画小球
setcolor(balls[i].color);
setfillstyle(balls[i].color);
fillcircle(int(balls[i].x + 0.5), int(balls[i].y + 0.5), int(balls[i].radius + 0.5));
}
FlushBatchDraw(); // 批量绘制
delay(5); // 暂停
}
void updateWithoutInput() // 与输入无关的更新
{
float toDist = WIDTH * 0.85; // 吸引距离,小球距离鼠标在此范围内,会受到向内的吸力
float blowDist = WIDTH * 0.4; // 打击距离,小球距离鼠标在此范围内,会受到向外的斥力
float stirDist = WIDTH * 0.15; // 扰动距离,小球距离鼠标在此范围内,会受到鼠标的扰动
// 前后两次运行间鼠标移动的距离,即为鼠标的速度
mouseVX = mouseX - prevMouseX;
mouseVY = mouseY - prevMouseY;
// 更新上次鼠标坐标变量,记录为当前鼠标的坐标
prevMouseX = mouseX;
prevMouseY = mouseY;
for (int i = 0; i < NUM_BALLS; i++) // 对所有小球遍历
{
float x = balls[i].x; // 当前小球坐标
float y = balls[i].y;
float vx = balls[i].vx; // 当前小球速度
float vy = balls[i].vy;
float dx = x - mouseX; // 计算当前小球位置和鼠标位置的差
float dy = y - mouseY;
float d = sqrt(dx * dx + dy * dy); // 当前小球和鼠标位置的距离
// 下面将dx、dy归一化,仅反映方向,和距离大小无关
if (d != 0)
{
dx = dx / d;
dy = dy / d;
}
else
{
dx = 0;
dy = 0;
}
// 小球距离鼠标 < toDist,在此范围内小球会受到鼠标的吸引
if (d < toDist)
{
// 吸引力引起的加速度幅度,小球距离鼠标越近,引起的加速度越大
float toAcc = (1 - (d / toDist)) * WIDTH * 0.0013f;
// 由dx、dy归一化方向信息,加速度幅度值toAcc,得到新的小球速度
vx = vx - dx * toAcc;
vy = vy - dy * toAcc;
}
// 当鼠标左键按下,并且小球距离鼠标 < blowDist(在打击范围内,会受到向外的力)
if (isMouseDown && d < blowDist)
{
// 打击力引起的加速度幅度(Acceleration),这个公式表示小球距离鼠标越近,打击斥力引起的加速度越大
float blowAcc = (1 - (d / blowDist)) * 11;
// 由上面得到的dx、dy归一化方向信息,上面的加速度幅度值blowAcc,得到新的小球速度
// 0.5f - float(rand()) / RAND_MAX 产生[-0.5,0.5]之间的随机数,加入一些扰动
vx = vx + dx * blowAcc + 0.5f - float(rand()) / RAND_MAX;
vy = vy + dy * blowAcc + 0.5f - float(rand()) / RAND_MAX;
}
// 小球距离鼠标 < stirDist,在此范围内小球会受到鼠标的扰动
if (d < stirDist)
{
// 扰动力引起的加速度幅度,小球距离鼠标越近,引起的加速度越大。扰动力的值更小
float mAcc = (1 - (d / stirDist)) * WIDTH * 0.00026f;
// 鼠标速度越快,引起的扰动力越大
vx = vx + mouseVX * mAcc;
vy = vy + mouseVY * mAcc;
}
// 小球运动有一个阻尼(摩擦力),速度逐渐减少
vx = vx * FRICTION;
vy = vy * FRICTION;
// 速度的绝对值
float avgVX = fabs(vx);
float avgVY = fabs(vy);
// 两个方向速度的平均
float avgV = (avgVX + avgVY) * 0.5f;
// 因为有上面阻尼的作用,如果速度过小的话,乘以一个[0,3]的随机数,会以比较大的概率让速度变大
if (avgVX < 0.1)
vx = vx * float(rand()) / RAND_MAX * 3;
if (avgVY < 0.1)
vy = vy * float(rand()) / RAND_MAX * 3;
// 小球的半径在[0.5,2.0]之间,速度越大,半径越大
float sc = avgV * 0.4f;
sc = max(min(sc, 2.0f), 0.5f);
balls[i].radius = sc;
// 根据位置+速度,更新小球的坐标
float nextX = x + vx;
float nextY = y + vy;
// 小球如果超过上下左右四个边界的话,位置设为边界处,速度反向
if (nextX > WIDTH)
{
nextX = WIDTH;
vx = -1 * vx;
}
else if (nextX < 0)
{
nextX = 0;
vx = -1 * vx;
}
if (nextY > HEIGHT)
{
nextY = HEIGHT;
vy = -1 * vy;
}
else if (nextY < 0)
{
nextY = 0;
vy = -1 * vy;
}
// 更新小球位置、速度的结构体数组
balls[i].vx = vx;
balls[i].vy = vy;
balls[i].x = nextX;
balls[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;
}
}
int main()
{
startup(); // 数据初始化
while (1) // 游戏循环执行
{
show(); // 显示画面
updateWithInput(); // 与用户输入有关的更新
updateWithoutInput(); // 与用户输入无关的更新
}
return 0;
}
这是河海大学童晶老师主教的一门课:基于游戏开发的C语言程序设计入门与实践_河海大学_中国大学MOOC(慕课)
上面的代码也是课程中的
用游戏敲开c语言编程的大门