活动公告

系统通知
05-18 21:22
系统通知
通知:本站资源由网友上传分享,如有违规等问题请到版务模块进行投诉,资源失效请在帖子内回复要求补档,会尽快处理!
10-23 09:31

深入浅出用C语言编写落体反弹物理模拟程序的实用教程

SunJu_FaceMall

3万

主题

2860

科技点

3万

积分

白金月票

碾压王

积分
32872

塔罗立华奏

<font color=白金月票" /> 发表于 2025-9-12 18:40:01 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
1. 物理模拟基础

物理模拟是通过计算机程序来模拟现实世界中的物理现象。在本文中,我们将专注于模拟一个物体在重力作用下下落并反弹的简单物理系统。这种模拟不仅有助于理解基本的物理原理,也是学习编程和计算机图形学的绝佳入门项目。

1.1 落体反弹的物理原理

在现实世界中,当一个物体从高处落下时,它受到重力的作用而加速下降。当物体撞击地面时,它会根据材料的弹性发生反弹。每次反弹后,物体达到的高度会逐渐降低,直到最终停止弹跳。

这个过程中涉及几个关键的物理概念:

• 重力加速度:在地球表面,物体自由下落的加速度约为9.8 m/s²,通常用g表示。
• 速度:物体的速度随时间变化,v = v₀ + gt,其中v₀是初始速度。
• 位置:物体的位置随时间变化,y = y₀ + v₀t + ½gt²,其中y₀是初始位置。
• 能量损失:每次反弹时,由于空气阻力和碰撞时的能量转化,物体会有部分能量损失,通常用恢复系数(coefficient of restitution)表示。

1.2 模拟的基本思路

在计算机程序中模拟这个物理过程,我们需要:

1. 定义物体的初始状态(位置、速度)
2. 在每个时间步长更新物体的速度和位置
3. 检测物体是否与地面碰撞
4. 如果发生碰撞,计算反弹后的速度
5. 重复上述过程,直到满足结束条件

2. 程序设计

2.1 开发环境准备

在开始编写代码之前,我们需要准备一个适合C语言开发的環境。对于本教程,我们假设您已经安装了:

• C编译器(如GCC)
• 一个文本编辑器或IDE(如Visual Studio Code、Dev-C++等)
• 如果需要图形显示,还需要安装图形库(如SDL、OpenGL或简单的控制台图形库)

2.2 程序结构设计

我们的程序将包含以下几个主要部分:

1. 头文件包含:包含必要的库文件
2. 常量定义:定义物理常量和模拟参数
3. 全局变量:存储物体的状态信息
4. 初始化函数:设置初始条件
5. 物理更新函数:计算物体在每个时间步长的状态
6. 碰撞检测函数:检测并处理物体与地面的碰撞
7. 显示函数:在屏幕上绘制物体的位置
8. 主循环:控制模拟的进行

3. 代码实现

3.1 基本框架

首先,我们创建程序的基本框架:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <math.h>
  4. #include <time.h>
  5. // 物理常量
  6. #define GRAVITY 9.8        // 重力加速度 (m/s²)
  7. #define TIME_STEP 0.01     // 时间步长 (s)
  8. #define COEFF_RESTITUTION 0.8  // 恢复系数
  9. // 模拟参数
  10. #define GROUND_LEVEL 0.0   // 地面位置 (m)
  11. #define INITIAL_HEIGHT 10.0 // 初始高度 (m)
  12. #define INITIAL_VELOCITY 0.0 // 初始速度 (m/s)
  13. // 全局变量
  14. double position = INITIAL_HEIGHT;  // 当前位置
  15. double velocity = INITIAL_VELOCITY; // 当前速度
  16. double time = 0.0;                 // 模拟时间
  17. int bounce_count = 0;              // 反弹次数
  18. // 函数声明
  19. void initialize_simulation();
  20. void update_physics();
  21. void check_collision();
  22. void display_state();
  23. void run_simulation();
  24. int main() {
  25.     run_simulation();
  26.     return 0;
  27. }
复制代码

3.2 初始化函数

