097. 编写代码实现简单的物理引擎

在C语言中实现一个简单的物理引擎可以是一个很好的学习项目,帮助你理解物理模拟的基本概念。这里我将展示一个简单的2D物理引擎,用于模拟物体的运动和碰撞。这个引擎将包括以下功能:

  1. 物体的位置、速度和加速度更新。
  2. 碰撞检测和简单的碰撞响应。
  3. 重力和简单的力的应用。

简单的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)

扩展功能

  1. 更复杂的碰撞检测: 实现多边形碰撞检测和响应。
  2. 摩擦力和阻尼:在物理更新中加入摩擦力和空气阻力。
  3. 关节和约束:实现关节和约束,例如弹簧、绳索等。
  4. 图形化显示:使用图形库(如SDL或OpenGL)显示物理模拟的结果。

视频讲解

BiliBili: 视睿网络-哔哩哔哩视频 (bilibili.com)