C语言实现极简物理引擎(二):小球弹性碰撞

目标

        在上一章的基础上,实现小球的弹性碰撞和摩擦力模拟


开发环境

        本项目在 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!");

        效果

ecdbda3fcd7a47d2a059ee1fb1295e4d.png


碰撞物理模拟

弹性碰撞

        为模拟弹性碰撞效果,对 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;
}

当前效果

1e9564e451944b6f84d2a164aa232c48.png


给轨迹上色

        为增加可视化效果,根据小球运动的不同阶段,通过 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;
}

最终效果

3edf3c634cce49178a360fd86599f05b.png

  • 39
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Annactswell

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值