初始化函数用于设置模拟的初始条件:
  1. void initialize_simulation() {
  2.     position = INITIAL_HEIGHT;
  3.     velocity = INITIAL_VELOCITY;
  4.     time = 0.0;
  5.     bounce_count = 0;
  6.    
  7.     printf("落体反弹物理模拟程序\n");
  8.     printf("初始高度: %.2f m\n", INITIAL_HEIGHT);
  9.     printf("初始速度: %.2f m/s\n", INITIAL_VELOCITY);
  10.     printf("重力加速度: %.2f m/s²\n", GRAVITY);
  11.     printf("恢复系数: %.2f\n", COEFF_RESTITUTION);
  12.     printf("--------------------------------\n");
  13. }
复制代码

3.3 物理更新函数

物理更新函数负责计算物体在每个时间步长的速度和位置:
  1. void update_physics() {
  2.     // 更新速度 (v = v0 + g*t)
  3.     velocity -= GRAVITY * TIME_STEP;
  4.    
  5.     // 更新位置 (y = y0 + v*t)
  6.     position += velocity * TIME_STEP;
  7.    
  8.     // 更新时间
  9.     time += TIME_STEP;
  10. }
复制代码

3.4 碰撞检测函数

碰撞检测函数检查物体是否与地面碰撞,并处理反弹:
  1. void check_collision() {
  2.     // 检测是否与地面碰撞
  3.     if (position <= GROUND_LEVEL && velocity < 0) {
  4.         // 确保物体不会穿透地面
  5.         position = GROUND_LEVEL;
  6.         
  7.         // 计算反弹速度 (v' = -e*v)
  8.         velocity = -velocity * COEFF_RESTITUTION;
  9.         
  10.         // 增加反弹计数
  11.         bounce_count++;
  12.         
  13.         printf("时间: %.2f s, 第 %d 次反弹, 反弹速度: %.2f m/s\n",
  14.                time, bounce_count, fabs(velocity));
  15.     }
  16. }
复制代码

3.5 显示函数

显示函数用于输出当前物体状态:
  1. void display_state() {
  2.     // 每隔一定时间输出一次状态
  3.     static int counter = 0;
  4.     counter++;
  5.    
  6.     if (counter >= 10) {  // 每10个时间步长输出一次
  7.         printf("时间: %.2f s, 位置: %.2f m, 速度: %.2f m/s\n",
  8.                time, position, velocity);
  9.         counter = 0;
  10.     }
  11. }
复制代码

3.6 主循环函数

主循环函数控制整个模拟的进行:
  1. void run_simulation() {
  2.     // 初始化模拟
  3.     initialize_simulation();
  4.    
  5.     // 模拟循环
  6.     while (time < 10.0) {  // 模拟10秒
  7.         // 更新物理状态
  8.         update_physics();
  9.         
  10.         // 检测碰撞
  11.         check_collision();
  12.         
  13.         // 显示当前状态
  14.         display_state();
  15.         
  16.         // 检查是否停止弹跳(速度非常小且在地面上)
  17.         if (fabs(velocity) < 0.1 && position <= GROUND_LEVEL + 0.01) {
  18.             printf("物体在 %.2f 秒后停止弹跳,共反弹 %d 次\n",
  19.                    time, bounce_count);
  20.             break;
  21.         }
  22.     }
  23.    
  24.     printf("模拟结束\n");
  25. }
复制代码

4. 完整代码示例

