097. 编写代码实现简单的物理引擎
在C语言中实现一个简单的物理引擎可以是一个很好的学习项目,帮助你理解物理模拟的基本概念。这里我将展示一个简单的2D物理引擎,用于模拟物体的运动和碰撞。这个引擎将包括以下功能:
- 物体的位置、速度和加速度更新。
- 碰撞检测和简单的碰撞响应。
- 重力和简单的力的应用。
简单的2D物理引擎
数据结构
首先,需要定义一些基本的数据结构来表示物体和物理状态。
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define PI 3.14159265358979323846
// 2D向量结构
typedef struct {
double x;
double y;
} Vector2;
// 物体结构
typedef struct {
Vector2 position;
Vector2 velocity;
Vector2 acceleration;
double mass;
} Rigidbody;
// 初始化向量
Vector2 makeVector2(double x, double y) {
Vector2 v = {x, y};
return v;
}
// 向量加法
Vector2 addVector2(Vector2 a, Vector2 b) {
return makeVector2(a.x + b.x, a.y + b.y);
}
// 向量减法
Vector2 subtractVector2(Vector2 a, Vector2 b) {
return makeVector2(a.x - b.x, a.y - b.y);
}
// 向量乘法(标量)
Vector2 multiplyVector2(Vector2 v, double scalar) {
return makeVector2(v.x * scalar, v.y * scalar);
}
// 向量除法(标量)
Vector2 divideVector2(Vector2 v, double scalar) {
return makeVector2(v.x / scalar, v.y / scalar);
}
// 计算向量长度
double magnitudeVector2(Vector2 v) {
return sqrt(v.x * v.x + v.y * v.y);
}
// 标准化向量
Vector2 normalizeVector2(Vector2 v) {
double mag = magnitudeVector2(v);
return makeVector2(v.x / mag, v.y / mag);
}
物理更新
接下来,需要实现物理更新逻辑,包括位置、速度和加速度的更新。
// 更新物体的物理状态
void updateRigidbody(Rigidbody* rb, double deltaTime) {
// 更新速度
rb->velocity = addVector2(rb->velocity, multiplyVector2(rb->acceleration, deltaTime));
// 更新位置
rb->position = addVector2(rb->position, multiplyVector2(rb->velocity, deltaTime));
}
// 应用力到物体上
void applyForce(Rigidbody* rb, Vector2 force) {
Vector2 acceleration = divideVector2(force, rb->mass);
rb->acceleration = addVector2(rb->acceleration, acceleration);
}
碰撞检测和响应
接下来,需要实现简单的碰撞检测和响应逻辑。
// 检测两个物体是否碰撞(假设为圆形)
int detectCollision(Rigidbody a, Rigidbody b, double radiusA, double radiusB) {
Vector2 delta = subtractVector2(a.position, b.position);
double distance = magnitudeVector2(delta);
return distance < (radiusA + radiusB);
}
// 简单的碰撞响应(假设为弹性碰撞)
void resolveCollision(Rigidbody* a, Rigidbody* b, double radiusA, double radiusB) {
if (detectCollision(*a, *b, radiusA, radiusB)) {
Vector2 delta = subtractVector2(a->position, b->position);
Vector2 normal = normalizeVector2(delta);
double distance = magnitudeVector2(delta);
double overlap = radiusA + radiusB - distance;
// 分离物体
a->position = subtractVector2(a->position, multiplyVector2(normal, overlap / 2.0));
b->position = addVector2(b->position, multiplyVector2(normal, overlap / 2.0));
// 计算碰撞后的速度
Vector2 relativeVelocity = subtractVector2(a->velocity, b->velocity);
double velocityAlongNormal = dotProductVector2(relativeVelocity, normal);
Vector2 impulse = multiplyVector2(normal, velocityAlongNormal);
a->velocity = subtractVector2(a->velocity, multiplyVector2(impulse, b->mass / (a->mass + b->mass)));
b->velocity = addVector2(b->velocity, multiplyVector2(impulse, a->mass / (a->mass + b->mass)));
}
}
// 向量点积
double dotProductVector2(Vector2 a, Vector2 b) {
return a.x * b.x + a.y * b.y;
}
主函数
最后,需要在主函数中初始化物体,并在每一帧更新物理状态。
int main() {
// 初始化两个物体
Rigidbody rb1 = {{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, 1.0};
Rigidbody rb2 = {{5.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, 1.0};
// 应用重力
applyForce(&rb1, makeVector2(0.0, -9.8));
applyForce(&rb2, makeVector2(0.0, -9.8));
double deltaTime = 0.01; // 模拟的时间步长
double radius = 1.0; // 物体半径
for (int i = 0; i < 1000; i++) {
// 更新物理状态
updateRigidbody(&rb1, deltaTime);
updateRigidbody(&rb2, deltaTime);
// 检测并解决碰撞
resolveCollision(&rb1, &rb2, radius, radius);
// 打印物体位置
printf("Frame %d: rb1 position (%.2f, %.2f), rb2 position (%.2f, %.2f)\n",
i, rb1.position.x, rb1.position.y, rb2.position.x, rb2.position.y);
}
return 0;
}
示例运行
Frame 0: rb1 position (0.00, 0.00), rb2 position (5.00, 0.00)
Frame 1: rb1 position (0.00, -0.01), rb2 position (5.00, -0.01)
Frame 2: rb1 position (0.00, -0.02), rb2 position (5.00, -0.02)
...
Frame 999: rb1 position (0.00, -49.00), rb2 position (5.00, -49.00)
扩展功能
- 更复杂的碰撞检测: 实现多边形碰撞检测和响应。
- 摩擦力和阻尼:在物理更新中加入摩擦力和空气阻力。
- 关节和约束:实现关节和约束,例如弹簧、绳索等。
- 图形化显示:使用图形库(如SDL或OpenGL)显示物理模拟的结果。
视频讲解
BiliBili: 视睿网络-哔哩哔哩视频 (bilibili.com)