目的
自由落下の公式を理解した上で、C言語にて実装します。
自由落下の運動方程式と数値解法
基本パラメータ
- $v$: 速度
- $y$: 位置
- $g$: 重力加速度
自由落下の運動方程式
$$
\frac{dv}{dt} = g, \quad \frac{dy}{dt} = v
$$
微分の定義から数値解法への導出
速度の更新式
微分の定義:
$$
\frac{dv}{dt} = \lim_{\Delta t \to 0} \frac{v(t+\Delta t) - v(t)}{\Delta t}
$$
近似式:
$$
g \approx \frac{v(t+\Delta t) - v(t)}{\Delta t}
$$
$$
g \cdot \Delta t \approx v(t+\Delta t) - v(t)
$$
よって、
$$
v(t+\Delta t) \approx v(t) + g \cdot \Delta t
$$
即ち、次の時刻の速度は、今の速度に重力加速度×△tを足したものとなります。
位置の更新式
微分の定義:
$$
\frac{dy}{dt} = \lim_{\Delta t \to 0} \frac{y(t+\Delta t) - y(t)}{\Delta t}
$$
近似式:
$$
v(t) \approx \frac{y(t+\Delta t) - y(t)}{\Delta t}
$$
$$
v(t) \cdot \Delta t \approx y(t+\Delta t) - y(t)
$$
よって、
$$
y(t+\Delta t) \approx y(t) + v(t) \cdot \Delta t
$$
即ち、次の時刻の位置は、今の位置に速度×△tを足したものなります。
これらの式をはC言語で以下の様に表せます。
v += g * dt;
y += v * dt;
C言語による実装
test@test-fujitsu:~/kaihatsu/butsuri$ gcc bouncing_ball.c -o bouncing_ball `sdl2-config --cflags --libs`
test@test-fujitsu:~/kaihatsu/butsuri$ ./bouncing_ball
#include <SDL2/SDL.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
SDL_Init(SDL_INIT_VIDEO);
//400×400ピクセルのウィンドウを画面中央へ配置
SDL_Window* window = SDL_CreateWindow("Bouncing Ball",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
400, 400, 0);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
double y = 0.0; // 球の 縦方向の位置(高さ)0 はウィンドウの上端、400 が下端(ウィンドウの高さ)
double v = 0.0; // 球の 縦方向の速度(ピクセル/秒)
double dt = 0.02; // 0.02秒 = 20ミリ秒ごとに物理計算する
double g = 200.0; // ピクセル/s^2
double e = 0.8; // 反発係数 1.0 = 反発完全、0 = 跳ねない
int ball_radius = 10; // 球の 半径(ピクセル) 描画や衝突判定に使う
int height = 400; // ウィンドウの 高さ(ピクセル)
int running = 1;
SDL_Event event;
while (running) {
while (SDL_PollEvent(&event)) { // 「×」押下時終了の処理
if (event.type == SDL_QUIT) running = 0;
}
// 更新
v += g * dt;
y += v * dt;
// 跳ね返り
// 「球の下端が地面(ウィンドウの底)より下に行ったら」という条件
// 球の半径分を引くことで、球の中心ではなく球の下端で跳ね返るように調整
if (y > height - ball_radius) {
y = height - ball_radius;
// -v … 速度の向きを逆にする
// *e …衝突で失われる運動エネルギーを表す
v = -v * e;
}
// 描画
// 1コマ前で描画した正方形を塗り潰す
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); // 描画色を設定 R=0 G=0 B=0 透明度=255(不透明)
SDL_RenderClear(renderer); // 塗り潰す
// 正方形を描画する
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); // 赤 = 255
// SDL_Rectは矩形用の構造体
// x = 200 - ball_radius → ウィンドウ横中央に球の中心を合わせる
// y = (int)y - ball_radius → 球の縦位置を描画用に調整
// w = ball_radius*2 → 幅 = 直径
// h = ball_radius*2 → 高さ = 直径
SDL_Rect ball = {200 - ball_radius, (int)y - ball_radius, ball_radius*2, ball_radius*2};
SDL_RenderFillRect(renderer, &ball);
SDL_RenderPresent(renderer); // 描画した内容を画面に表示する
SDL_Delay((int)(dt*1000));
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
筆者の環境
-
OS: Ubuntu 22.04.5 LTS (Jammy Jellyfish)
Linux test-fujitsu 6.8.0-87-generic x86_64 GNU/Linux -
Cコンパイラ: GCC 11.4.0
gcc version 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04.2) -
SDL2ライブラリ: 2.0.20
pkg-config --modversion sdl2 → 2.0.20