将上述所有部分组合起来,我们得到完整的落体反弹物理模拟程序:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <math.h>
  4. #include <time.h>
  5. // 物理常量
  6. #define GRAVITY 9.8        // 重力加速度 (m/s²)
  7. #define TIME_STEP 0.01     // 时间步长 (s)
  8. #define COEFF_RESTITUTION 0.8  // 恢复系数
  9. // 模拟参数
  10. #define GROUND_LEVEL 0.0   // 地面位置 (m)
  11. #define INITIAL_HEIGHT 10.0 // 初始高度 (m)
  12. #define INITIAL_VELOCITY 0.0 // 初始速度 (m/s)
  13. // 全局变量
  14. double position = INITIAL_HEIGHT;  // 当前位置
  15. double velocity = INITIAL_VELOCITY; // 当前速度
  16. double time = 0.0;                 // 模拟时间
  17. int bounce_count = 0;              // 反弹次数
  18. // 函数声明
  19. void initialize_simulation();
  20. void update_physics();
  21. void check_collision();
  22. void display_state();
  23. void run_simulation();
  24. int main() {
  25.     run_simulation();
  26.     return 0;
  27. }
  28. void initialize_simulation() {
  29.     position = INITIAL_HEIGHT;
  30.     velocity = INITIAL_VELOCITY;
  31.     time = 0.0;
  32.     bounce_count = 0;
  33.    
  34.     printf("落体反弹物理模拟程序\n");
  35.     printf("初始高度: %.2f m\n", INITIAL_HEIGHT);
  36.     printf("初始速度: %.2f m/s\n", INITIAL_VELOCITY);
  37.     printf("重力加速度: %.2f m/s²\n", GRAVITY);
  38.     printf("恢复系数: %.2f\n", COEFF_RESTITUTION);
  39.     printf("--------------------------------\n");
  40. }
  41. void update_physics() {
  42.     // 更新速度 (v = v0 + g*t)
  43.     velocity -= GRAVITY * TIME_STEP;
  44.    
  45.     // 更新位置 (y = y0 + v*t)
  46.     position += velocity * TIME_STEP;
  47.    
  48.     // 更新时间
  49.     time += TIME_STEP;
  50. }
  51. void check_collision() {
  52.     // 检测是否与地面碰撞
  53.     if (position <= GROUND_LEVEL && velocity < 0) {
  54.         // 确保物体不会穿透地面
  55.         position = GROUND_LEVEL;
  56.         
  57.         // 计算反弹速度 (v' = -e*v)
  58.         velocity = -velocity * COEFF_RESTITUTION;
  59.         
  60.         // 增加反弹计数
  61.         bounce_count++;
  62.         
  63.         printf("时间: %.2f s, 第 %d 次反弹, 反弹速度: %.2f m/s\n",
  64.                time, bounce_count, fabs(velocity));
  65.     }
  66. }
  67. void display_state() {
  68.     // 每隔一定时间输出一次状态
  69.     static int counter = 0;
  70.     counter++;
  71.    
  72.     if (counter >= 10) {  // 每10个时间步长输出一次
  73.         printf("时间: %.2f s, 位置: %.2f m, 速度: %.2f m/s\n",
  74.                time, position, velocity);
  75.         counter = 0;
  76.     }
  77. }
  78. void run_simulation() {
  79.     // 初始化模拟
  80.     initialize_simulation();
  81.    
  82.     // 模拟循环
  83.     while (time < 10.0) {  // 模拟10秒
  84.         // 更新物理状态
  85.         update_physics();
  86.         
  87.         // 检测碰撞
  88.         check_collision();
  89.         
  90.         // 显示当前状态
  91.         display_state();
  92.         
  93.         // 检查是否停止弹跳(速度非常小且在地面上)
  94.         if (fabs(velocity) < 0.1 && position <= GROUND_LEVEL + 0.01) {
  95.             printf("物体在 %.2f 秒后停止弹跳,共反弹 %d 次\n",
  96.                    time, bounce_count);
  97.             break;
  98.         }
  99.     }
  100.    
  101.     printf("模拟结束\n");
  102. }
复制代码

5. 代码解释和改进方向

5.1 代码解释

这个程序模拟了一个物体在重力作用下下落并反弹的过程。下面是对代码主要部分的解释:

1. 常量定义:我们定义了物理常量(如重力加速度)和模拟参数(如初始高度),这些值可以根据需要进行调整。
2. 全局变量:我们使用全局变量来跟踪物体的状态(位置、速度)和模拟信息(时间、反弹次数)。
3. 物理更新:update_physics()函数根据物理定律更新物体的速度和位置。我们使用简单的欧拉方法进行数值积分。
4. 碰撞检测:check_collision()函数检测物体是否与地面碰撞,并在碰撞时计算反弹速度。反弹速度由恢复系数决定,该系数表示碰撞后保留的动能比例。
5. 模拟循环:run_simulation()函数控制整个模拟过程,包括初始化、循环更新和结束条件检查。

常量定义:我们定义了物理常量(如重力加速度)和模拟参数(如初始高度),这些值可以根据需要进行调整。

全局变量:我们使用全局变量来跟踪物体的状态(位置、速度)和模拟信息(时间、反弹次数)。

物理更新:update_physics()函数根据物理定律更新物体的速度和位置。我们使用简单的欧拉方法进行数值积分。

碰撞检测:check_collision()函数检测物体是否与地面碰撞,并在碰撞时计算反弹速度。反弹速度由恢复系数决定,该系数表示碰撞后保留的动能比例。

模拟循环:run_simulation()函数控制整个模拟过程,包括初始化、循环更新和结束条件检查。

5.2 可能的改进方向

