目标
在上一章的基础上,实现小球的弹性碰撞和摩擦力模拟
开发环境
本项目在 Dev-C++ 环境下开发
回顾
在《C语言实现极简物理引擎(一):小球自由落体》中,我们实现了小球的自由落体文本动画物理模拟,本章节将在此基础上实现小球的弹性碰撞
上章代码
#include<stdio.h>
#include<stdbool.h>
#include<conio.h>
#include<Windows.h>
int MAX_SIZE = 25;
double REFRESH_TIME = 0.01;
void gotoxy(int x,int y){
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos = {x,y};
SetConsoleCursorPosition(handle,pos);
}
void hideCursor(){
CONSOLE_CURSOR_INFO cursor_info = {1,0};
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info);
}
bool checkBoundary(double x,double y){
if(x<=0 || x>=MAX_SIZE-1 || y<=0 || y>=MAX_SIZE-1)
return true;
else return false;
}
void drawBackground(){
gotoxy(0,0);
for(int x=0;x<MAX_SIZE;x++){
for(int y=0;y<MAX_SIZE;y++){
if(checkBoundary((double)x,(double)y)) printf("墙");
else printf(" ");
}
printf("\n");
}
}
void drawBall(int x,int y){
gotoxy(2*x,y);
printf("球");
}
void simulateFreeFall(double x,double y,double vx,double vy,double g){
double t = 0;
while(true){
drawBall((int)x,(int)y);
Sleep(REFRESH_TIME*1000);
x = x + vx*t;
y = y + vy*t + 0.5*g*t*t;
t += REFRESH_TIME;
if(checkBoundary(x,y)){
getchar();
break;
};
}
}
int main(){
double x,y,vx,vy,g;
x=1; y=1; vx=1; vy=0; g=9.8;
//此处可自行修改参数
//printf("画布大小(m):"); scanf("%d",&MAX_SIZE);
//printf("\n刷新时间(s):"); scanf("%lf",&REFRESH_TIME);
//printf("\n初始横坐标(m):"); scanf("%lf",&x);
//printf("\n初始纵坐标(m):"); scanf("%lf",&y);
//printf("\n初始水平速度(m/s):"); scanf("%lf",&vx);
//printf("\n初始竖直速度(m/s):"); scanf("%lf",&vy);
//printf("\n重力加速度(m/s^2):"); scanf("%lf",&g);
getchar();
hideCursor();
drawBackground();
simulateFreeFall(x,y,vx,vy,g);
return 0;
}
基本函数
光标定位函数 gotoxy 和 光标隐藏函数 hideCursor 见上一章
头文件
在项目开始之前,我们需要引入几个必要的头文件
#include<stdio.h>
#include<conio.h>
#include<Windows.h>
改变输出颜色
setColor 函数定义如下,用于改变控制台中文字的颜色
void SetColor(UINT uBack,UINT uFore){
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(handle,uFore + uBack*0x10);
}
使用:system("color 控制台颜色 字符颜色");
颜色对应关系如下:
0:黑色;1:蓝色;2:绿色;3:浅绿色;4:红色;5:紫色;6:黄色;7:白色;8:灰色;9:淡蓝色;A:淡绿色;B:淡浅绿色;C:淡红色;D:淡紫色;E:淡黄色;F:亮白色;
示例
SetColor(3,14);
printf("Hello,");
SetColor(0,6);
printf(" World!");
效果
碰撞物理模拟
弹性碰撞
为模拟弹性碰撞效果,对 checkBoundary 函数做如下修改
原代码
bool checkBoundary(double x,double y){
if(x<=0 || x>=MAX_SIZE-1 || y<=0 || y>=MAX_SIZE-1)
return true;
else return false;
}
修改后
bool checkBoundary(double *x, double *y, double *vx, double *vy){
bool isCollided = false; // 判断并处理小球与边界的碰撞
if(*x <= 1 || *x >= MAX_SIZE - 1){ // 检查水平边界碰撞
*x = (*x <= 1) ? 1 : MAX_SIZE - 1;
*vx = -(*vx); // 反转水平速度
isCollided = true;
}
if(*y <= 1 || *y >= MAX_SIZE - 1){ // 检查垂直边界碰撞
*y = (*y <= 1) ? 1 : MAX_SIZE - 1;
*vy = -(*vy); // 反转竖直速度
isCollided = true;
}
return isCollided;
}
碰撞损失和摩擦力
为了模拟弹性碰撞过程中的损失和摩擦力,我们引入变量 collisionLoss 和 friction,加入碰撞损失和摩擦力的处理。
bool checkBoundary(double *x, double *y, double *vx, double *vy, double collisionLoss, double friction){
bool isCollided = false; // 判断并处理小球与边界的碰撞
if(*x <= 1 || *x >= MAX_SIZE - 1){ // 检查水平边界碰撞
*x = (*x <= 1) ? 1 : MAX_SIZE - 1;
*vx = -(*vx) * collisionLoss; // 反转并减速水平速度
*vy *= friction; // 竖直方向摩擦减速
isCollided = true;
}
if(*y <= 1 || *y >= MAX_SIZE - 1){ // 检查垂直边界碰撞
*y = (*y <= 1) ? 1 : MAX_SIZE - 1;
*vy = -(*vy) * collisionLoss; // 反转并减速竖直速度
*vx *= friction; // 水平方向摩擦减速
isCollided = true;
}
return isCollided;
}
当前效果
给轨迹上色
为增加可视化效果,根据小球运动的不同阶段,通过 setColor 函数改变输出颜色,每次碰撞后,小球轨迹颜色改变。
int color = 1;
bool isCollided = checkBoundary(&x, &y, &vx, &vy, collisionLoss, friction);
if(isCollided){
color = (++color) % 16; // 更改颜色
setColor(0, color); // 应用新颜色
};
完整代码
#include<stdio.h>
#include<conio.h>
#include<Windows.h>
int MAX_SIZE = 40; // 40的边长比较大,记得最大化控制台窗口
double REFRESH_TIME = 0.01;
// 光标定位函数
void gotoxy(int x,int y){
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos = {x,y};
SetConsoleCursorPosition(handle,pos);
}
// 光标隐藏函数
void hideCursor(){
CONSOLE_CURSOR_INFO cursor_info = {1,0};
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info);
}
// 设置控制台文字颜色
void setColor(UINT uBack,UINT uFore){
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(handle,uFore + uBack*0x10);
}
// 检查边界碰撞
bool checkBoundary(double *x, double *y, double *vx, double *vy, double collisionLoss, double friction){
bool isCollided = false; // 判断并处理小球与边界的碰撞
if(*x <= 1 || *x >= MAX_SIZE - 1){ // 检查水平边界碰撞
*x = (*x <= 1) ? 1 : MAX_SIZE - 1;
*vx = -(*vx) * collisionLoss; // 反转并减速水平速度
*vy *= friction; // 竖直方向摩擦减速
isCollided = true;
}
if(*y <= 1 || *y >= MAX_SIZE - 1){ // 检查垂直边界碰撞
*y = (*y <= 1) ? 1 : MAX_SIZE - 1;
*vy = -(*vy) * collisionLoss; // 反转并减速竖直速度
*vx *= friction; // 水平方向摩擦减速
isCollided = true;
}
return isCollided;
}
// 绘制背景
void drawBackground(){
gotoxy(0,0);
for(int x=0; x<=MAX_SIZE; x++){
for(int y=0; y<=MAX_SIZE; y++){
if(x==0 || x==MAX_SIZE || y==0 || y==MAX_SIZE) printf("墙");
else printf(" ");
}
printf("\n");
}
}
// 绘制小球
void drawBall(int x,int y){
gotoxy(2*x,y);
printf("球");
}
int main(){
int color=0;
double x,y,vx,vy,g,collisionLoss,friction;
x=1; y=1; vx=20; vy=0; g=98; collisionLoss=0.85; friction=0.95;
//此处可自行修改参数
//printf("画布大小(m):"); scanf("%d",&MAX_SIZE);
//printf("\n刷新时间(s):"); scanf("%lf",&REFRESH_TIME);
//printf("\n初始横坐标(m):"); scanf("%lf",&x);
//printf("\n初始纵坐标(m):"); scanf("%lf",&y);
//printf("\n初始水平速度(m/s):"); scanf("%lf",&vx);
//printf("\n初始竖直速度(m/s):"); scanf("%lf",&vy);
//printf("\n重力加速度(m/s^2):"); scanf("%lf",&g);
//printf("\n碰撞损失:"); scanf("%lf",&collisionLoss);
//printf("\n摩擦损失:"); scanf("%lf",&friction);
hideCursor();
drawBackground();
getchar();
while(true){
Sleep(REFRESH_TIME*1000);
vy = vy + g*REFRESH_TIME;
x = x + vx*REFRESH_TIME;
y = y + vy*REFRESH_TIME;
bool isCollided = checkBoundary(&x,&y,&vx,&vy,collisionLoss,friction);
if(isCollided){
color = (++color)%16;
setColor(0,color);
};
drawBall((int)x, (int)y);
}
return 0;
}