关于C语言的模拟物理模型
声明:本文全部代码效果基于C语言easyx图形界面库。
引言
关于很多游戏和模型的开发,都需要模拟真实的物理模型
比如:基本矢量运动模型(位移,速度,加速度),重力模型,碰撞模型,万有引力模型。
本文我会一一介绍这几种模型的开发。
既然是模拟显示物理模型,那么引入的现实物理公式也是必要的。
本文所需要引用的公式有:其中TIKME_STEP是每帧变化的量,来弥补时间效果和逐帧效果的差
最后的总物理模型的话,可以选择性给出每个球的状态情况,比如每个方向的初速度,初加速度,以及是否受重力和球之间的万有引力,对偏心碰撞,边框碰撞。
代码
位移公式 : ▲x = v * t
程序内使用:x += vx y+= vy (逐帧使用)
void move()//移动
{for (int i = 0; i <= 20; i++) {if (a[i].live) {a[i].x += a[i].vx * TIME_STEP;a[i].y += a[i].vy * TIME_STEP;}}
}
速度公式 :▲v = a * t
程序内使用 vx += ax vy += ay (逐帧使用)
void speed()//速度变化
{for (int i = 0; i <= 20; i++) {if (a[i].live) {a[i].vx += a[i].ax * TIME_STEP;a[i].vy += a[i].ay * TIME_STEP;}}
}
加速度公式:a = F / m
程序内使用 (Fx+Fx1+Fx2...) / m = ax (Fy+Fy1+Fy2...) / m = ay
void Fa()//受力
{for (int i = 0; i <= 20; i++) {if (a[i].live) {a[i].ax = (a[i].Fx + a[i].Fwx) / a[i].m;a[i].ay = (a[i].Fy + a[i].GN + a[i].Fwy) / a[i].m;}}
}
重力公式: G = mg
程序内使用 G = m*g (g预定义数值) 然后G 是Fy的组成元素a[i].GN = a[i].m * g;
碰撞前后的动量守恒和能量守恒
m1*v1 + m2*v2 = m1*v3+m2*v4;
0.5*m1*v1*v1 + 0.5*m2*v2*v2 = 0.5*m1*v3*v3 + 0.5*m2*v3*v4;
然后再考虑是否对心碰撞分析左右偏移向量
// 检测并处理小球碰撞
void collisionDetection()
{for (int i = 0; i < 20; i++) {if (a[i].live) {for (int j = i + 1; j <= 20; j++) {if (a[j].live) {double dx = a[i].x - a[j].x;double dy = a[i].y - a[j].y;double distance = sqrt(dx * dx + dy * dy);if (distance < 100) { // 两个小球半径之和为100double m1 = a[i].m;double m2 = a[j].m;double v1ix = a[i].vx;double v1iy = a[i].vy;double v2ix = a[j].vx;double v2iy = a[j].vy;// 计算碰撞法线方向的单位向量double n_x = dx / distance;double n_y = dy / distance;// 计算相对速度在法线方向和切线方向的分量double v1n = v1ix * n_x + v1iy * n_y;double v2n = v2ix * n_x + v2iy * n_y;double v1t = v1iy * n_x - v1ix * n_y;double v2t = v2iy * n_x - v2ix * n_y;// 应用动量守恒和恢复系数计算碰撞后的法向速度double v1nf = ((m1 - m2) * v1n + 2 * m2 * v2n + RESTITUTION_COEFFICIENT * m2 * (v2n - v1n)) / (m1 + m2);double v2nf = ((m2 - m1) * v2n + 2 * m1 * v1n + RESTITUTION_COEFFICIENT * m1 * (v1n - v2n)) / (m1 + m2);// 切线方向速度不变(假设无摩擦力)double v1tf = v1t;double v2tf = v2t;// 将法向和切向速度转换回笛卡尔坐标系a[i].vx = v1nf * n_x - v1tf * n_y;a[i].vy = v1nf * n_y + v1tf * n_x;a[j].vx = v2nf * n_x - v2tf * n_y;a[j].vy = v2nf * n_y + v2tf * n_x;}}}}}
}
边框碰撞模型
void peng()//碰撞影响一定放到位置更新之后
{for (int i = 0; i <= 20; i++) {if (a[i].live) {if (a[i].y - 50 < 0) {a[i].y = 50; // 精确调整位置到边界a[i].vy = -a[i].vy; // 确保速度反向}if (a[i].x - 50 < 0) {a[i].x = 50;a[i].vx = -a[i].vx;}if (a[i].y + 50 > high) {a[i].y = high - 50;a[i].vy = -a[i].vy;}if (a[i].x + 50 > width) {a[i].x = width - 50;a[i].vx = -a[i].vx;}}}
}
检测遇到边界后,改变水平或者垂直方向的速度方向相反,保持速度大小不变,
实现碰撞墙壁效果。
万有引力模型
F万 = G*m1*m2/(x*x)
void F_allocation()
{double sin_w = (a[0].y - a[1].y) / sqrt((a[0].y - a[1].y) * (a[0].y - a[1].y) + (a[0].x - a[1].x) * (a[0].x - a[1].x));double cos_w = (a[0].x - a[1].x) / sqrt((a[0].y - a[1].y) * (a[0].y - a[1].y) + (a[0].x - a[1].x) * (a[0].x - a[1].x));double Fw = Gk * a[0].m * a[1].m / ((a[0].y - a[1].y) * (a[0].y - a[1].y) + (a[0].x - a[1].x) * (a[0].x - a[1].x));a[0].Fwx = -Fw * cos_w;a[0].Fwy = -Fw * sin_w;a[1].Fwx = -a[0].Fwx;a[1].Fwy = -a[0].Fwy;
}
关于运行前的准备
预编译和结构体
#include <graphics.h>
#include <conio.h>
#include <stdio.h>
#include <time.h>
#include <stdbool.h>
#include <math.h>#define g 98
#define width 1700
#define high 1000
#define TIME_STEP 0.05 // 固定时间步长
#define Gk 677770
#define RESTITUTION_COEFFICIENT 0.8 // 恢复系数,0 <= e <= 1,0为完全非弹性,1为完全弹性typedef struct stu {double x;double y;double ax;double ay;double vx;double vy;double Fx;double Fy;double Fwx;double Fwy;double m;double GN;bool live;
} G;
代码是集成的,每个作用都在,如果要研究其中某种模型,只需要调用该模型函数即可。
比如研究万有引力模型,需要将重力模型关掉,然后控制不要碰撞,这里数值我测出来了一个效果就是,用假设初速度完全满足万有引力提供向心力从而做圆周运动的模型,
我们需要将两个球的质量全设置为10,然后锁定右侧球,两球高度一致,赋予左球vy = 130便可以满足万有引力完全提供向心力的天体匀速圆周模型。
展示总函数
#include <graphics.h>
#include <conio.h>
#include <stdio.h>
#include <time.h>
#include <stdbool.h>
#include <math.h>#define g 98
#define width 1700
#define high 1000
#define TIME_STEP 0.05 // 固定时间步长
#define Gk 677770
#define RESTITUTION_COEFFICIENT 0.8 // 恢复系数,0 <= e <= 1,0为完全非弹性,1为完全弹性typedef struct stu {double x;double y;double ax;double ay;double vx;double vy;double Fx;double Fy;double Fwx;double Fwy;double m;double GN;bool live;
} G;G a[21];void Fa()//受力
{for (int i = 0; i <= 20; i++) {if (a[i].live) {a[i].ax = (a[i].Fx + a[i].Fwx) / a[i].m;a[i].ay = (a[i].Fy + a[i].GN + a[i].Fwy) / a[i].m;}}
}void speed()//速度变化
{for (int i = 0; i <= 20; i++) {if (a[i].live) {a[i].vx += a[i].ax * TIME_STEP;a[i].vy += a[i].ay * TIME_STEP;}}
}void move()//移动
{for (int i = 0; i <= 20; i++) {if (a[i].live) {a[i].x += a[i].vx * TIME_STEP;a[i].y += a[i].vy * TIME_STEP;}}
}void draw()
{for (int i = 0; i <= 20; i++) {if (a[i].live) {solidcircle((int)a[i].x, (int)a[i].y, 50);}}
}void peng()//碰撞影响一定放到位置更新之后
{for (int i = 0; i <= 20; i++) {if (a[i].live) {if (a[i].y - 50 < 0) {a[i].y = 50; // 精确调整位置到边界a[i].vy = -a[i].vy; // 确保速度反向}if (a[i].x - 50 < 0) {a[i].x = 50;a[i].vx = -a[i].vx;}if (a[i].y + 50 > high) {a[i].y = high - 50;a[i].vy = -a[i].vy;}if (a[i].x + 50 > width) {a[i].x = width - 50;a[i].vx = -a[i].vx;}}}
}void init()
{for (int i = 0; i <= 20; i++) {a[i].x = 0;a[i].y = 0;a[i].ax = 0;a[i].ay = 0;a[i].vx = 0;a[i].vy = 0;a[i].Fx = 0;a[i].Fy = 0;a[i].Fwx = 0;a[i].Fwy = 0;a[i].m = 10;a[i].live = false;a[i].GN = a[i].m * g;}
}void F_allocation()
{double sin_w = (a[0].y - a[1].y) / sqrt((a[0].y - a[1].y) * (a[0].y - a[1].y) + (a[0].x - a[1].x) * (a[0].x - a[1].x));double cos_w = (a[0].x - a[1].x) / sqrt((a[0].y - a[1].y) * (a[0].y - a[1].y) + (a[0].x - a[1].x) * (a[0].x - a[1].x));double Fw = Gk * a[0].m * a[1].m / ((a[0].y - a[1].y) * (a[0].y - a[1].y) + (a[0].x - a[1].x) * (a[0].x - a[1].x));a[0].Fwx = -Fw * cos_w;a[0].Fwy = -Fw * sin_w;//a[1].Fwx = -a[0].Fwx;//a[1].Fwy = -a[0].Fwy;
}// 检测并处理小球碰撞
void collisionDetection()
{for (int i = 0; i < 20; i++) {if (a[i].live) {for (int j = i + 1; j <= 20; j++) {if (a[j].live) {double dx = a[i].x - a[j].x;double dy = a[i].y - a[j].y;double distance = sqrt(dx * dx + dy * dy);if (distance < 100) { // 两个小球半径之和为100double m1 = a[i].m;double m2 = a[j].m;double v1ix = a[i].vx;double v1iy = a[i].vy;double v2ix = a[j].vx;double v2iy = a[j].vy;// 计算碰撞法线方向的单位向量double n_x = dx / distance;double n_y = dy / distance;// 计算相对速度在法线方向和切线方向的分量double v1n = v1ix * n_x + v1iy * n_y;double v2n = v2ix * n_x + v2iy * n_y;double v1t = v1iy * n_x - v1ix * n_y;double v2t = v2iy * n_x - v2ix * n_y;// 应用动量守恒和恢复系数计算碰撞后的法向速度double v1nf = ((m1 - m2) * v1n + 2 * m2 * v2n + RESTITUTION_COEFFICIENT * m2 * (v2n - v1n)) / (m1 + m2);double v2nf = ((m2 - m1) * v2n + 2 * m1 * v1n + RESTITUTION_COEFFICIENT * m1 * (v1n - v2n)) / (m1 + m2);// 切线方向速度不变(假设无摩擦力)double v1tf = v1t;double v2tf = v2t;// 将法向和切向速度转换回笛卡尔坐标系a[i].vx = v1nf * n_x - v1tf * n_y;a[i].vy = v1nf * n_y + v1tf * n_x;a[j].vx = v2nf * n_x - v2tf * n_y;a[j].vy = v2nf * n_y + v2tf * n_x;}}}}}
}int main()
{init();a[0].live = true;a[0].x = 200;a[0].y = 500;/*a[0].vx = 50;a[0].vy = 20;*/a[1].m = 10;a[1].live = true;a[1].x = 600;a[1].y = 500;/*a[1].vx = 30;a[1].vy = 20;*/// 初始化图形窗口,大小为 width x highinitgraph(width, high);// 设置填充颜色为绿色setfillcolor(GREEN);BeginBatchDraw();while (!_kbhit()) {cleardevice(); // 清除绘图// F_allocation();Fa();speed();move();peng();collisionDetection(); // 检测并处理碰撞draw();FlushBatchDraw();Sleep(8);}EndBatchDraw();// 关闭图形窗口closegraph();return 0;
}
本文展现的是集成的物理模型框架,感兴趣的可以尝试调试实现各种效果。
我自己测试的是
1,万有引力完全提供向心力的匀速圆周运动
2,对偏心碰撞的守恒
3,以及编写初期的重力测试,和碰撞测试,加速度测试。
至于运行效果,这博客也不好发视频,大家可以尝试敲一下代码运行试试。
当物理模型自己结合物理公式和编程写出来展现在眼前的物理运动时,是真的相信课本里说的天体运动,也是切身感受到了,还有碰撞问题,感觉可以写个桌球小游戏,不过这些交给大家发挥了,本文到此,感谢观看。