虽然这个基本程序能够模拟落体反弹的过程,但还有许多可以改进的地方:

当前的程序只在控制台输出文本信息。我们可以添加图形显示,使模拟更加直观。例如,使用SDL库创建一个简单的窗口,并在其中绘制物体的运动:
  1. // 需要安装SDL库
  2. #include <SDL2/SDL.h>
  3. #define WINDOW_WIDTH 800
  4. #define WINDOW_HEIGHT 600
  5. #define SCALE 50  // 像素/米的比例
  6. SDL_Window* window = NULL;
  7. SDL_Renderer* renderer = NULL;
  8. void init_graphics() {
  9.     SDL_Init(SDL_INIT_VIDEO);
  10.     window = SDL_CreateWindow("落体反弹模拟",
  11.                              SDL_WINDOWPOS_UNDEFINED,
  12.                              SDL_WINDOWPOS_UNDEFINED,
  13.                              WINDOW_WIDTH, WINDOW_HEIGHT,
  14.                              SDL_WINDOW_SHOWN);
  15.     renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
  16. }
  17. void draw_scene() {
  18.     // 清除屏幕
  19.     SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
  20.     SDL_RenderClear(renderer);
  21.    
  22.     // 绘制地面
  23.     SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
  24.     SDL_RenderDrawLine(renderer, 0, WINDOW_HEIGHT - 50, WINDOW_WIDTH, WINDOW_HEIGHT - 50);
  25.    
  26.     // 计算物体在屏幕上的位置
  27.     int screen_x = WINDOW_WIDTH / 2;
  28.     int screen_y = WINDOW_HEIGHT - 50 - (int)(position * SCALE);
  29.    
  30.     // 绘制物体
  31.     SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
  32.     SDL_Rect ball = {screen_x - 10, screen_y - 10, 20, 20};
  33.     SDL_RenderFillRect(renderer, &ball);
  34.    
  35.     // 更新屏幕
  36.     SDL_RenderPresent(renderer);
  37. }
  38. void close_graphics() {
  39.     SDL_DestroyRenderer(renderer);
  40.     SDL_DestroyWindow(window);
  41.     SDL_Quit();
  42. }
复制代码

然后,我们需要修改主循环以包含图形更新:
  1. void run_simulation() {
  2.     // 初始化模拟
  3.     initialize_simulation();
  4.    
  5.     // 初始化图形
  6.     init_graphics();
  7.    
  8.     // 模拟循环
  9.     SDL_Event e;
  10.     int quit = 0;
  11.    
  12.     while (!quit && time < 10.0) {
  13.         // 处理事件
  14.         while (SDL_PollEvent(&e) != 0) {
  15.             if (e.type == SDL_QUIT) {
  16.                 quit = 1;
  17.             }
  18.         }
  19.         
  20.         // 更新物理状态
  21.         update_physics();
  22.         
  23.         // 检测碰撞
  24.         check_collision();
  25.         
  26.         // 显示当前状态
  27.         display_state();
  28.         
  29.         // 绘制场景
  30.         draw_scene();
  31.         
  32.         // 检查是否停止弹跳
  33.         if (fabs(velocity) < 0.1 && position <= GROUND_LEVEL + 0.01) {
  34.             printf("物体在 %.2f 秒后停止弹跳,共反弹 %d 次\n",
  35.                    time, bounce_count);
  36.             break;
  37.         }
  38.         
  39.         // 控制帧率
  40.         SDL_Delay(10);
  41.     }
  42.    
  43.     // 关闭图形
  44.     close_graphics();
  45.    
  46.     printf("模拟结束\n");
  47. }
复制代码

当前的模拟使用简单的欧拉方法进行数值积分,这可能会导致一些误差。我们可以使用更精确的数值积分方法,如Verlet积分或Runge-Kutta方法。

例如,使用Verlet积分:
  1. void update_physics_verlet() {
  2.     double acceleration = -GRAVITY;
  3.     double new_position = 2 * position - prev_position + acceleration * TIME_STEP * TIME_STEP;
  4.    
  5.     prev_position = position;
  6.     position = new_position;
  7.    
  8.     // 速度可以通过位置差计算
  9.     velocity = (position - prev_position) / TIME_STEP;
  10.    
  11.     // 更新时间
  12.     time += TIME_STEP;
  13. }
复制代码

使用这种方法需要添加一个prev_position变量来存储上一时刻的位置。

现实世界中,物体下落时还会受到空气阻力的影响。我们可以添加一个简单的空气阻力模型:
  1. #define AIR_RESISTANCE 0.1  // 空气阻力系数
  2. void update_physics_with_air_resistance() {
  3.     // 计算空气阻力 (F = -b*v)
  4.     double air_resistance_force = -AIR_RESISTANCE * velocity;
  5.    
  6.     // 计算总加速度 (a = g + F/m)
  7.     double acceleration = -GRAVITY + air_resistance_force;
  8.    
  9.     // 更新速度 (v = v0 + a*t)
  10.     velocity += acceleration * TIME_STEP;
  11.    
  12.     // 更新位置 (y = y0 + v*t)
  13.     position += velocity * TIME_STEP;
  14.    
  15.     // 更新时间
  16.     time += TIME_STEP;
  17. }
复制代码

我们可以扩展程序以模拟多个物体的落体反弹:
  1. #define MAX_OBJECTS 10
  2. typedef struct {
  3.     double position;
  4.     double velocity;
  5.     double mass;
  6.     double radius;
  7.     int bounce_count;
  8. } PhysicsObject;
  9. PhysicsObject objects[MAX_OBJECTS];
  10. int object_count = 0;
  11. void add_object(double initial_height, double initial_velocity, double mass, double radius) {
  12.     if (object_count < MAX_OBJECTS) {
  13.         objects[object_count].position = initial_height;
  14.         objects[object_count].velocity = initial_velocity;
  15.         objects[object_count].mass = mass;
  16.         objects[object_count].radius = radius;
  17.         objects[object_count].bounce_count = 0;
  18.         object_count++;
  19.     }
  20. }
  21. void update_physics_multiple() {
  22.     for (int i = 0; i < object_count; i++) {
  23.         // 更新速度
  24.         objects[i].velocity -= GRAVITY * TIME_STEP;
  25.         
  26.         // 更新位置
  27.         objects[i].position += objects[i].velocity * TIME_STEP;
  28.         
  29.         // 检测碰撞
  30.         if (objects[i].position <= GROUND_LEVEL && objects[i].velocity < 0) {
  31.             objects[i].position = GROUND_LEVEL;
  32.             objects[i].velocity = -objects[i].velocity * COEFF_RESTITUTION;
  33.             objects[i].bounce_count++;
  34.         }
  35.     }
  36.    
  37.     // 更新时间
  38.     time += TIME_STEP;
  39. }
复制代码

如果我们模拟多个物体,还需要考虑它们之间的碰撞:
  1. void check_object_collisions() {
  2.     for (int i = 0; i < object_count; i++) {
  3.         for (int j = i + 1; j < object_count; j++) {
  4.             // 计算物体间的距离
  5.             double distance = fabs(objects[i].position - objects[j].position);
  6.             
  7.             // 检测是否碰撞
  8.             if (distance < (objects[i].radius + objects[j].radius)) {
  9.                 // 简单的弹性碰撞处理
  10.                 double temp_velocity = objects[i].velocity;
  11.                 objects[i].velocity = objects[j].velocity * COEFF_RESTITUTION;
  12.                 objects[j].velocity = temp_velocity * COEFF_RESTITUTION;
  13.                
  14.                 // 分离物体,避免重叠
  15.                 double overlap = (objects[i].radius + objects[j].radius) - distance;
  16.                 if (objects[i].position < objects[j].position) {
  17.                     objects[i].position -= overlap / 2;
  18.                     objects[j].position += overlap / 2;
  19.                 } else {
  20.                     objects[i].position += overlap / 2;
  21.                     objects[j].position -= overlap / 2;
  22.                 }
  23.             }
  24.         }
  25.     }
  26. }
复制代码

6. 完整的改进版代码示例

下面是一个包含图形显示和空气阻力的完整改进版代码示例:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <math.h>
  4. #include <time.h>
  5. #include <SDL2/SDL.h>
  6. // 物理常量
  7. #define GRAVITY 9.8           // 重力加速度 (m/s²)
  8. #define TIME_STEP 0.01        // 时间步长 (s)
  9. #define COEFF_RESTITUTION 0.8 // 恢复系数
  10. #define AIR_RESISTANCE 0.05   // 空气阻力系数
  11. // 模拟参数
  12. #define GROUND_LEVEL 0.0      // 地面位置 (m)
  13. #define INITIAL_HEIGHT 10.0   // 初始高度 (m)
  14. #define INITIAL_VELOCITY 0.0  // 初始速度 (m/s)
  15. // 图形参数
  16. #define WINDOW_WIDTH 800
  17. #define WINDOW_HEIGHT 600
  18. #define SCALE 30  // 像素/米的比例
  19. // 全局变量
  20. double position = INITIAL_HEIGHT;  // 当前位置
  21. double velocity = INITIAL_VELOCITY; // 当前速度
  22. double time = 0.0;                 // 模拟时间
  23. int bounce_count = 0;              // 反弹次数
  24. // SDL变量
  25. SDL_Window* window = NULL;
  26. SDL_Renderer* renderer = NULL;
  27. // 函数声明
  28. void initialize_simulation();
  29. void update_physics();
  30. void check_collision();
  31. void display_state();
  32. void init_graphics();
  33. void draw_scene();
  34. void close_graphics();
  35. void run_simulation();
  36. int main() {
  37.     run_simulation();
  38.     return 0;
  39. }
  40. void initialize_simulation() {
  41.     position = INITIAL_HEIGHT;
  42.     velocity = INITIAL_VELOCITY;
  43.     time = 0.0;
  44.     bounce_count = 0;
  45.    
  46.     printf("落体反弹物理模拟程序\n");
  47.     printf("初始高度: %.2f m\n", INITIAL_HEIGHT);
  48.     printf("初始速度: %.2f m/s\n", INITIAL_VELOCITY);
  49.     printf("重力加速度: %.2f m/s²\n", GRAVITY);
  50.     printf("恢复系数: %.2f\n", COEFF_RESTITUTION);
  51.     printf("空气阻力系数: %.2f\n", AIR_RESISTANCE);
  52.     printf("--------------------------------\n");
  53. }
  54. void update_physics() {
  55.     // 计算空气阻力 (F = -b*v)
  56.     double air_resistance_force = -AIR_RESISTANCE * velocity;
  57.    
  58.     // 计算总加速度 (a = g + F/m)
  59.     double acceleration = -GRAVITY + air_resistance_force;
  60.    
  61.     // 更新速度 (v = v0 + a*t)
  62.     velocity += acceleration * TIME_STEP;
  63.    
  64.     // 更新位置 (y = y0 + v*t)
  65.     position += velocity * TIME_STEP;
  66.    
  67.     // 更新时间
  68.     time += TIME_STEP;
  69. }
  70. void check_collision() {
  71.     // 检测是否与地面碰撞
  72.     if (position <= GROUND_LEVEL && velocity < 0) {
  73.         // 确保物体不会穿透地面
  74.         position = GROUND_LEVEL;
  75.         
  76.         // 计算反弹速度 (v' = -e*v)
  77.         velocity = -velocity * COEFF_RESTITUTION;
  78.         
  79.         // 增加反弹计数
  80.         bounce_count++;
  81.         
  82.         printf("时间: %.2f s, 第 %d 次反弹, 反弹速度: %.2f m/s\n",
  83.                time, bounce_count, fabs(velocity));
  84.     }
  85. }
  86. void display_state() {
  87.     // 每隔一定时间输出一次状态
  88.     static int counter = 0;
  89.     counter++;
  90.    
  91.     if (counter >= 50) {  // 每50个时间步长输出一次
  92.         printf("时间: %.2f s, 位置: %.2f m, 速度: %.2f m/s\n",
  93.                time, position, velocity);
  94.         counter = 0;
  95.     }
  96. }
  97. void init_graphics() {
  98.     // 初始化SDL
  99.     if (SDL_Init(SDL_INIT_VIDEO) < 0) {
  100.         printf("SDL初始化失败: %s\n", SDL_GetError());
  101.         exit(1);
  102.     }
  103.    
  104.     // 创建窗口
  105.     window = SDL_CreateWindow("落体反弹模拟",
  106.                              SDL_WINDOWPOS_UNDEFINED,
  107.                              SDL_WINDOWPOS_UNDEFINED,
  108.                              WINDOW_WIDTH, WINDOW_HEIGHT,
  109.                              SDL_WINDOW_SHOWN);
  110.     if (window == NULL) {
  111.         printf("窗口创建失败: %s\n", SDL_GetError());
  112.         exit(1);
  113.     }
  114.    
  115.     // 创建渲染器
  116.     renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
  117.     if (renderer == NULL) {
  118.         printf("渲染器创建失败: %s\n", SDL_GetError());
  119.         exit(1);
  120.     }
  121. }
  122. void draw_scene() {
  123.     // 清除屏幕
  124.     SDL_SetRenderDrawColor(renderer, 240, 240, 240, 255);
  125.     SDL_RenderClear(renderer);
  126.    
  127.     // 绘制地面
  128.     SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
  129.     SDL_RenderDrawLine(renderer, 0, WINDOW_HEIGHT - 50, WINDOW_WIDTH, WINDOW_HEIGHT - 50);
  130.    
  131.     // 计算物体在屏幕上的位置
  132.     int screen_x = WINDOW_WIDTH / 2;
  133.     int screen_y = WINDOW_HEIGHT - 50 - (int)(position * SCALE);
  134.    
  135.     // 绘制物体
  136.     SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
  137.     SDL_Rect ball = {screen_x - 15, screen_y - 15, 30, 30};
  138.     SDL_RenderFillRect(renderer, &ball);
  139.    
  140.     // 绘制高度标尺
  141.     SDL_SetRenderDrawColor(renderer, 100, 100, 100, 255);
  142.     for (int i = 0; i <= INITIAL_HEIGHT; i++) {
  143.         int y = WINDOW_HEIGHT - 50 - (int)(i * SCALE);
  144.         SDL_RenderDrawLine(renderer, 50, y, 60, y);
  145.         
  146.         // 绘制刻度值
  147.         char text[10];
  148.         sprintf(text, "%dm", i);
  149.         // 这里简化处理,实际应用中应使用SDL_ttf来渲染文本
  150.     }
  151.    
  152.     // 更新屏幕
  153.     SDL_RenderPresent(renderer);
  154. }
  155. void close_graphics() {
  156.     SDL_DestroyRenderer(renderer);
  157.     SDL_DestroyWindow(window);
  158.     SDL_Quit();
  159. }
  160. void run_simulation() {
  161.     // 初始化模拟
  162.     initialize_simulation();
  163.    
  164.     // 初始化图形
  165.     init_graphics();
  166.    
  167.     // 模拟循环
  168.     SDL_Event e;
  169.     int quit = 0;
  170.    
  171.     while (!quit && time < 20.0) {
  172.         // 处理事件
  173.         while (SDL_PollEvent(&e) != 0) {
  174.             if (e.type == SDL_QUIT) {
  175.                 quit = 1;
  176.             }
  177.         }
  178.         
  179.         // 更新物理状态
  180.         update_physics();
  181.         
  182.         // 检测碰撞
  183.         check_collision();
  184.         
  185.         // 显示当前状态
  186.         display_state();
  187.         
  188.         // 绘制场景
  189.         draw_scene();
  190.         
  191.         // 检查是否停止弹跳
  192.         if (fabs(velocity) < 0.05 && position <= GROUND_LEVEL + 0.01) {
  193.             printf("物体在 %.2f 秒后停止弹跳,共反弹 %d 次\n",
  194.                    time, bounce_count);
  195.             
  196.             // 等待几秒后退出
  197.             SDL_Delay(3000);
  198.             break;
  199.         }
  200.         
  201.         // 控制帧率
  202.         SDL_Delay((int)(TIME_STEP * 1000));
  203.     }
  204.    
  205.     // 关闭图形
  206.     close_graphics();
  207.    
  208.     printf("模拟结束\n");
  209. }
复制代码

7. 总结

本教程详细介绍了如何使用C语言编写一个落体反弹物理模拟程序。我们从基本的物理原理开始,逐步构建了一个完整的模拟程序,并讨论了多种可能的改进方向。

通过这个项目,我们学习了:

1. 如何将物理原理转化为计算机程序
2. 如何使用数值积分方法模拟连续的物理过程
3. 如何检测和处理碰撞事件
4. 如何使用SDL库创建简单的图形显示
5. 如何通过添加空气阻力等效果使模拟更加真实

这个基础程序可以作为更复杂物理模拟的起点,例如扩展到二维或三维空间、添加更多物体、模拟更复杂的物理现象等。物理模拟是计算机图形学、游戏开发和科学计算中的重要组成部分,掌握这些基本技术将为您的编程之路打开新的可能性。

希望这个教程对您有所帮助,祝您编程愉快!
「七転び八起き(ななころびやおき)」
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则